prop/lib/microwaveprop_web/components/layouts.ex
Graham McIntire ad041ccf4a
Nav reorder + path calc link from beacon detail
Move Scoring Algorithm directly above About in map and weather-map
sidebars; reorder the horizontal nav so Submit sits before Contacts
to match.

Add a "Plot path to beacon" button on the beacon detail page that
navigates to /path with destination, band (snapped via BandResolver),
and dst_height_ft prefilled from the beacon.
2026-04-12 16:33:09 -05:00

202 lines
6.4 KiB
Elixir

defmodule MicrowavepropWeb.Layouts do
@moduledoc """
This module holds layouts and related functionality
used by your application.
"""
use MicrowavepropWeb, :html
# Embed all files in layouts/* within this module.
# The default root.html.heex file contains the HTML
# skeleton of your application, namely HTML headers
# and other static content.
embed_templates "layouts/*"
@doc """
Renders your app layout.
This function is typically invoked from every template,
and it often contains your application menu, sidebar,
or similar.
## Examples
<Layouts.app flash={@flash}>
<h1>Content</h1>
</Layouts.app>
"""
attr :flash, :map, required: true, doc: "the map of flash messages"
attr :current_scope, :map,
default: nil,
doc: "the current [scope](https://hexdocs.pm/phoenix/scopes.html)"
attr :max_width, :string, default: "max-w-2xl", doc: "max width class for the content container"
slot :inner_block, required: true
def app(assigns) do
~H"""
<header class="navbar px-4 sm:px-6 lg:px-8">
<div class="flex-1 gap-4">
<a href="/" class="font-semibold">NTMS Propagation Prediction</a>
<nav class="flex gap-2">
<.link navigate="/map" class="btn btn-ghost btn-sm">Map</.link>
<.link navigate="/path" class="btn btn-ghost btn-sm">Path</.link>
<.link navigate="/beacons" class="btn btn-ghost btn-sm">Beacons</.link>
<.link navigate="/submit" class="btn btn-ghost btn-sm">Submit</.link>
<.link navigate="/contacts" class="btn btn-ghost btn-sm">Contacts</.link>
<.link navigate="/contacts/map" class="btn btn-ghost btn-sm">Contact Map</.link>
<.link navigate="/about" class="btn btn-ghost btn-sm">About</.link>
<.link
:if={@current_scope && @current_scope.user && @current_scope.user.is_admin}
navigate="/users"
class="btn btn-ghost btn-sm"
>
Users
</.link>
<.link
:if={@current_scope && @current_scope.user && @current_scope.user.is_admin}
navigate="/admin/contact-edits"
class="btn btn-ghost btn-sm"
>
Edits
</.link>
<.link
:if={@current_scope && @current_scope.user && @current_scope.user.is_admin}
href="/admin/oban"
class="btn btn-ghost btn-sm"
>
Oban
</.link>
<%= if @current_scope && @current_scope.user do %>
<.link
navigate={~p"/u/#{@current_scope.user.callsign}"}
class="btn btn-ghost btn-sm"
>
{@current_scope.user.callsign}
</.link>
<.link href={~p"/users/settings"} class="btn btn-ghost btn-sm">Settings</.link>
<.link href={~p"/users/log-out"} method="delete" class="btn btn-ghost btn-sm">
Log out
</.link>
<% else %>
<.link href={~p"/users/register"} class="btn btn-ghost btn-sm">Register</.link>
<.link href={~p"/users/log-in"} class="btn btn-ghost btn-sm">Log in</.link>
<% end %>
</nav>
</div>
<div class="flex-none">
<.theme_toggle />
</div>
</header>
<main class="px-4 py-20 sm:px-6 lg:px-8">
<div class={["mx-auto space-y-4", @max_width]}>
{render_slot(@inner_block)}
</div>
</main>
<footer class="px-4 sm:px-6 lg:px-8 pb-6">
<div class={["mx-auto text-center text-xs opacity-60 space-y-1", @max_width]}>
<div>
Built by and for the
<a href="https://ntms.org" target="_blank" class="link link-hover">
North Texas Microwave Society
</a>
</div>
<div>
Found this useful?
<a
href="https://www.paypal.com/ncp/payment/53VLBD2E67JAE"
target="_blank"
class="link link-hover"
>
Donate to NTMS
</a>
</div>
<div>
<.link navigate="/privacy" class="link link-hover">Privacy</.link>
</div>
</div>
</footer>
<.flash_group flash={@flash} />
"""
end
@doc """
Shows the flash group with standard titles and content.
## Examples
<.flash_group flash={@flash} />
"""
attr :flash, :map, required: true, doc: "the map of flash messages"
attr :id, :string, default: "flash-group", doc: "the optional id of flash container"
def flash_group(assigns) do
~H"""
<div id={@id} aria-live="polite">
<.flash kind={:info} flash={@flash} />
<.flash kind={:error} flash={@flash} />
<.flash
id="client-error"
kind={:error}
title={gettext("We can't find the internet")}
hidden
>
{gettext("Attempting to reconnect")}
<.icon name="hero-arrow-path" class="ml-1 size-3 motion-safe:animate-spin" />
</.flash>
<.flash
id="server-error"
kind={:error}
title={gettext("Something went wrong!")}
hidden
>
{gettext("Attempting to reconnect")}
<.icon name="hero-arrow-path" class="ml-1 size-3 motion-safe:animate-spin" />
</.flash>
</div>
"""
end
@doc """
Provides dark vs light theme toggle based on themes defined in app.css.
See <head> in root.html.heex which applies the theme before page load.
"""
def theme_toggle(assigns) do
~H"""
<div class="card relative flex flex-row items-center border-2 border-base-300 bg-base-300 rounded-full">
<div class="absolute w-1/3 h-full rounded-full border-1 border-base-200 bg-base-100 brightness-200 left-0 [[data-theme=light]_&]:left-1/3 [[data-theme=dark]_&]:left-2/3 transition-[left]" />
<button
class="flex p-2 cursor-pointer w-1/3"
phx-click={JS.dispatch("phx:set-theme")}
data-phx-theme="system"
>
<.icon name="hero-computer-desktop-micro" class="size-4 opacity-75 hover:opacity-100" />
</button>
<button
class="flex p-2 cursor-pointer w-1/3"
phx-click={JS.dispatch("phx:set-theme")}
data-phx-theme="light"
>
<.icon name="hero-sun-micro" class="size-4 opacity-75 hover:opacity-100" />
</button>
<button
class="flex p-2 cursor-pointer w-1/3"
phx-click={JS.dispatch("phx:set-theme")}
data-phx-theme="dark"
>
<.icon name="hero-moon-micro" class="size-4 opacity-75 hover:opacity-100" />
</button>
</div>
"""
end
end