Add comma separators to MHz frequencies on beacon pages

Formats frequencies like 10368.1 as "10,368.1" on both the
beacon list table and detail page (subtitle, map label, stat field).
This commit is contained in:
Graham McIntire 2026-04-11 09:37:25 -05:00
parent 6608e6105a
commit 8425edbdc4
4 changed files with 32 additions and 6 deletions

View file

@ -62,6 +62,32 @@ defmodule Microwaveprop.Beacons.Beacon do
def format_mw(mw), do: to_string(mw)
@doc "Formats a frequency in MHz with comma separators (e.g. 10368 → \"10,368\")."
def format_freq(nil), do: ""
def format_freq(mhz) when is_number(mhz) do
int_part = trunc(mhz)
frac = mhz - int_part
formatted_int =
int_part
|> Integer.to_string()
|> String.reverse()
|> String.replace(~r/.{3}/, "\\0,")
|> String.trim_trailing(",")
|> String.reverse()
if frac == 0.0 do
formatted_int
else
decimal = mhz |> :erlang.float_to_binary(decimals: 3) |> trim_trailing_zeros()
[_int, frac_part] = String.split(decimal, ".")
"#{formatted_int}.#{frac_part}"
end
end
def format_freq(mhz), do: to_string(mhz)
defp trim_trailing_zeros(str) do
if String.contains?(str, ".") do
str |> String.trim_trailing("0") |> String.trim_trailing(".")

View file

@ -24,7 +24,7 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
rows={@streams.beacons}
row_click={fn {_id, beacon} -> JS.navigate(~p"/beacons/#{beacon}") end}
>
<:col :let={{_id, beacon}} label="Frequency (MHz)">{beacon.frequency_mhz}</:col>
<: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>
@ -65,7 +65,7 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
rows={@streams.pending}
row_click={fn {_id, beacon} -> JS.navigate(~p"/beacons/#{beacon}") end}
>
<:col :let={{_id, beacon}} label="Frequency (MHz)">{beacon.frequency_mhz}</:col>
<: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>

View file

@ -13,7 +13,7 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
<.header>
{@beacon.callsign}
<:subtitle>
{@beacon.frequency_mhz} MHz &middot; {@beacon.grid}
{Beacon.format_freq(@beacon.frequency_mhz)} MHz &middot; {@beacon.grid}
<span :if={not @beacon.approved} class="badge badge-warning badge-sm ml-2">
Pending approval
</span>
@ -46,7 +46,7 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
class="h-96 w-full rounded-lg border border-base-300 mb-2"
data-lat={@beacon.lat}
data-lon={@beacon.lon}
data-label={"#{@beacon.callsign} · #{@beacon.frequency_mhz} MHz"}
data-label={"#{@beacon.callsign} · #{Beacon.format_freq(@beacon.frequency_mhz)} MHz"}
data-on-the-air={to_string(@beacon.on_the_air)}
data-grid-step={@estimate.grid_step}
data-cells={Jason.encode!(@estimate.cells)}
@ -101,7 +101,7 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
<div class="rounded-lg border border-base-300 bg-base-200/40 p-3 text-sm">
<dl class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-x-4 gap-y-2">
<.stat_field label="Frequency">{@beacon.frequency_mhz} MHz</.stat_field>
<.stat_field label="Frequency">{Beacon.format_freq(@beacon.frequency_mhz)} MHz</.stat_field>
<.stat_field label="Callsign">{@beacon.callsign}</.stat_field>
<.stat_field label="Keying">{Beacon.keying_label(@beacon.keying)}</.stat_field>
<.stat_field label="On the air">

View file

@ -84,7 +84,7 @@ defmodule MicrowavepropWeb.BeaconLiveTest do
beacon = approved_beacon_fixture(user_fixture())
{:ok, _view, html} = live(conn, ~p"/beacons/#{beacon}")
assert html =~ beacon.callsign
assert html =~ "#{beacon.frequency_mhz}"
assert html =~ "10,368.1"
end
test "shows pending badge on unapproved beacons", %{conn: conn} do