Registered users can suggest edits to any contact's core fields (callsigns, grids, band, mode, timestamp). Edits enter an admin approval queue with field-by-field diff view. On approve, changes are applied and enrichment re-enqueued if grids/band changed. Users receive email notification on approve or reject. Also updates dependabot.yml for mix ecosystem.
166 lines
4.9 KiB
Elixir
166 lines
4.9 KiB
Elixir
defmodule MicrowavepropWeb.BeaconLive.Form 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}>
|
|
<.header>
|
|
{@page_title}
|
|
<:subtitle>
|
|
Enter either a grid or lat/lon — whichever you leave blank is filled in
|
|
from the other.
|
|
</:subtitle>
|
|
</.header>
|
|
|
|
<.form for={@form} id="beacon-form" phx-change="validate" phx-submit="save">
|
|
<.input
|
|
field={@form[:frequency_mhz]}
|
|
type="text"
|
|
label="Frequency (MHz)"
|
|
inputmode="decimal"
|
|
phx-hook="CommaNumber"
|
|
required
|
|
/>
|
|
<.input field={@form[:callsign]} type="text" label="Call" required />
|
|
<.input field={@form[:grid]} type="text" label="Grid" />
|
|
<.input field={@form[:lat]} type="number" label="Latitude" step="any" />
|
|
<.input field={@form[:lon]} type="number" label="Longitude" step="any" />
|
|
<.input
|
|
field={@form[:power_mw]}
|
|
type="number"
|
|
label="TX power (EIRP) (mW)"
|
|
step="any"
|
|
required
|
|
/>
|
|
<.input
|
|
field={@form[:height_ft]}
|
|
type="number"
|
|
label="Height above ground (ft)"
|
|
required
|
|
/>
|
|
<.input
|
|
field={@form[:bearing]}
|
|
type="text"
|
|
label="Bearing"
|
|
placeholder="omni or 0-360 degrees"
|
|
/>
|
|
<.input
|
|
field={@form[:beamwidth_deg]}
|
|
type="number"
|
|
label="Beamwidth (degrees)"
|
|
step="any"
|
|
placeholder="Leave blank for omni"
|
|
/>
|
|
<.input
|
|
field={@form[:keying]}
|
|
type="select"
|
|
label="Keying"
|
|
options={Beacon.keying_options()}
|
|
required
|
|
/>
|
|
<.input field={@form[:on_the_air]} type="checkbox" label="On the air" />
|
|
<.input field={@form[:notes]} type="textarea" label="Notes" />
|
|
<footer>
|
|
<.button phx-disable-with="Saving..." variant="primary">Save Beacon</.button>
|
|
<.button navigate={return_path(@return_to, @beacon)}>Cancel</.button>
|
|
</footer>
|
|
</.form>
|
|
</Layouts.app>
|
|
"""
|
|
end
|
|
|
|
@impl true
|
|
def mount(params, _session, socket) do
|
|
{:ok,
|
|
socket
|
|
|> assign(:return_to, return_to(params["return_to"]))
|
|
|> apply_action(socket.assigns.live_action, params)}
|
|
end
|
|
|
|
defp return_to("show"), do: "show"
|
|
defp return_to(_), do: "index"
|
|
|
|
defp apply_action(socket, :edit, %{"id" => id}) do
|
|
beacon = Beacons.get_beacon!(id)
|
|
|
|
socket
|
|
|> assign(:page_title, "Edit beacon")
|
|
|> assign(:beacon, beacon)
|
|
|> assign(:form, to_form(Beacons.change_beacon(beacon)))
|
|
end
|
|
|
|
defp apply_action(socket, :new, _params) do
|
|
beacon = %Beacon{}
|
|
|
|
socket
|
|
|> assign(:page_title, "New beacon")
|
|
|> assign(:beacon, beacon)
|
|
|> assign(:form, to_form(Beacons.change_beacon(beacon)))
|
|
end
|
|
|
|
@impl true
|
|
def handle_event("validate", %{"beacon" => beacon_params}, socket) do
|
|
changeset = Beacons.change_beacon(socket.assigns.beacon, strip_commas(beacon_params))
|
|
{:noreply, assign(socket, form: to_form(changeset, action: :validate))}
|
|
end
|
|
|
|
def handle_event("save", %{"beacon" => beacon_params}, socket) do
|
|
save_beacon(socket, socket.assigns.live_action, strip_commas(beacon_params))
|
|
end
|
|
|
|
defp strip_commas(params) do
|
|
params
|
|
|> Map.update("frequency_mhz", nil, fn
|
|
v when is_binary(v) -> String.replace(v, ",", "")
|
|
v -> v
|
|
end)
|
|
|> Map.update("height_ft", nil, fn
|
|
v when is_binary(v) -> String.replace(v, ~r/\.0*$/, "")
|
|
v -> v
|
|
end)
|
|
end
|
|
|
|
defp save_beacon(socket, :edit, beacon_params) do
|
|
case Beacons.update_beacon(socket.assigns.beacon, beacon_params) do
|
|
{:ok, beacon} ->
|
|
{:noreply,
|
|
socket
|
|
|> put_flash(:info, "Beacon updated")
|
|
|> push_navigate(to: return_path(socket.assigns.return_to, beacon))}
|
|
|
|
{:error, %Ecto.Changeset{} = changeset} ->
|
|
{:noreply, assign(socket, form: to_form(changeset))}
|
|
end
|
|
end
|
|
|
|
defp save_beacon(socket, :new, beacon_params) do
|
|
user = current_user(socket.assigns.current_scope)
|
|
|
|
case Beacons.create_beacon(user, beacon_params) do
|
|
{:ok, beacon} ->
|
|
flash =
|
|
if user,
|
|
do: "Beacon submitted — pending admin approval.",
|
|
else: "Thanks! Your beacon has been submitted and is awaiting admin approval."
|
|
|
|
{:noreply,
|
|
socket
|
|
|> put_flash(:info, flash)
|
|
|> push_navigate(to: return_path(socket.assigns.return_to, beacon))}
|
|
|
|
{:error, %Ecto.Changeset{} = changeset} ->
|
|
{:noreply, assign(socket, form: to_form(changeset))}
|
|
end
|
|
end
|
|
|
|
defp current_user(%{user: user}), do: user
|
|
defp current_user(_), do: nil
|
|
|
|
defp return_path("index", _beacon), do: ~p"/beacons"
|
|
defp return_path("show", beacon), do: ~p"/beacons/#{beacon}"
|
|
end
|