Beacon page improvements: comma formatting, sorting, rounding
- Add CommaNumber JS hook for live comma formatting while typing frequency MHz in the beacon form - Strip commas server-side before changeset validation - Order beacon list by most recently added (desc inserted_at) - Round lat/lon to 6 decimal places in changeset and display - Round height_ft to integer in changeset and display - Display coords at 6 decimal places on show page
This commit is contained in:
parent
9451b7f451
commit
0059317043
6 changed files with 54 additions and 11 deletions
|
|
@ -55,11 +55,35 @@ const UtcClock = {
|
|||
}
|
||||
}
|
||||
|
||||
const CommaNumber = {
|
||||
mounted() {
|
||||
this.el.addEventListener("input", () => this.format())
|
||||
this.format()
|
||||
},
|
||||
updated() { this.format() },
|
||||
format() {
|
||||
const input = this.el.querySelector("input") || this.el
|
||||
const raw = input.value.replace(/,/g, "")
|
||||
if (raw === "" || raw === "-") return
|
||||
const cursor = input.selectionStart
|
||||
const commasBefore = (input.value.slice(0, cursor).match(/,/g) || []).length
|
||||
const parts = raw.split(".")
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|
||||
const formatted = parts.join(".")
|
||||
if (formatted !== input.value) {
|
||||
input.value = formatted
|
||||
const commasAfter = (formatted.slice(0, cursor).match(/,/g) || []).length
|
||||
const newCursor = cursor + (commasAfter - commasBefore)
|
||||
input.setSelectionRange(newCursor, newCursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
||||
const liveSocket = new LiveSocket("/live", Socket, {
|
||||
longPollFallbackMs: 2500,
|
||||
params: initLiveStash({_csrf_token: csrfToken}),
|
||||
hooks: {...colocatedHooks, PropagationMap, UtcClock, ContactMap, ContactsMap, ElevationProfile, WeatherMap, LocateMe, CopyLink, RoverMap, BeaconMap},
|
||||
hooks: {...colocatedHooks, PropagationMap, UtcClock, ContactMap, ContactsMap, ElevationProfile, WeatherMap, LocateMe, CopyLink, RoverMap, BeaconMap, CommaNumber},
|
||||
})
|
||||
|
||||
// Show progress bar on live navigation and form submits
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ defmodule Microwaveprop.Beacons do
|
|||
Phoenix.PubSub.broadcast(Microwaveprop.PubSub, @topic, message)
|
||||
end
|
||||
|
||||
@doc "Returns approved beacons ordered by frequency."
|
||||
@doc "Returns approved beacons ordered by most recently added."
|
||||
def list_beacons do
|
||||
Repo.all(
|
||||
from b in Beacon,
|
||||
where: b.approved == true,
|
||||
order_by: [asc: b.frequency_mhz, asc: b.callsign]
|
||||
order_by: [desc: b.inserted_at]
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -166,6 +166,9 @@ defmodule Microwaveprop.Beacons.Beacon do
|
|||
:notes
|
||||
])
|
||||
|> update_change(:callsign, fn cs -> cs && String.upcase(String.trim(cs)) end)
|
||||
|> update_change(:lat, &round_coord/1)
|
||||
|> update_change(:lon, &round_coord/1)
|
||||
|> update_change(:height_ft, &round_int/1)
|
||||
|> maybe_fill_latlon()
|
||||
|> maybe_fill_grid()
|
||||
|> normalize_bearing_change()
|
||||
|
|
@ -182,6 +185,14 @@ defmodule Microwaveprop.Beacons.Beacon do
|
|||
|> validate_grid_format()
|
||||
end
|
||||
|
||||
defp round_coord(nil), do: nil
|
||||
defp round_coord(v) when is_float(v), do: Float.round(v, 6)
|
||||
defp round_coord(v), do: v
|
||||
|
||||
defp round_int(nil), do: nil
|
||||
defp round_int(v) when is_float(v), do: Float.round(v, 0)
|
||||
defp round_int(v), do: v
|
||||
|
||||
# If grid is blank but lat/lon are valid, derive it.
|
||||
defp maybe_fill_grid(changeset) do
|
||||
grid = get_field(changeset, :grid)
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@ defmodule MicrowavepropWeb.BeaconLive.Form do
|
|||
<.form for={@form} id="beacon-form" phx-change="validate" phx-submit="save">
|
||||
<.input
|
||||
field={@form[:frequency_mhz]}
|
||||
type="number"
|
||||
type="text"
|
||||
label="Frequency (MHz)"
|
||||
step="any"
|
||||
inputmode="decimal"
|
||||
phx-hook="CommaNumber"
|
||||
required
|
||||
/>
|
||||
<.input field={@form[:callsign]} type="text" label="Call" required />
|
||||
|
|
@ -105,12 +106,19 @@ defmodule MicrowavepropWeb.BeaconLive.Form do
|
|||
|
||||
@impl true
|
||||
def handle_event("validate", %{"beacon" => beacon_params}, socket) do
|
||||
changeset = Beacons.change_beacon(socket.assigns.beacon, beacon_params)
|
||||
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, beacon_params)
|
||||
save_beacon(socket, socket.assigns.live_action, strip_commas(beacon_params))
|
||||
end
|
||||
|
||||
defp strip_commas(params) do
|
||||
Map.update(params, "frequency_mhz", nil, fn
|
||||
v when is_binary(v) -> String.replace(v, ",", "")
|
||||
v -> v
|
||||
end)
|
||||
end
|
||||
|
||||
defp save_beacon(socket, :edit, beacon_params) do
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
|
|||
<: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="Height AGL (ft)">{if beacon.height_ft, do: round(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"]}>
|
||||
|
|
@ -69,7 +69,7 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
|
|||
<: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="Height AGL (ft)">{if beacon.height_ft, do: round(beacon.height_ft)}</:col>
|
||||
<:col :let={{_id, beacon}} label="Submitted">
|
||||
{Calendar.strftime(beacon.inserted_at, "%Y-%m-%d %H:%M UTC")}
|
||||
</:col>
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
|
|||
<.stat_field label="Longitude">{format_coord(@beacon.lon)}</.stat_field>
|
||||
<.stat_field label="EIRP">{Beacon.format_mw(@beacon.power_mw)} mW</.stat_field>
|
||||
|
||||
<.stat_field label="Height AGL">{Beacon.format_mw(@beacon.height_ft)} ft</.stat_field>
|
||||
<.stat_field label="Height AGL">{if @beacon.height_ft, do: round(@beacon.height_ft)} ft</.stat_field>
|
||||
<.stat_field label="Bearing">{bearing_label(@beacon.bearing)}</.stat_field>
|
||||
<.stat_field :if={@beacon.beamwidth_deg} label="Beamwidth">
|
||||
{Beacon.format_mw(@beacon.beamwidth_deg)}°
|
||||
|
|
@ -199,7 +199,7 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
|
|||
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) when is_float(value), do: :erlang.float_to_binary(value, decimals: 6)
|
||||
defp format_coord(value), do: to_string(value)
|
||||
|
||||
attr :label, :string, required: true
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue