feat(rover): full-bleed layout matching /map and /weather; remove Mode selector

This commit is contained in:
Graham McIntire 2026-04-25 16:26:19 -05:00
parent 7c506a6453
commit c13a5f53be
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
3 changed files with 210 additions and 252 deletions

View file

@ -1,7 +1,7 @@
defmodule MicrowavepropWeb.RoverLive do defmodule MicrowavepropWeb.RoverLive do
@moduledoc """ @moduledoc """
Rover-planning page: enter the fixed stations you want to work, pick a Rover-planning page: enter the fixed stations you want to work, pick a
band/mode/forecast hour, and the map paints a smoothed propagation band and forecast hour, and the map paints a smoothed propagation
quality heatmap with the top 5 ranked drive-to candidates pinned in the quality heatmap with the top 5 ranked drive-to candidates pinned in the
bottom strip. bottom strip.
""" """
@ -10,7 +10,6 @@ defmodule MicrowavepropWeb.RoverLive do
alias Microwaveprop.Accounts.User alias Microwaveprop.Accounts.User
alias Microwaveprop.Propagation alias Microwaveprop.Propagation
alias Microwaveprop.Propagation.ModeThresholds
alias Microwaveprop.Radio.Maidenhead alias Microwaveprop.Radio.Maidenhead
alias Microwaveprop.Repo alias Microwaveprop.Repo
alias Microwaveprop.Rover alias Microwaveprop.Rover
@ -119,10 +118,6 @@ defmodule MicrowavepropWeb.RoverLive do
{:noreply, assign(socket, band: band)} {:noreply, assign(socket, band: band)}
end end
def handle_event("select_mode", %{"mode" => mode_str}, socket) do
{:noreply, assign(socket, mode: parse_mode(mode_str, socket.assigns.mode))}
end
def handle_event("set_forecast_hour", %{"value" => v}, socket) do def handle_event("set_forecast_hour", %{"value" => v}, socket) do
hour = v |> parse_int(@default_forecast_hour) |> clamp(0, 18) hour = v |> parse_int(@default_forecast_hour) |> clamp(0, 18)
{:noreply, assign(socket, forecast_hour: hour)} {:noreply, assign(socket, forecast_hour: hour)}
@ -471,28 +466,12 @@ defmodule MicrowavepropWeb.RoverLive do
end end
end end
defp parse_mode(mode_str, fallback) do
case mode_str do
"ssb" -> :ssb
"cw" -> :cw
"q65_60a" -> :q65_60a
"q65_120b" -> :q65_120b
_ -> fallback
end
end
defp clamp(v, lo, hi), do: v |> max(lo) |> min(hi) defp clamp(v, lo, hi), do: v |> max(lo) |> min(hi)
defp band_label(band) do defp band_label(band) do
Enum.find_value(@bands, "#{band} MHz", fn b -> if b.value == band, do: b.label end) Enum.find_value(@bands, "#{band} MHz", fn b -> if b.value == band, do: b.label end)
end end
defp mode_label(mode) do
Enum.find_value(ModeThresholds.modes(), "#{mode}", fn {atom, label} ->
if atom == mode, do: label
end)
end
defp station_id(%{id: id}), do: to_string(id) defp station_id(%{id: id}), do: to_string(id)
defp station_id(_), do: nil defp station_id(_), do: nil
@ -501,13 +480,12 @@ defmodule MicrowavepropWeb.RoverLive do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash} current_scope={@current_scope} max_width="max-w-full"> <div class="flex flex-col w-screen h-screen overflow-hidden">
<div class="flex flex-col h-[calc(100vh-3.5rem)] w-full">
<header class="flex items-center justify-between h-11 px-4 bg-base-100 border-b border-base-300 shrink-0"> <header class="flex items-center justify-between h-11 px-4 bg-base-100 border-b border-base-300 shrink-0">
<div class="flex flex-col leading-tight"> <div class="flex flex-col leading-tight">
<span class="font-semibold text-sm">Rover planning</span> <span class="font-semibold text-sm">Rover planning</span>
<span class="text-xs text-base-content/60"> <span class="text-xs text-base-content/60">
{band_label(@band)} · {mode_label(@mode)} · Forecast +{@forecast_hour}h {band_label(@band)} · Forecast +{@forecast_hour}h
</span> </span>
</div> </div>
<button <button
@ -572,18 +550,15 @@ defmodule MicrowavepropWeb.RoverLive do
<div class="absolute bottom-[122px] left-4 z-20 bg-base-100/90 rounded-box shadow p-2 text-xs flex flex-col gap-1"> <div class="absolute bottom-[122px] left-4 z-20 bg-base-100/90 rounded-box shadow p-2 text-xs flex flex-col gap-1">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="w-3 h-3 rounded-sm" style="background-color: #16a34a; opacity: 0.5"> <span class="w-3 h-3 rounded-sm" style="background-color: #16a34a; opacity: 0.5"></span>
</span>
<span>Excellent 10 dB</span> <span>Excellent 10 dB</span>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="w-3 h-3 rounded-sm" style="background-color: #eab308; opacity: 0.5"> <span class="w-3 h-3 rounded-sm" style="background-color: #eab308; opacity: 0.5"></span>
</span>
<span>Good 3..10 dB</span> <span>Good 3..10 dB</span>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="w-3 h-3 rounded-sm" style="background-color: #f97316; opacity: 0.5"> <span class="w-3 h-3 rounded-sm" style="background-color: #f97316; opacity: 0.5"></span>
</span>
<span>Marginal 0..3 dB</span> <span>Marginal 0..3 dB</span>
</div> </div>
<div class="flex items-center gap-2 pt-1 border-t border-base-300/50"> <div class="flex items-center gap-2 pt-1 border-t border-base-300/50">
@ -684,22 +659,6 @@ defmodule MicrowavepropWeb.RoverLive do
</div> </div>
</.sidebar_section> </.sidebar_section>
<.sidebar_section title="MODE">
<select
name="mode"
phx-change="select_mode"
class="select select-sm select-bordered w-full"
>
<option
:for={{atom, label} <- ModeThresholds.modes()}
value={Atom.to_string(atom)}
selected={atom == @mode}
>
{label}
</option>
</select>
</.sidebar_section>
<.sidebar_section title="MAX DRIVE TIME"> <.sidebar_section title="MAX DRIVE TIME">
<input <input
type="range" type="range"
@ -738,7 +697,6 @@ defmodule MicrowavepropWeb.RoverLive do
</aside> </aside>
</div> </div>
</div> </div>
</Layouts.app>
""" """
end end

