Formats frequencies like 10368.1 as "10,368.1" on both the beacon list table and detail page (subtitle, map label, stat field).
165 lines
5.5 KiB
Elixir
165 lines
5.5 KiB
Elixir
defmodule MicrowavepropWeb.BeaconLive.Index do
|
|
@moduledoc false
|
|
use MicrowavepropWeb, :live_view
|
|
|
|
alias Microwaveprop.Beacons
|
|
alias Microwaveprop.Beacons.Beacon
|
|
|
|
@impl true
|
|
def render(assigns) do
|
|
~H"""
|
|
<Layouts.app flash={@flash} current_scope={@current_scope} max_width="max-w-6xl">
|
|
<.header>
|
|
Beacons
|
|
<:subtitle>Microwave beacons tracked by NTMS.</:subtitle>
|
|
<:actions>
|
|
<.button variant="primary" navigate={~p"/beacons/new"}>
|
|
<.icon name="hero-plus" /> Submit Beacon
|
|
</.button>
|
|
</:actions>
|
|
</.header>
|
|
|
|
<.table
|
|
id="beacons"
|
|
rows={@streams.beacons}
|
|
row_click={fn {_id, beacon} -> JS.navigate(~p"/beacons/#{beacon}") end}
|
|
>
|
|
<:col :let={{_id, beacon}} label="Frequency (MHz)">{Beacon.format_freq(beacon.frequency_mhz)}</:col>
|
|
<:col :let={{_id, beacon}} label="Call">{beacon.callsign}</:col>
|
|
<:col :let={{_id, beacon}} label="Grid">{beacon.grid}</:col>
|
|
<:col :let={{_id, beacon}} label="Lat">{beacon.lat}</:col>
|
|
<:col :let={{_id, beacon}} label="Lon">{beacon.lon}</:col>
|
|
<:col :let={{_id, beacon}} label="EIRP (mW)">{Beacon.format_mw(beacon.power_mw)}</:col>
|
|
<:col :let={{_id, beacon}} label="Height AGL (ft)">{beacon.height_ft}</:col>
|
|
<:col :let={{_id, beacon}} label="Keying">{Beacon.keying_label(beacon.keying)}</:col>
|
|
<:col :let={{_id, beacon}} label="On air">
|
|
<span class={["badge badge-sm", (beacon.on_the_air && "badge-success") || "badge-ghost"]}>
|
|
{if beacon.on_the_air, do: "Yes", else: "No"}
|
|
</span>
|
|
</:col>
|
|
<:action :let={{_id, beacon}}>
|
|
<div class="sr-only">
|
|
<.link navigate={~p"/beacons/#{beacon}"}>Show</.link>
|
|
</div>
|
|
<.link :if={admin?(@current_scope)} navigate={~p"/beacons/#{beacon}/edit"}>Edit</.link>
|
|
</:action>
|
|
<:action :let={{id, beacon}}>
|
|
<.link
|
|
:if={admin?(@current_scope)}
|
|
phx-click={JS.push("delete", value: %{id: beacon.id}) |> hide("##{id}")}
|
|
data-confirm="Delete this beacon?"
|
|
>
|
|
Delete
|
|
</.link>
|
|
</:action>
|
|
</.table>
|
|
|
|
<div :if={admin?(@current_scope) and @pending != []} class="mt-10">
|
|
<.header>
|
|
Pending approval
|
|
<:subtitle>Submitted beacons waiting for admin review.</:subtitle>
|
|
</.header>
|
|
|
|
<.table
|
|
id="pending-beacons"
|
|
rows={@streams.pending}
|
|
row_click={fn {_id, beacon} -> JS.navigate(~p"/beacons/#{beacon}") end}
|
|
>
|
|
<:col :let={{_id, beacon}} label="Frequency (MHz)">{Beacon.format_freq(beacon.frequency_mhz)}</:col>
|
|
<:col :let={{_id, beacon}} label="Call">{beacon.callsign}</:col>
|
|
<:col :let={{_id, beacon}} label="Grid">{beacon.grid}</:col>
|
|
<:col :let={{_id, beacon}} label="EIRP (mW)">{Beacon.format_mw(beacon.power_mw)}</:col>
|
|
<:col :let={{_id, beacon}} label="Height AGL (ft)">{beacon.height_ft}</:col>
|
|
<:col :let={{_id, beacon}} label="Submitted">
|
|
{Calendar.strftime(beacon.inserted_at, "%Y-%m-%d %H:%M UTC")}
|
|
</:col>
|
|
<:action :let={{_id, beacon}}>
|
|
<.link
|
|
phx-click={JS.push("approve", value: %{id: beacon.id})}
|
|
data-confirm="Approve this beacon?"
|
|
>
|
|
Approve
|
|
</.link>
|
|
</:action>
|
|
<:action :let={{id, beacon}}>
|
|
<.link
|
|
phx-click={JS.push("delete", value: %{id: beacon.id}) |> hide("##{id}")}
|
|
data-confirm="Delete this beacon?"
|
|
>
|
|
Delete
|
|
</.link>
|
|
</:action>
|
|
</.table>
|
|
</div>
|
|
</Layouts.app>
|
|
"""
|
|
end
|
|
|
|
@impl true
|
|
def mount(_params, _session, socket) do
|
|
if connected?(socket), do: Beacons.subscribe_beacons()
|
|
|
|
pending =
|
|
if admin?(socket.assigns.current_scope) do
|
|
Beacons.list_pending_beacons()
|
|
else
|
|
[]
|
|
end
|
|
|
|
{:ok,
|
|
socket
|
|
|> assign(:page_title, "Beacons")
|
|
|> assign(:pending, pending)
|
|
|> stream(:beacons, Beacons.list_beacons())
|
|
|> stream(:pending, pending)}
|
|
end
|
|
|
|
@impl true
|
|
def handle_event("delete", %{"id" => id}, socket) do
|
|
if admin?(socket.assigns.current_scope) do
|
|
beacon = Beacons.get_beacon!(id)
|
|
{:ok, _} = Beacons.delete_beacon(beacon)
|
|
|
|
{:noreply,
|
|
socket
|
|
|> stream_delete(:beacons, beacon)
|
|
|> stream_delete(:pending, beacon)}
|
|
else
|
|
{:noreply, put_flash(socket, :error, "Admins only.")}
|
|
end
|
|
end
|
|
|
|
def handle_event("approve", %{"id" => id}, socket) do
|
|
if admin?(socket.assigns.current_scope) do
|
|
beacon = Beacons.get_beacon!(id)
|
|
{:ok, approved} = Beacons.approve_beacon(beacon)
|
|
|
|
{:noreply,
|
|
socket
|
|
|> put_flash(:info, "Approved #{approved.callsign}.")
|
|
|> stream_delete(:pending, beacon)
|
|
|> stream_insert(:beacons, approved)}
|
|
else
|
|
{:noreply, put_flash(socket, :error, "Admins only.")}
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def handle_info({type, %Beacon{}}, socket) when type in [:created, :updated, :deleted] do
|
|
pending =
|
|
if admin?(socket.assigns.current_scope) do
|
|
Beacons.list_pending_beacons()
|
|
else
|
|
[]
|
|
end
|
|
|
|
{:noreply,
|
|
socket
|
|
|> assign(:pending, pending)
|
|
|> stream(:beacons, Beacons.list_beacons(), reset: true)
|
|
|> stream(:pending, pending, reset: true)}
|
|
end
|
|
|
|
defp admin?(%{user: %{is_admin: true}}), do: true
|
|
defp admin?(_), do: false
|
|
end
|