import { useState } from 'react'; import { Title, Text, Stack, Group, Card, MultiSelect, Loader, Center, Badge, SimpleGrid, Table, } from '@mantine/core'; import { useQuery } from '@tanstack/react-query'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, } from 'recharts'; import api from '../../services/api'; const fmt = (v: number) => v.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }); const COLORS = ['#228be6', '#40c057', '#7950f2', '#fd7e14']; export function ScenarioComparisonPage() { const [selectedIds, setSelectedIds] = useState([]); // Load all scenarios for the selector const { data: allScenarios } = useQuery({ queryKey: ['board-planning-scenarios-all'], queryFn: async () => { const { data } = await api.get('/board-planning/scenarios'); return data; }, }); // Load comparison data when scenarios are selected const { data: comparison, isLoading: compLoading } = useQuery({ queryKey: ['board-planning-compare', selectedIds], queryFn: async () => { const { data } = await api.get(`/board-planning/compare?ids=${selectedIds.join(',')}`); return data; }, enabled: selectedIds.length >= 1, }); const selectorData = (allScenarios || []).map((s) => ({ value: s.id, label: `${s.name} (${s.scenario_type})`, })); // Build merged chart data with all scenarios const chartData = (() => { if (!comparison?.scenarios?.length) return []; const firstScenario = comparison.scenarios[0]; if (!firstScenario?.projection?.datapoints) return []; return firstScenario.projection.datapoints.map((_: any, idx: number) => { const point: any = { month: firstScenario.projection.datapoints[idx].month }; comparison.scenarios.forEach((s: any, sIdx: number) => { const dp = s.projection?.datapoints?.[idx]; if (dp) { point[`total_${sIdx}`] = dp.operating_cash + dp.operating_investments + dp.reserve_cash + dp.reserve_investments; } }); return point; }); })(); return (
Compare Scenarios Select up to 4 scenarios to compare their projected financial impact side-by-side
{compLoading &&
} {comparison?.scenarios?.length > 0 && ( <> {/* Overlaid Line Chart */} Total Liquidity Projection `$${(v / 1000).toFixed(0)}k`} /> fmt(v)} labelStyle={{ fontWeight: 600 }} /> {comparison.scenarios.map((s: any, idx: number) => ( ))} {/* Summary Metrics Comparison */} Summary Comparison Metric {comparison.scenarios.map((s: any, idx: number) => (
{s.name} ))} End Liquidity {comparison.scenarios.map((s: any) => ( {fmt(s.projection?.summary?.end_liquidity || 0)} ))} Minimum Liquidity {comparison.scenarios.map((s: any) => ( {fmt(s.projection?.summary?.min_liquidity || 0)} ))} Period Change {comparison.scenarios.map((s: any) => { const change = s.projection?.summary?.period_change || 0; return ( = 0 ? 'green' : 'red'}> {change >= 0 ? '+' : ''}{fmt(change)} ); })} Reserve Coverage {comparison.scenarios.map((s: any) => ( {(s.projection?.summary?.reserve_coverage_months || 0).toFixed(1)} months ))} End Operating Cash {comparison.scenarios.map((s: any) => ( {fmt(s.projection?.summary?.end_operating_cash || 0)} ))} End Reserve Cash {comparison.scenarios.map((s: any) => ( {fmt(s.projection?.summary?.end_reserve_cash || 0)} ))}
{/* Risk Flags */} {comparison.scenarios.some((s: any) => (s.projection?.summary?.min_liquidity || 0) < 0) && ( Liquidity Warnings {comparison.scenarios.filter((s: any) => (s.projection?.summary?.min_liquidity || 0) < 0).map((s: any) => ( {s.name}: projected negative liquidity of {fmt(s.projection.summary.min_liquidity)} ))} )} )} {selectedIds.length === 0 && (
Select one or more scenarios above to compare their financial projections
)}
); }