The cached path-detail URL was a thinned-down view (geometry + loss-budget summary + score). The user wanted the same rich display /path renders — terrain chart with feet axes + tooltips, conditions, scoring, link/power budgets, forecast — so PathShow now resolves the row's rover/station/band/heights and push_navigates to /path with those params baked in. The cached result map on Path.result is still what the rover-planning show table reads from for fast row rendering; this just bridges the row click to the canonical Path Calculator.
84 lines
2.9 KiB
Elixir
84 lines
2.9 KiB
Elixir
defmodule MicrowavepropWeb.RoverPlanningLive.PathShow do
|
|
@moduledoc """
|
|
Stable permalink for one rover-planning Path. Resolves the saved
|
|
rover-location, station, band, and antenna heights into the full
|
|
`/path` URL so the operator sees the same terrain chart, conditions
|
|
readout, link / power budget, and forecast that the Path Calculator
|
|
renders. The cached `result` map on the Path row is still consulted
|
|
by the rover-planning show table — this LiveView just bridges the
|
|
row click back to the canonical `/path` view.
|
|
"""
|
|
use MicrowavepropWeb, :live_view
|
|
|
|
import Ecto.Query
|
|
|
|
alias Microwaveprop.Radio.Maidenhead
|
|
alias Microwaveprop.Repo
|
|
alias Microwaveprop.RoverPlanning.Mission
|
|
alias Microwaveprop.RoverPlanning.Path
|
|
|
|
@impl true
|
|
def mount(%{"id" => mission_id, "path_id" => path_id}, _session, socket) do
|
|
case load_path(mission_id, path_id) do
|
|
nil ->
|
|
{:ok,
|
|
socket
|
|
|> put_flash(:error, "Path profile not found.")
|
|
|> push_navigate(to: ~p"/rover-planning/#{mission_id}")}
|
|
|
|
%Path{} = path ->
|
|
{:ok, push_navigate(socket, to: live_path_url(path))}
|
|
end
|
|
end
|
|
|
|
# Builds the `/path` URL with the rover/station/band/heights baked
|
|
# in. Source uses a 10-char Maidenhead grid (max precision) so the
|
|
# rover spot resolves precisely; destination prefers the station's
|
|
# callsign, falling back to grid or coordinates.
|
|
defp live_path_url(%Path{} = path) do
|
|
%Path{rover_location: rover, station: station, mission: %Mission{} = mission} = path
|
|
src = Maidenhead.from_latlon(rover.lat, rover.lon, 10)
|
|
dst = station_endpoint(station)
|
|
band = path.band_mhz || mission.band_mhz
|
|
|
|
query =
|
|
URI.encode_query(%{
|
|
"source" => src,
|
|
"destination" => dst,
|
|
"band" => Integer.to_string(band),
|
|
"src_height_ft" => Float.to_string((mission.rover_height_ft || 8.0) * 1.0),
|
|
"dst_height_ft" => Float.to_string((mission.station_height_ft || 30.0) * 1.0)
|
|
})
|
|
|
|
"/path?" <> query
|
|
end
|
|
|
|
defp station_endpoint(%{callsign: c}) when is_binary(c) and c != "", do: c
|
|
defp station_endpoint(%{grid: g}) when is_binary(g) and g != "", do: g
|
|
|
|
defp station_endpoint(%{lat: lat, lon: lon}) when is_number(lat) and is_number(lon),
|
|
do: Maidenhead.from_latlon(lat, lon, 8)
|
|
|
|
defp station_endpoint(_), do: ""
|
|
|
|
defp load_path(mission_id, path_id) do
|
|
with {:ok, mission_uuid} <- Ecto.UUID.cast(mission_id),
|
|
{:ok, path_uuid} <- Ecto.UUID.cast(path_id) do
|
|
Path
|
|
|> where([p], p.id == ^path_uuid and p.mission_id == ^mission_uuid)
|
|
|> preload([:rover_location, :station, :mission])
|
|
|> Repo.one()
|
|
else
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def render(assigns) do
|
|
~H"""
|
|
<Layouts.app flash={@flash} current_scope={@current_scope} max_width="max-w-3xl">
|
|
<p class="text-sm text-base-content/60 p-8">Opening Path Calculator…</p>
|
|
</Layouts.app>
|
|
"""
|
|
end
|
|
end
|