import { useState } from 'react'; import { Title, Text, Card, Table, Group, Stack, Badge, Loader, Center, Button, NumberInput, } from '@mantine/core'; import { IconPrinter } from '@tabler/icons-react'; import { useQuery } from '@tanstack/react-query'; import api from '../../services/api'; interface ProjectItem { id: string; name: string; description: string; category: string; estimated_cost: number; target_year: number | null; useful_life_years: number | null; last_replacement_date: string | null; fund_source: string; status: string; priority: number; condition_rating: number | null; year_amounts: Record; beyond: number; } interface CategoryGroup { category: string; projects: ProjectItem[]; } interface CapitalPlanningData { title: string; start_year: number; years: number[]; categories: CategoryGroup[]; year_totals: Record; beyond_total: number; grand_total: number; generated_at: string; } const fmt = (v: number) => v === 0 ? '-' : v.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }); export function CapitalPlanningPage() { const [startYear, setStartYear] = useState(new Date().getFullYear()); const { data, isLoading } = useQuery({ queryKey: ['capital-planning', startYear], queryFn: async () => { const { data } = await api.get(`/reports/capital-planning?startYear=${startYear}`); return data; }, }); if (isLoading) return
; const years = data?.years || []; const hasProjects = (data?.categories || []).some((c) => c.projects.length > 0); return (
Capital Planning Report {data?.title || '5-Year Capital Project Forecast'}
v && setStartYear(Number(v))} min={2020} max={2050} />
{!hasProjects ? ( No capital projects found. Add projects on the Projects page to generate this report. ) : ( {data?.title} Generated {new Date(data?.generated_at || '').toLocaleDateString()} Description Life (yr) Last Done {years.map((y) => ( {y} ))} Beyond {(data?.categories || []).map((cat) => { const catTotals: Record = {}; let catBeyond = 0; for (const y of years) catTotals[y] = 0; for (const p of cat.projects) { for (const y of years) catTotals[y] += p.year_amounts[y] || 0; catBeyond += p.beyond; } return [ {cat.category} , ...cat.projects.map((p) => ( {p.name} {p.status !== 'planned' && ( {p.status} )} {p.useful_life_years || '-'} {p.last_replacement_date ? new Date(p.last_replacement_date).getFullYear() : '-'} {years.map((y) => ( {fmt(p.year_amounts[y] || 0)} ))} {fmt(p.beyond)} )), Subtotal — {cat.category} {years.map((y) => ( {fmt(catTotals[y])} ))} {fmt(catBeyond)} , ]; })} TOTAL {years.map((y) => ( {fmt(data?.year_totals[y] || 0)} ))} {fmt(data?.beyond_total || 0)}
)}
); }