defmodule MicrowavepropWeb.Admin.MonitorLive.Index do @moduledoc "Admin beacon-monitor list at `/admin/beacon-monitors`." use MicrowavepropWeb, :live_view import Ecto.Query alias Microwaveprop.Accounts.User alias Microwaveprop.BeaconMonitors alias Microwaveprop.BeaconMonitors.BeaconMonitor alias Microwaveprop.Repo @impl true def mount(_params, _session, socket) do users = Repo.all(from u in User, order_by: u.callsign) {:ok, socket |> assign(:page_title, "Beacon monitors") |> assign(:users, users) |> assign(:form, to_form(BeaconMonitors.change_monitor())) |> stream(:monitors, [])} end @impl true def handle_params(_params, _url, socket) do monitors = BeaconMonitors.list_all_monitors() {:noreply, stream(socket, :monitors, monitors, reset: true)} end @impl true def render(assigns) do ~H""" <.header> Beacon monitors <:subtitle>Provision and manage physical monitor hardware.
<.button phx-click={JS.push("show-create-form", target: @myself)} variant="primary"> <.icon name="hero-plus" class="size-4" /> New monitor

Register new hardware

<.form for={@form} id="monitor-form" phx-submit="create" phx-change="validate">
<.input field={@form[:name]} type="text" label="Name" required /> <.input field={@form[:user_id]} type="select" label="Assign to" options={user_options(@users)} /> <.input field={@form[:hardware_type]} type="text" label="Hardware type" placeholder="RTL-SDR" /> <.input field={@form[:hardware_id]} type="text" label="Hardware ID / Serial" placeholder="SN-001" /> <.input field={@form[:firmware_version]} type="text" label="Firmware version" /> <.input field={@form[:antenna_type]} type="text" label="Antenna type" placeholder="Dipole" /> <.input field={@form[:antenna_gain_dbi]} type="number" step="any" label="Antenna gain (dBi)" />
<.input field={@form[:lat]} type="number" step="any" label="Latitude" /> <.input field={@form[:lon]} type="number" step="any" label="Longitude" />
<.button variant="primary" phx-disable-with="Creating...">Create <.button phx-click={JS.push("hide-create-form", target: @myself)}>Cancel
Name Hardware Assigned to Monitoring Last seen
{monitor.name} <%= if monitor.hardware_type do %> {monitor.hardware_type} ({monitor.hardware_id}) <% else %> <% end %> <%= if monitor.user do %> <.link navigate={~p"/u/#{monitor.user.callsign}"} class="link link-hover font-mono"> {monitor.user.callsign} <% else %> Unassigned <% end %> <%= if monitor.beacon do %> <.link navigate={~p"/beacons/#{monitor.beacon.id}"} class="link link-hover font-mono" > {monitor.beacon.callsign} <% else %> <% end %> <%= if monitor.last_seen_at do %> {Calendar.strftime(monitor.last_seen_at, "%Y-%m-%d %H:%M UTC")} <% else %> never <% end %>
<.link navigate={~p"/admin/beacon-monitors/#{monitor.id}"} class="btn btn-xs btn-ghost" > Detail <.link phx-click={JS.push("delete", value: %{id: monitor.id}, target: @myself)} data-confirm={"Delete monitor '#{monitor.name}'? This cannot be undone."} class="btn btn-xs btn-ghost text-error" > Delete
""" end @impl true def handle_event("show-create-form", _params, socket) do form = to_form(BeaconMonitors.change_monitor(), as: :beacon_monitor) {:noreply, assign(socket, :form, form)} end def handle_event("hide-create-form", _params, socket) do {:noreply, assign(socket, :form, to_form(BeaconMonitors.change_monitor(), action: :ignore))} end def handle_event("validate", %{"beacon_monitor" => params}, socket) do changeset = %BeaconMonitor{} |> BeaconMonitor.provision_changeset(params) |> Map.put(:action, :validate) {:noreply, assign(socket, :form, to_form(changeset, as: :beacon_monitor))} end def handle_event("create", %{"beacon_monitor" => params}, socket) do admin = socket.assigns.current_scope.user case BeaconMonitors.create_hardware(admin, params) do {:ok, monitor} -> {:noreply, socket |> put_flash(:info, "Monitor '#{monitor.name}' created.") |> assign(:form, to_form(BeaconMonitors.change_monitor(), action: :ignore)) |> stream(:monitors, [monitor], at: 0)} {:error, changeset} -> {:noreply, assign(socket, :form, to_form(changeset, as: :beacon_monitor))} end end def handle_event("delete", %{"id" => id}, socket) do case BeaconMonitors.delete_monitor!(id) do {:ok, monitor} -> {:noreply, socket |> put_flash(:info, "Monitor '#{monitor.name}' deleted.") |> stream_delete(:monitors, monitor)} {:error, :not_found} -> {:noreply, put_flash(socket, :error, "Monitor not found.")} end end defp user_options(users) do Enum.map(users, &{&1.callsign, &1.id}) end end