<.input field={s[:lat]} type="number" step="any" label="Lat" />
@@ -265,7 +274,7 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Form do
type="button"
phx-click="remove_station"
phx-value-index={s.index}
- class="btn btn-ghost btn-sm text-error mb-2"
+ class="btn btn-ghost btn-sm text-error mt-6"
title="Remove station"
>
<.icon name="hero-trash" class="w-4 h-4" />
diff --git a/lib/microwaveprop_web/live/rover_planning_live/show.ex b/lib/microwaveprop_web/live/rover_planning_live/show.ex
index a8d3ed53..d3c5b38d 100644
--- a/lib/microwaveprop_web/live/rover_planning_live/show.ex
+++ b/lib/microwaveprop_web/live/rover_planning_live/show.ex
@@ -124,12 +124,18 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
%{total: total, complete: complete, pending: pending, failed: failed}
end
- defp format_distance(km) when is_number(km), do: "#{Float.round(km, 1)} km / #{Float.round(km * 0.621371, 1)} mi"
+ # JSONB round-trips bare zeros as integers (e.g. `0` not `0.0`), and
+ # `Float.round/2` only accepts floats. Coerce with `* 1.0` everywhere
+ # we round a value that came out of a `:map` column.
+ defp format_distance(km) when is_number(km), do: "#{Float.round(km * 1.0, 1)} km / #{Float.round(km * 0.621371, 1)} mi"
defp format_distance(_), do: "—"
defp format_db(nil), do: "—"
- defp format_db(value) when is_number(value), do: "#{Float.round(value, 1)} dB"
+ defp format_db(value) when is_number(value), do: "#{Float.round(value * 1.0, 1)} dB"
+
+ defp format_meters(nil), do: "—"
+ defp format_meters(value) when is_number(value), do: "#{Float.round(value * 1.0, 1)} m"
defp rover_label(%{rover_location: %{lat: lat, lon: lon}}) when is_number(lat) and is_number(lon) do
grid = Maidenhead.from_latlon(lat, lon, 6)
@@ -243,9 +249,7 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
{if path.result, do: format_distance(path.result["distance_km"]), else: "—"}
- {if path.result,
- do: "#{Float.round(path.result["min_clearance_m"] || 0.0, 1)} m",
- else: "—"}
+ {if path.result, do: format_meters(path.result["min_clearance_m"]), else: "—"}
|
{if path.result, do: format_db(path.result["diffraction_db"]), else: "—"}
diff --git a/test/microwaveprop_web/live/rover_planning_live_test.exs b/test/microwaveprop_web/live/rover_planning_live_test.exs
index 994478e9..d775e91e 100644
--- a/test/microwaveprop_web/live/rover_planning_live_test.exs
+++ b/test/microwaveprop_web/live/rover_planning_live_test.exs
@@ -4,8 +4,10 @@ defmodule MicrowavepropWeb.RoverPlanningLiveTest do
import Phoenix.LiveViewTest
alias Microwaveprop.AccountsFixtures
+ alias Microwaveprop.Repo
alias Microwaveprop.Rover
alias Microwaveprop.RoverPlanning
+ alias Microwaveprop.RoverPlanning.Path
alias Microwaveprop.Terrain.ElevationClient
setup do
@@ -69,6 +71,34 @@ defmodule MicrowavepropWeb.RoverPlanningLiveTest do
assert html =~ "Path profiles"
end
+ test "renders integer path-result values without crashing", %{conn: conn} do
+ # JSONB round-trips bare zeros as integers (e.g. `0` not `0.0`), so the
+ # show page MUST tolerate non-float numerics in `result`. Previously
+ # `Float.round(value, n)` crashed with FunctionClauseError when value
+ # came back as integer 0.
+ user = AccountsFixtures.user_fixture()
+ mission = create_mission(user, "Integer result mission")
+
+ [path | _] = Repo.all(Path)
+
+ {:ok, _} =
+ path
+ |> Ecto.Changeset.change(%{
+ status: :complete,
+ result: %{
+ "distance_km" => 0,
+ "min_clearance_m" => 0,
+ "diffraction_db" => 0,
+ "verdict" => "clear"
+ }
+ })
+ |> Repo.update()
+
+ {:ok, _lv, html} = live(conn, ~p"/rover-planning/#{mission.id}")
+ assert html =~ "Integer result mission"
+ assert html =~ "Clear"
+ end
+
test "redirects when mission missing", %{conn: conn} do
missing = Ecto.UUID.generate()
@@ -83,6 +113,21 @@ defmodule MicrowavepropWeb.RoverPlanningLiveTest do
live(conn, ~p"/rover-planning/new")
end
+ test "clicking 'Add station' does not show 'can't be blank' on the new row", %{conn: conn} do
+ user = AccountsFixtures.user_fixture()
+ conn = log_in_user(conn, user)
+ {:ok, lv, _html} = live(conn, ~p"/rover-planning/new")
+
+ html =
+ lv
+ |> element("button[phx-click='add_station']")
+ |> render_click()
+
+ # The new (untouched) station must not render a validation error until
+ # the user actually interacts with its input.
+ refute html =~ "can't be blank"
+ end
+
test "typing into the form does not flip the only_known_good checkbox", %{conn: conn} do
user = AccountsFixtures.user_fixture()
conn = log_in_user(conn, user)
|