prop/lib/microwaveprop_web/live/rover_locations_live/map.ex
Graham McIntire 473c2ab0ec
perf: fix high-severity LiveView blocking queries
- 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
2026-06-01 15:31:28 -05:00

77 lines
1.9 KiB
Elixir

defmodule MicrowavepropWeb.RoverLocationsLive.Map do
@moduledoc """
`/rover-locations/map` — full-screen map of every rover-location
marked Good. Each marker links to its detail page.
"""
use MicrowavepropWeb, :live_view
import Ecto.Query
alias Microwaveprop.Cache
alias Microwaveprop.Radio.Maidenhead
alias Microwaveprop.Repo
alias Microwaveprop.Rover.Location
@cache_key {__MODULE__, :points}
@cache_ttl_ms 30_000
@impl true
def mount(_params, _session, socket) do
points = cached_points()
{:ok,
assign(socket,
page_title: "Rover Locations Map",
points: points,
points_json: Jason.encode!(points)
)}
end
defp cached_points do
Cache.fetch_or_store(@cache_key, @cache_ttl_ms, fn ->
Location
|> where([l], l.status == :good)
|> Repo.all()
|> Enum.map(&point_payload/1)
end)
end
defp point_payload(%Location{} = loc) do
%{
id: loc.id,
lat: loc.lat,
lon: loc.lon,
grid: Maidenhead.from_latlon(loc.lat, loc.lon, 6),
notes: loc.notes
}
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope} max_width="max-w-7xl">
<.header>
Rover Locations Map
<:subtitle>
{length(@points)} good location{if length(@points) == 1, do: "", else: "s"} —
click a marker for details.
</:subtitle>
<:actions>
<.link navigate={~p"/rover-locations"} class="btn btn-ghost btn-sm">
<.icon name="hero-list-bullet" class="w-4 h-4" /> List
</.link>
</:actions>
</.header>
<div
id="rover-locations-map"
phx-hook="RoverLocationsMap"
phx-update="ignore"
data-points={@points_json}
class="h-[75vh] rounded border border-base-300 z-0"
>
</div>
</Layouts.app>
"""
end
end