defmodule MicrowavepropWeb.BeaconLive.Show do @moduledoc false use MicrowavepropWeb, :live_view alias Microwaveprop.Beacons alias Microwaveprop.Beacons.Beacon alias Microwaveprop.Beacons.RangeEstimate @impl true def render(assigns) do ~H""" <.header> {@beacon.callsign} <:subtitle> {@beacon.frequency_mhz} MHz · {@beacon.grid} Pending approval <:actions> <.button :if={admin?(@current_scope) and not @beacon.approved} variant="primary" phx-click="approve" > <.icon name="hero-check" /> Approve <.button navigate={~p"/beacons"}> <.icon name="hero-arrow-left" /> <.button :if={admin?(@current_scope)} variant="primary" navigate={~p"/beacons/#{@beacon}/edit?return_to=show"} > <.icon name="hero-pencil-square" /> Edit
@beacon.id} phx-hook="BeaconMap" phx-update="ignore" class="h-96 w-full rounded-lg border border-base-300 mb-2" data-lat={@beacon.lat} data-lon={@beacon.lon} data-label={"#{@beacon.callsign} · #{@beacon.frequency_mhz} MHz"} data-on-the-air={to_string(@beacon.on_the_air)} data-grid-step={@estimate.grid_step} data-cells={Jason.encode!(@estimate.cells)} data-show-coverage={to_string(@show_coverage)} >
Band {@estimate.band_label} · EIRP {@estimate.eirp_dbm} dBm · Atm loss {@estimate.atm_per_km} dB/km Beacon grid score {@estimate.center_score} <%= if @estimate.valid_time do %> at {Calendar.strftime(@estimate.valid_time, "%Y-%m-%d %H:%M UTC")} <% else %> (no HRRR data — defaulting cells to 50) <% end %> · {length(@estimate.cells)} cells rendered
<%= for tier <- @estimate.tiers do %>
{tier.label} (≥ {tier.min_dbm} dBm)
<% end %>
<.stat_field label="Frequency">{@beacon.frequency_mhz} MHz <.stat_field label="Callsign">{@beacon.callsign} <.stat_field label="Keying">{Beacon.keying_label(@beacon.keying)} <.stat_field label="On the air"> {if @beacon.on_the_air, do: "Yes", else: "No"} <.stat_field label="Grid">{@beacon.grid} <.stat_field label="Latitude">{format_coord(@beacon.lat)} <.stat_field label="Longitude">{format_coord(@beacon.lon)} <.stat_field label="EIRP">{Beacon.format_mw(@beacon.power_mw)} mW <.stat_field label="Height AGL">{Beacon.format_mw(@beacon.height_ft)} ft <.stat_field label="Bearing">{bearing_label(@beacon.bearing)} <.stat_field :if={@beacon.beamwidth_deg} label="Beamwidth"> {Beacon.format_mw(@beacon.beamwidth_deg)}°
Notes
{@beacon.notes}
""" end @impl true def mount(%{"id" => id}, _session, socket) do if connected?(socket), do: Beacons.subscribe_beacons() beacon = Beacons.get_beacon!(id) {:ok, socket |> assign(:page_title, "Beacon") |> assign(:beacon, beacon) |> assign(:show_coverage, false) |> assign(:estimate, RangeEstimate.estimate(beacon))} end @impl true def handle_event("toggle_coverage", _params, socket) do show = not socket.assigns.show_coverage {:noreply, socket |> assign(:show_coverage, show) |> push_event("toggle_coverage", %{show: show})} end def handle_event("approve", _params, socket) do if admin?(socket.assigns.current_scope) do {:ok, approved} = Beacons.approve_beacon(socket.assigns.beacon) {:noreply, socket |> assign(:beacon, approved) |> put_flash(:info, "Beacon approved.")} else {:noreply, put_flash(socket, :error, "Admins only.")} end end @impl true def handle_info({:updated, %Beacon{id: id} = beacon}, %{assigns: %{beacon: %{id: id}}} = socket) do {:noreply, socket |> assign(:beacon, beacon) |> assign(:estimate, RangeEstimate.estimate(beacon))} end def handle_info({:deleted, %Beacon{id: id}}, %{assigns: %{beacon: %{id: id}}} = socket) do {:noreply, socket |> put_flash(:error, "This beacon was deleted.") |> push_navigate(to: ~p"/beacons")} end def handle_info({type, %Beacon{}}, socket) when type in [:created, :updated, :deleted] do {:noreply, socket} end defp admin?(%{user: %{is_admin: true}}), do: true defp admin?(_), do: false defp bearing_label(nil), do: "Omni" defp bearing_label("omni"), do: "Omni" defp bearing_label(value), do: "#{value}°" defp format_coord(value) when is_float(value), do: :erlang.float_to_binary(value, decimals: 5) defp format_coord(value), do: to_string(value) attr :label, :string, required: true slot :inner_block, required: true defp stat_field(assigns) do ~H"""
{@label}
{render_slot(@inner_block)}
""" end end