After picking a site in the coverage form, a new optional dropdown lets the user attach the coverage to one of the devices at that site. The dropdown reloads on every site change. If the site has no devices yet, a hint replaces it. Schema: nullable device_id FK on coverages (on_delete: nilify_all so deleting a device leaves the coverage standing). Filtered by both site_id and organization_id when loading the dropdown options. Show page lists the attached device by name (or IP) when set.
127 lines
3.8 KiB
Elixir
127 lines
3.8 KiB
Elixir
defmodule ToweropsWeb.CoverageLive.Show do
|
|
@moduledoc false
|
|
use ToweropsWeb, :live_view
|
|
|
|
alias Towerops.Coverages
|
|
alias Towerops.Coverages.Antenna
|
|
alias Towerops.Coverages.Coverage
|
|
|
|
@impl true
|
|
def mount(%{"id" => id}, _session, socket) do
|
|
organization = socket.assigns.current_scope.organization
|
|
coverage = organization.id |> Coverages.get_coverage!(id) |> Towerops.Repo.preload(:device)
|
|
antenna = Antenna.get(coverage.antenna_slug)
|
|
eirp = Coverage.eirp_dbm(coverage, (antenna && antenna.gain_dbi) || 0.0)
|
|
|
|
if connected?(socket), do: Coverages.subscribe(coverage.id)
|
|
|
|
{:ok,
|
|
socket
|
|
|> assign(:page_title, coverage.name)
|
|
|> assign(:organization, organization)
|
|
|> assign(:coverage, coverage)
|
|
|> assign(:antenna, antenna)
|
|
|> assign(:eirp_dbm, eirp)}
|
|
end
|
|
|
|
@impl true
|
|
def handle_params(_params, _url, socket), do: {:noreply, socket}
|
|
|
|
@impl true
|
|
def handle_event("recompute", _params, socket) do
|
|
case Coverages.queue_compute(socket.assigns.coverage) do
|
|
{:ok, coverage} ->
|
|
{:noreply,
|
|
socket
|
|
|> assign(:coverage, coverage)
|
|
|> put_flash(:info, t("Coverage queued for recompute"))}
|
|
|
|
{:error, :compute_in_progress} ->
|
|
{:noreply, put_flash(socket, :error, t("Coverage is already being computed"))}
|
|
|
|
{:error, _} ->
|
|
{:noreply, put_flash(socket, :error, t("Failed to queue coverage"))}
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def handle_event("delete", _params, socket) do
|
|
case Coverages.delete_coverage(socket.assigns.coverage) do
|
|
{:ok, _} ->
|
|
{:noreply,
|
|
socket
|
|
|> put_flash(:info, t("Coverage deleted"))
|
|
|> push_navigate(to: ~p"/coverage")}
|
|
|
|
{:error, _} ->
|
|
{:noreply, put_flash(socket, :error, t("Unable to delete coverage"))}
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def handle_info({:coverage_status, _status, _extra}, socket) do
|
|
coverage =
|
|
Coverages.get_coverage!(socket.assigns.organization.id, socket.assigns.coverage.id)
|
|
|
|
eirp =
|
|
Coverage.eirp_dbm(
|
|
coverage,
|
|
(socket.assigns.antenna && socket.assigns.antenna.gain_dbi) || 0.0
|
|
)
|
|
|
|
{:noreply, socket |> assign(:coverage, coverage) |> assign(:eirp_dbm, eirp)}
|
|
end
|
|
|
|
@doc false
|
|
def status_badge_class("draft"), do: "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300"
|
|
|
|
def status_badge_class("queued"), do: "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"
|
|
|
|
def status_badge_class("computing"), do: "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300"
|
|
|
|
def status_badge_class("ready"), do: "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300"
|
|
|
|
def status_badge_class("failed"), do: "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300"
|
|
|
|
def status_badge_class(_), do: "bg-gray-100 text-gray-700"
|
|
|
|
@doc false
|
|
def coverage_lat(coverage) do
|
|
case Coverage.location(coverage) do
|
|
{lat, _lon} -> lat
|
|
nil -> nil
|
|
end
|
|
end
|
|
|
|
@doc false
|
|
def coverage_lon(coverage) do
|
|
case Coverage.location(coverage) do
|
|
{_lat, lon} -> lon
|
|
nil -> nil
|
|
end
|
|
end
|
|
|
|
@doc false
|
|
def format_ft(nil), do: "—"
|
|
def format_ft(m) when is_number(m), do: "#{round(m / 0.3048)} ft"
|
|
|
|
@doc false
|
|
def format_mi(nil), do: "—"
|
|
def format_mi(m) when is_number(m), do: "#{Float.round(m / 1609.344, 2)} mi"
|
|
|
|
@doc false
|
|
def format_ghz(nil), do: "—"
|
|
def format_ghz(mhz) when is_number(mhz), do: "#{Float.round(mhz / 1000.0, 3)} GHz"
|
|
|
|
attr :label, :string, required: true
|
|
attr :value, :string, required: true
|
|
|
|
def param_row(assigns) do
|
|
~H"""
|
|
<div class="flex justify-between gap-3">
|
|
<dt class="text-gray-600 dark:text-gray-400">{@label}</dt>
|
|
<dd class="text-gray-900 dark:text-gray-100 font-medium text-right">{@value}</dd>
|
|
</div>
|
|
"""
|
|
end
|
|
end
|