- rover_planning_live/show.ex: use pre-loaded @rover_sites assign
instead of Repo.all(Location) on every keystroke
- rover_locations_live/map.ex: cache locations query with 30s TTL
(was loading + JSON-encoding all good locations in every mount)
- status_live.ex: consolidate 5 separate stat fetches into single
cached blob, so PubSub-triggered refreshes hit cache instead of
running 10+ DB queries each time
show.ex (rover-planning) and show.ex (rover-locations) auth + delete
flows go from nested case/case/case to a `with` ladder fed by a single
`authenticated/1` clause that returns `{:ok, user}` or
`{:error, :unauthenticated}`. The else block enumerates the small set
of failure tuples instead of rebuilding nested error returns.
Other idiomatic-Elixir tightening:
- `progress_summary/1` reduces with a `tally_path/2` multi-clause
helper (status as a head pattern, not three Enum.count passes).
- `paths_by_rover_location/1` extracts `group_to_pair/1`,
`station_position/1`, and `group_lat/1` so each transformation is a
pattern match instead of an anonymous fn with `&& fallbacks`.
- `error_summary/1` is two clauses (empty vs populated errors)
instead of a pipe-into-case.
- Drag-to-edit show.ex consolidates the working-coords assigns into
`assign_working_coords/3`.
Bug fix uncovered while writing tests: `add_station` on a fresh
/rover-planning/new form was a visual no-op on the first click —
cast_assoc replaced the unseeded default Station struct with the new
params row instead of appending. Now the initial changeset is built
from `%{"stations" => %{"0" => %{"position" => "0"}}}` so add_station
appends from the get-go. Regression test added at
test/microwaveprop_web/live/rover_planning_live_test.exs.
New test branches:
- form: add_station appends on first click + remove_station drops
the targeted row.
- rover-planning show: add_rover_site whitespace-input rejection,
delete_rover_site permission denial.
- rover-locations show: save_edit rejects unauthenticated drivers.
Suite: 3228 tests, 0 failures. Credo strict: 0 issues.
Owner / admin click "Edit" on /rover-locations/:id and the marker
becomes draggable. As they drag, the JS hook pushes `location_dragged`
events with the new lat/lon; the LiveView updates `working_lat` /
`working_lon` / `grid` so the page shows the live preview without
writing to the DB. "Save" persists via Rover.update_location, "Cancel"
reverts both the assigns and the marker position.
Implementation notes:
- Switched the map marker from L.circleMarker to a draggable L.marker
with a divIcon (CircleMarker has no `dragging` handler).
- A second `draggingDotIcon` (amber, larger, grab cursor) makes the
edit affordance obvious.
- Server <-> hook coordination uses push_event:
set_marker_draggable / reset_marker. Drag results come back on
pushEvent("location_dragged", { lat, lon }).
- Coordinates row binds to working_lat/working_lon so the displayed
decimals + derived grid update on every drag, not only on save.
Adds a full-screen map view rendering every Good rover-location as a
green circle marker (popup links to the detail page). Uses the standard
OSM/Satellite + Maidenhead grid overlay pattern. Bad locations are
excluded. Adds a Map button next to Add location on the index header.
Renames the rover-location status enum and every label that referenced
it. Existing rows are migrated in place. Also touches the consumers:
- Rover.Location enum + default
- RoverLocationsLive index, status filter, form, show, badges
- Rover.Compute and rover_live (only_ideal_locations → only_good_locations)
- RoverPlanning context candidate filter (status == :good)
- RoverPlanning.Show / Form copy
- All rover-related tests
Rover.update_location/3 and delete_location/2 now resolve any record
when the actor is_admin. The index + show LiveViews surface Edit/Delete
on every row for admin users, matching the existing owner UX.
Each row now has a View link (and the location cell links too) to a
new show LiveView that renders the full record alongside a Leaflet
map centered on the marker. Owners get a Delete button there as well.