diff --git a/lib/microwaveprop_web/live/rover_planning_live/show.ex b/lib/microwaveprop_web/live/rover_planning_live/show.ex
index 53b8497c..f0913041 100644
--- a/lib/microwaveprop_web/live/rover_planning_live/show.ex
+++ b/lib/microwaveprop_web/live/rover_planning_live/show.ex
@@ -150,19 +150,24 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
~H|Pending|
end
- defp verdict_badge("clear") do
+ defp verdict_badge("CLEAR") do
assigns = %{}
~H|Clear|
end
- defp verdict_badge("blocked") do
+ defp verdict_badge("BLOCKED") do
assigns = %{}
~H|Blocked|
end
- defp verdict_badge("marginal") do
+ defp verdict_badge("FRESNEL_PARTIAL") do
assigns = %{}
- ~H|Marginal|
+ ~H|Fresnel partial|
+ end
+
+ defp verdict_badge("FRESNEL_MINOR") do
+ assigns = %{}
+ ~H|Fresnel minor|
end
defp verdict_badge(_), do: ""
@@ -209,32 +214,27 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
defp format_distance(_), do: "—"
- defp format_db(nil), do: "—"
- defp format_db(value) when is_number(value), do: "#{Float.round(value * 1.0, 1)} dB"
+ # Two-line station identity for the path table. Returns
+ # `{primary, secondary}` — primary is the callsign (or grid when no
+ # callsign), secondary is a Maidenhead grid (stored or derived from
+ # lat/lon) when it adds info beyond `primary`.
+ defp station_lines(%{station: %{} = station}), do: station_lines_for(station)
+ defp station_lines(_), do: {"—", nil}
- defp format_meters(nil), do: "—"
- defp format_meters(value) when is_number(value), do: "#{Float.round(value * 1.0, 1)} m"
+ defp station_lines_for(%{callsign: c} = s) when is_binary(c) and c != "", do: {c, station_grid(s)}
- # Grid+callsign-first station label. Stations entered as raw lat/lon
- # have no callsign or grid stored, so derive a 6-char grid from the
- # coords — that's what the user wants to see, not bare decimals.
- defp station_label(%{station: %{} = station}), do: station_label_for(station)
- defp station_label(_), do: "—"
+ defp station_lines_for(%{grid: g}) when is_binary(g) and g != "", do: {g, nil}
- defp station_label_for(%{callsign: c, grid: g}) when is_binary(c) and c != "" and is_binary(g) and g != "",
- do: "#{c} · #{g}"
+ defp station_lines_for(%{lat: lat, lon: lon}) when is_number(lat) and is_number(lon),
+ do: {Maidenhead.from_latlon(lat, lon, 6), nil}
- defp station_label_for(%{callsign: c, lat: lat, lon: lon})
- when is_binary(c) and c != "" and is_number(lat) and is_number(lon),
- do: "#{c} · #{Maidenhead.from_latlon(lat, lon, 6)}"
+ defp station_lines_for(_), do: {"—", nil}
- defp station_label_for(%{callsign: c}) when is_binary(c) and c != "", do: c
- defp station_label_for(%{grid: g}) when is_binary(g) and g != "", do: g
+ defp station_grid(%{grid: g}) when is_binary(g) and g != "", do: g
- defp station_label_for(%{lat: lat, lon: lon}) when is_number(lat) and is_number(lon),
- do: Maidenhead.from_latlon(lat, lon, 6)
+ defp station_grid(%{lat: lat, lon: lon}) when is_number(lat) and is_number(lon), do: Maidenhead.from_latlon(lat, lon, 6)
- defp station_label_for(_), do: "—"
+ defp station_grid(_), do: nil
# Endpoint string used for /path?destination=…. Prefers callsign, then
# any stored or derived grid (PathLive's LocationResolver re-resolves
@@ -440,8 +440,6 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
Station |
Status |
Distance |
- Min clearance |
- Diffraction |
Verdict |
@@ -454,17 +452,15 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
]}
title={path_url(@mission, path) && "Open in Path Calculator"}
>
- {station_label(path)} |
+
+ <% {primary, secondary} = station_lines(path) %>
+ {primary}
+ {secondary}
+ |
{status_badge(path.status)} |
{if path.result, do: format_distance(path.result["distance_km"]), else: "—"}
|
-
- {if path.result, do: format_meters(path.result["min_clearance_m"]), else: "—"}
- |
-
- {if path.result, do: format_db(path.result["diffraction_db"]), else: "—"}
- |
{if path.result, do: verdict_badge(path.result["verdict"]), else: "—"}
|
diff --git a/test/microwaveprop_web/live/rover_planning_live_test.exs b/test/microwaveprop_web/live/rover_planning_live_test.exs
index 686d9ac5..52b34248 100644
--- a/test/microwaveprop_web/live/rover_planning_live_test.exs
+++ b/test/microwaveprop_web/live/rover_planning_live_test.exs
@@ -116,7 +116,7 @@ defmodule MicrowavepropWeb.RoverPlanningLiveTest do
refute html =~ ~s(33.189, -96.452 | )
end
- test "station label shows callsign + grid together when both are known", %{conn: conn} do
+ test "station cell renders callsign + grid as separate lines", %{conn: conn} do
user = AccountsFixtures.user_fixture()
mission = create_mission(user, "Callsign-grid mission")
@@ -125,9 +125,81 @@ defmodule MicrowavepropWeb.RoverPlanningLiveTest do
{:ok, _lv, html} = live(conn, ~p"/rover-planning/#{mission.id}")
- # The path table should display "AA5C · EM13se" so the operator can
- # see both at a glance — not just one or the other and not lat/lon.
- assert html =~ "AA5C · EM13se"
+ # Callsign and grid both appear, on separate lines (callsign primary,
+ # grid as a muted secondary). The legacy one-liner "AA5C · EM13se"
+ # no longer appears anywhere.
+ assert html =~ "AA5C"
+ assert html =~ "EM13se"
+ refute html =~ "AA5C · EM13se"
+ end
+
+ test "verdict column renders the badge for completed paths", %{conn: conn} do
+ user = AccountsFixtures.user_fixture()
+ mission = create_mission(user, "Verdict mission")
+
+ [path | _] = Repo.all(Path)
+
+ {:ok, _} =
+ path
+ |> Ecto.Changeset.change(%{
+ status: :complete,
+ result: %{
+ "distance_km" => 12.0,
+ "verdict" => "CLEAR",
+ "min_clearance_m" => 50.0,
+ "diffraction_db" => 0.0
+ }
+ })
+ |> Repo.update()
+
+ {:ok, _lv, html} = live(conn, ~p"/rover-planning/#{mission.id}")
+ assert html =~ "Clear"
+ end
+
+ test "verdict column maps BLOCKED + FRESNEL_PARTIAL to badges", %{conn: conn} do
+ user = AccountsFixtures.user_fixture()
+
+ mission =
+ create_mission(user, "Blocked mission",
+ locations: [
+ %{lat: 32.0, lon: -97.0, status: :good},
+ %{lat: 33.0, lon: -96.0, status: :good}
+ ]
+ )
+
+ [p1, p2] = Repo.all(Path)
+
+ {:ok, _} =
+ p1
+ |> Ecto.Changeset.change(%{
+ status: :complete,
+ result: %{"distance_km" => 8.0, "verdict" => "BLOCKED"}
+ })
+ |> Repo.update()
+
+ {:ok, _} =
+ p2
+ |> Ecto.Changeset.change(%{
+ status: :complete,
+ result: %{"distance_km" => 9.0, "verdict" => "FRESNEL_PARTIAL"}
+ })
+ |> Repo.update()
+
+ {:ok, _lv, html} = live(conn, ~p"/rover-planning/#{mission.id}")
+ assert html =~ "Blocked"
+ assert html =~ "Fresnel"
+ end
+
+ test "path table omits min clearance + diffraction columns", %{conn: conn} do
+ user = AccountsFixtures.user_fixture()
+ mission = create_mission(user, "Trim columns mission")
+
+ {:ok, _lv, html} = live(conn, ~p"/rover-planning/#{mission.id}")
+ # Header columns relevant to a planning summary: Station / Status /
+ # Distance / Verdict. Min clearance + diffraction are detail-level
+ # values better viewed on the Path Calculator itself.
+ refute html =~ "Min clearance"
+ refute html =~ "Diffraction"
end
test "path rows link to /path with mission params prefilled", %{conn: conn} do
@@ -198,7 +270,7 @@ defmodule MicrowavepropWeb.RoverPlanningLiveTest do
"distance_km" => 0,
"min_clearance_m" => 0,
"diffraction_db" => 0,
- "verdict" => "clear"
+ "verdict" => "CLEAR"
}
})
|> Repo.update()