'use client'; import { fetchDemandByRegion } from '@/actions/demand.js'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js'; import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, type ChartConfig, } from '@/components/ui/chart.js'; import { deserialize } from '@/lib/superjson.js'; import { Activity, Server, TrendingUp, Zap } from 'lucide-react'; import { useCallback, useMemo, useState } from 'react'; import { Bar, BarChart, CartesianGrid, ComposedChart, Line, XAxis, YAxis } from 'recharts'; type TimeRange = '24h' | '7d' | '30d' | '90d' | '1y' | '5y' | 'all'; interface DemandRow { region_code: string; region_name: string; day: Date | null; avg_demand: number | null; peak_demand: number | null; datacenter_count: number | null; total_dc_capacity_mw: number | null; } interface DemandChartProps { initialData: DemandRow[]; summaryData: DemandRow[]; } const TIME_RANGES: { value: TimeRange; label: string }[] = [ { value: '7d', label: '7D' }, { value: '30d', label: '30D' }, { value: '90d', label: '90D' }, { value: '1y', label: '1Y' }, { value: '5y', label: '5Y' }, { value: 'all', label: 'ALL' }, ]; const REGION_COLORS: Record = { PJM: 'hsl(210, 80%, 60%)', ERCOT: 'hsl(30, 80%, 55%)', CAISO: 'hsl(145, 65%, 50%)', NYISO: 'hsl(280, 70%, 60%)', ISONE: 'hsl(350, 70%, 55%)', MISO: 'hsl(60, 70%, 50%)', SPP: 'hsl(180, 60%, 50%)', BPA: 'hsl(95, 55%, 50%)', NWMT: 'hsl(310, 50%, 55%)', WAPA: 'hsl(165, 50%, 50%)', TVA: 'hsl(15, 70%, 50%)', DUKE: 'hsl(240, 55%, 60%)', SOCO: 'hsl(350, 55%, 50%)', FPC: 'hsl(45, 60%, 45%)', }; function formatDemandValue(value: number): string { if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`; if (value >= 1_000) return `${(value / 1_000).toFixed(1)}K`; return value.toFixed(0); } function formatDateLabel(date: Date): string { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', }); } export function DemandChart({ initialData, summaryData }: DemandChartProps) { const [timeRange, setTimeRange] = useState('all'); const [selectedRegion, setSelectedRegion] = useState('ALL'); const [chartData, setChartData] = useState(initialData); const [loading, setLoading] = useState(false); const regions = useMemo(() => { const regionSet = new Map(); for (const row of summaryData) { if (!regionSet.has(row.region_code)) { regionSet.set(row.region_code, row.region_name); } } return Array.from(regionSet.entries()).map(([code, name]) => ({ code, name, })); }, [summaryData]); const handleTimeRangeChange = useCallback( async (range: TimeRange) => { setTimeRange(range); setLoading(true); try { const result = await fetchDemandByRegion(selectedRegion, range); if (result.ok) { setChartData(deserialize(result.data)); } } finally { setLoading(false); } }, [selectedRegion], ); const handleRegionChange = useCallback( async (region: string) => { setSelectedRegion(region); setLoading(true); try { const result = await fetchDemandByRegion(region, timeRange); if (result.ok) { setChartData(deserialize(result.data)); } } finally { setLoading(false); } }, [timeRange], ); const trendChartData = useMemo(() => { const filtered = selectedRegion === 'ALL' ? chartData : chartData.filter(r => r.region_code === selectedRegion); const byDay = new Map(); for (const row of filtered) { if (!row.day || row.avg_demand === null) continue; const dateKey = row.day.toISOString().split('T')[0] ?? ''; if (!byDay.has(dateKey)) { byDay.set(dateKey, { date: formatDateLabel(row.day), dateObj: row.day }); } const entry = byDay.get(dateKey); if (entry) { entry[row.region_code] = Math.round(row.avg_demand); } } return Array.from(byDay.values()).sort((a, b) => a.dateObj.getTime() - b.dateObj.getTime()); }, [chartData, selectedRegion]); const trendChartConfig = useMemo(() => { const config: ChartConfig = {}; const activeRegions = selectedRegion === 'ALL' ? regions : regions.filter(r => r.code === selectedRegion); for (const region of activeRegions) { config[region.code] = { label: region.code, color: REGION_COLORS[region.code] ?? 'hsl(0, 0%, 60%)', }; } return config; }, [regions, selectedRegion]); const peakDemandByRegion = useMemo(() => { const peaks = new Map(); for (const row of chartData) { if (!row.day || row.peak_demand === null) continue; const existing = peaks.get(row.region_code); if (!existing || row.peak_demand > existing.peakDemand) { peaks.set(row.region_code, { regionCode: row.region_code, regionName: row.region_name, peakDemand: row.peak_demand, day: row.day, }); } } return Array.from(peaks.values()).sort((a, b) => b.peakDemand - a.peakDemand); }, [chartData]); const dcImpactData = useMemo(() => { const regionAgg = new Map< string, { regionCode: string; regionName: string; totalDemand: number; count: number; dcCapacityMw: number; } >(); for (const row of summaryData) { if (row.avg_demand === null) continue; const existing = regionAgg.get(row.region_code); if (existing) { existing.totalDemand += row.avg_demand; existing.count += 1; existing.dcCapacityMw = row.total_dc_capacity_mw ?? existing.dcCapacityMw; } else { regionAgg.set(row.region_code, { regionCode: row.region_code, regionName: row.region_name, totalDemand: row.avg_demand, count: 1, dcCapacityMw: row.total_dc_capacity_mw ?? 0, }); } } return Array.from(regionAgg.values()) .map(r => { const avgDemand = r.totalDemand / r.count; const dcPercent = avgDemand > 0 ? (r.dcCapacityMw / avgDemand) * 100 : 0; return { region: r.regionCode, regionName: r.regionName, avgDemand: Math.round(avgDemand), dcCapacityMw: Math.round(r.dcCapacityMw), dcPercent: Math.round(dcPercent * 10) / 10, }; }) .sort((a, b) => b.dcPercent - a.dcPercent); }, [summaryData]); const dcImpactConfig = { avgDemand: { label: 'Avg Demand (MW)', color: 'hsl(210, 80%, 60%)', }, dcCapacityMw: { label: 'DC Capacity (MW)', color: 'hsl(340, 75%, 55%)', }, } satisfies ChartConfig; const hasData = trendChartData.length > 0; const hasDcImpact = dcImpactData.length > 0; return (
{/* Controls */}
{TIME_RANGES.map(range => ( ))}
{regions.map(region => ( ))}
{loading && Loading...}
{/* Demand Trend Chart */} Regional Demand Trends Average daily electricity demand by ISO region (MW) {hasData ? ( formatDemandValue(value)} tick={{ fontSize: 11, fill: 'var(--color-muted-foreground)' }} /> { const numVal = typeof value === 'number' ? value : Number(value); return `${formatDemandValue(numVal)} MW`; }} /> } /> } /> {Object.keys(trendChartConfig).map(regionCode => ( ))} ) : ( )}
{/* Peak Demand Records */} Peak Demand Records Highest recorded demand in selected period {peakDemandByRegion.length > 0 ? (
{peakDemandByRegion.map((peak, idx) => (
{idx + 1}

{peak.regionCode}

{formatDateLabel(peak.day)}

{formatDemandValue(peak.peakDemand)} MW

))}
) : ( )}
{/* DC Impact Chart */} Datacenter Load Impact Estimated datacenter capacity as % of regional demand {hasDcImpact ? ( <> formatDemandValue(value)} tick={{ fontSize: 11, fill: 'var(--color-muted-foreground)' }} /> { const numVal = typeof value === 'number' ? value : Number(value); return `${formatDemandValue(numVal)} MW`; }} /> } /> } /> {/* DC % Table */}
{dcImpactData.map(row => (
{row.region}
{row.dcPercent}%
))}
) : ( )}
{/* Summary Stats Row */} {hasDcImpact && (
} label="Total Avg Demand" value={`${formatDemandValue(dcImpactData.reduce((sum, r) => sum + r.avgDemand, 0))} MW`} /> } label="Total DC Capacity" value={`${formatDemandValue(dcImpactData.reduce((sum, r) => sum + r.dcCapacityMw, 0))} MW`} /> } label="Regions Tracked" value={`${dcImpactData.length}`} />
)}
); } function EmptyState({ message }: { message: string }) { return (

{message}

); } function SummaryCard({ icon, label, value }: { icon: React.ReactNode; label: string; value: string }) { return ( {icon}

{label}

{value}

); }