fix(rover-planning): two-line station, drop noise columns, fix verdict badge
Path table now shows callsign on top with grid muted below, and the detail-only Min clearance / Diffraction columns are gone. The verdict badge matched lowercase 'clear/blocked/marginal' but the worker stores uppercase 'CLEAR/BLOCKED/FRESNEL_PARTIAL/FRESNEL_MINOR' from TerrainAnalysis, so every cell silently rendered empty. Updated the guard clauses to the actual upstream strings and added Fresnel variants.
This commit is contained in:
parent
bd77d1a5b0
commit
d3b96725c8
2 changed files with 105 additions and 37 deletions
|
|
@ -150,19 +150,24 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
|
|||
~H|<span class="badge badge-ghost badge-sm">Pending</span>|
|
||||
end
|
||||
|
||||
defp verdict_badge("clear") do
|
||||
defp verdict_badge("CLEAR") do
|
||||
assigns = %{}
|
||||
~H|<span class="badge badge-success badge-xs">Clear</span>|
|
||||
end
|
||||
|
||||
defp verdict_badge("blocked") do
|
||||
defp verdict_badge("BLOCKED") do
|
||||
assigns = %{}
|
||||
~H|<span class="badge badge-error badge-xs">Blocked</span>|
|
||||
end
|
||||
|
||||
defp verdict_badge("marginal") do
|
||||
defp verdict_badge("FRESNEL_PARTIAL") do
|
||||
assigns = %{}
|
||||
~H|<span class="badge badge-warning badge-xs">Marginal</span>|
|
||||
~H|<span class="badge badge-warning badge-xs">Fresnel partial</span>|
|
||||
end
|
||||
|
||||
defp verdict_badge("FRESNEL_MINOR") do
|
||||
assigns = %{}
|
||||
~H|<span class="badge badge-info badge-xs">Fresnel minor</span>|
|
||||
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
|
|||
<th>Station</th>
|
||||
<th>Status</th>
|
||||
<th>Distance</th>
|
||||
<th>Min clearance</th>
|
||||
<th>Diffraction</th>
|
||||
<th>Verdict</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -454,17 +452,15 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
|
|||
]}
|
||||
title={path_url(@mission, path) && "Open in Path Calculator"}
|
||||
>
|
||||
<td class="font-mono text-xs">{station_label(path)}</td>
|
||||
<td class="font-mono text-xs">
|
||||
<% {primary, secondary} = station_lines(path) %>
|
||||
<div>{primary}</div>
|
||||
<div :if={secondary} class="opacity-60">{secondary}</div>
|
||||
</td>
|
||||
<td>{status_badge(path.status)}</td>
|
||||
<td class="text-xs">
|
||||
{if path.result, do: format_distance(path.result["distance_km"]), else: "—"}
|
||||
</td>
|
||||
<td class="text-xs">
|
||||
{if path.result, do: format_meters(path.result["min_clearance_m"]), else: "—"}
|
||||
</td>
|
||||
<td class="text-xs">
|
||||
{if path.result, do: format_db(path.result["diffraction_db"]), else: "—"}
|
||||
</td>
|
||||
<td>
|
||||
{if path.result, do: verdict_badge(path.result["verdict"]), else: "—"}
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ defmodule MicrowavepropWeb.RoverPlanningLiveTest do
|
|||
refute html =~ ~s(<td class="font-mono text-xs">33.189, -96.452</td>)
|
||||
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()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue