/** * Scenario detail — params + the latest persisted MC projection. * * Reuses FanChart from the What-If page. If the scenario has no MC run * yet, prompts the user to run /recompute. */ import { useQuery } from '@tanstack/react-query'; import { Link, useParams } from 'react-router-dom'; import { api } from '@/api/client'; import { ApiError } from '@/api/client'; import { FanChart } from '@/components/FanChart'; import { gbp, pct } from '@/lib/format'; export function ScenarioDetail() { const params = useParams<{ id: string }>(); const id = Number(params.id); const scen = useQuery({ queryKey: ['scenarios', id], queryFn: () => api.scenarios.get(id), enabled: Number.isFinite(id), }); const proj = useQuery({ queryKey: ['scenarios', id, 'projection'], queryFn: () => api.scenarios.projection(id), enabled: Number.isFinite(id), retry: (count, err) => { // Don't retry the 404 — it's the "no run yet" empty state. if (err instanceof ApiError && err.status === 404) return false; return count < 2; }, }); if (!Number.isFinite(id)) { return

Invalid scenario id.

; } if (scen.isLoading) return

Loading…

; if (scen.isError || !scen.data) { return (
Couldn't load scenario {id}.
); } const s = scen.data; const projection = proj.data; const projection404 = proj.isError && proj.error instanceof ApiError && proj.error.status === 404; return (
← Scenarios

{s.name ?? s.external_id}

{s.kind} · {s.jurisdiction} · {s.strategy} · leave UK y{s.leave_uk_year} ·{' '} {s.glide_path} glide · {s.horizon_years}y horizon

{s.description &&

{s.description}

}
{projection ? ( <>

Portfolio fan

) : projection404 ? (

No projection yet.

Run python -m fire_planner recompute-all or{' '} POST /recompute to fill in MC projections for all scenarios.

) : proj.isLoading ? (

Loading projection…

) : (
{String((proj.error as Error)?.message ?? proj.error)}
)}
); } function Stat({ label, value, accent, }: { label: string; value: string | number; accent?: boolean; }) { return (
{label}
{value}
); }