View file

@ -40,7 +40,7 @@ defmodule Microwaveprop.Rover.ComputeTest do
assert is_list(result.cells) assert is_list(result.cells)
assert length(result.top_candidates) <= 5 assert length(result.top_candidates) <= 5
assert length(result.top_candidates) > 0 refute result.top_candidates == []
# Sorted by score desc. # Sorted by score desc.
scores = Enum.map(result.top_candidates, & &1.score) scores = Enum.map(result.top_candidates, & &1.score)

View file

@ -14,15 +14,15 @@ defmodule MicrowavepropWeb.RoverLiveTest do
assert html =~ "Calculate" assert html =~ "Calculate"
end end
test "shows the right-docked sidebar with the six configured sections", %{conn: conn} do test "shows the right-docked sidebar with the configured sections", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/rover") {:ok, _view, html} = live(conn, ~p"/rover")
assert html =~ ~s(id="rover-sidebar") assert html =~ ~s(id="rover-sidebar")
assert html =~ "FIXED STATIONS" assert html =~ "FIXED STATIONS"
assert html =~ "FORECAST HOUR" assert html =~ "FORECAST HOUR"
assert html =~ "BAND" assert html =~ "BAND"
assert html =~ "MODE"
assert html =~ "MAX DRIVE TIME" assert html =~ "MAX DRIVE TIME"
assert html =~ "MIN ELEV GAIN" assert html =~ "MIN ELEV GAIN"
refute html =~ ">MODE<"
end end
test "displays the three default NTMS stations", %{conn: conn} do test "displays the three default NTMS stations", %{conn: conn} do