/** * Scenarios list — both Cartesian (engine-generated) and user-defined. * Filter chips at the top; each row links to a detail page. * * The Cartesian set is whatever the latest /recompute produced (default * 120 scenarios). User scenarios survive recomputes. */ import { useQuery } from '@tanstack/react-query'; import { Link } from 'react-router-dom'; import { useState } from 'react'; import { api, type Scenario } from '@/api/client'; import { gbp } from '@/lib/format'; type Filter = 'all' | 'cartesian' | 'user'; export function Scenarios() { const [filter, setFilter] = useState('all'); const scenarios = useQuery({ queryKey: ['scenarios', filter], queryFn: () => api.scenarios.list(filter === 'all' ? undefined : filter), }); return (

Scenarios

Persisted plans. Cartesian rebuilt from /recompute; user-defined survive.

{scenarios.isLoading ? (

Loading…

) : scenarios.isError ? ( ) : scenarios.data && scenarios.data.length > 0 ? ( ) : ( )}
); } function FilterChips({ value, onChange }: { value: Filter; onChange: (v: Filter) => void }) { const opts: { key: Filter; label: string }[] = [ { key: 'all', label: 'All' }, { key: 'cartesian', label: 'Cartesian' }, { key: 'user', label: 'User' }, ]; return (
{opts.map((o) => ( ))}
); } function ScenarioTable({ scenarios }: { scenarios: Scenario[] }) { return (
{scenarios.map((s) => ( ))}
Name / ID Kind Jurisdiction Strategy Leave UK Glide Spending NW seed
{s.name ?? s.external_id} {s.name && (
{s.external_id}
)}
{s.jurisdiction} {s.strategy} y{s.leave_uk_year} {s.glide_path} {gbp(s.spending_gbp)} {gbp(s.nw_seed_gbp)}
); } function KindBadge({ kind }: { kind: Scenario['kind'] }) { const styles = kind === 'user' ? 'bg-emerald-100 text-emerald-800' : 'bg-slate-100 text-slate-600'; return ( {kind} ); } function EmptyState({ filter }: { filter: Filter }) { return (

No scenarios{filter !== 'all' ? ` (${filter})` : ''}.

Run python -m fire_planner recompute-all to populate the Cartesian set, or create a user scenario via POST /scenarios.

); } function ErrorBox({ error }: { error: unknown }) { return (
{String((error as Error)?.message ?? error)}
); }