'use client'; import { useMemo } from 'react'; import { CartesianGrid, Label, Scatter, ScatterChart, XAxis, YAxis, ZAxis } from 'recharts'; import type { SuperJSONResult } from 'superjson'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js'; import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from '@/components/ui/chart.js'; import { deserialize } from '@/lib/superjson.js'; interface RegionCapacityPrice { region_code: string; avg_price: number; total_capacity_mw: number; } interface CorrelationChartProps { data: SuperJSONResult; } const REGION_COLORS: Record = { PJM: 'hsl(210, 90%, 55%)', ERCOT: 'hsl(25, 90%, 55%)', CAISO: 'hsl(140, 70%, 45%)', NYISO: 'hsl(280, 70%, 55%)', ISONE: 'hsl(340, 70%, 55%)', MISO: 'hsl(55, 80%, 50%)', SPP: 'hsl(180, 60%, 45%)', }; const chartConfig: ChartConfig = { correlation: { label: 'DC Capacity vs. Avg Price', color: 'hsl(210, 90%, 55%)', }, }; interface ScatterPoint { region_code: string; total_capacity_mw: number; avg_price: number; fill: string; } function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null; } function getNumberProp(obj: Record, key: string): number { const val = obj[key]; return typeof val === 'number' ? val : 0; } function getScatterPayload(obj: Record): ScatterPoint | null { const payload = obj['payload']; if (!isRecord(payload)) return null; const regionCode = typeof payload['region_code'] === 'string' ? payload['region_code'] : ''; const fill = typeof payload['fill'] === 'string' ? payload['fill'] : 'hsl(0,0%,50%)'; return { region_code: regionCode, total_capacity_mw: getNumberProp(payload, 'total_capacity_mw'), avg_price: getNumberProp(payload, 'avg_price'), fill, }; } function CustomDot(props: unknown): React.JSX.Element { if (!isRecord(props)) return ; const cx = getNumberProp(props, 'cx'); const cy = getNumberProp(props, 'cy'); const payload = getScatterPayload(props); if (!payload) return ; return ( {payload.region_code} ); } export function CorrelationChart({ data }: CorrelationChartProps) { const rows = useMemo(() => deserialize(data), [data]); const scatterData: ScatterPoint[] = useMemo( () => rows .filter(r => r.total_capacity_mw > 0 || r.avg_price > 0) .map(r => ({ region_code: r.region_code, total_capacity_mw: Math.round(r.total_capacity_mw), avg_price: Number(r.avg_price.toFixed(2)), fill: REGION_COLORS[r.region_code] ?? 'hsl(0, 0%, 50%)', })), [rows], ); if (scatterData.length === 0) { return ( DC Capacity vs. Electricity Price No data available for correlation analysis.

No data available. Ingest price data and seed datacenters first.

); } return ( DC Capacity vs. Electricity Price Datacenter capacity (MW) versus average electricity price per region `${v} MW`}> `$${v}`}> { const nameStr = String(name); const valStr = String(value); if (nameStr === 'DC Capacity') return [`${valStr} MW`, undefined]; if (nameStr === 'Avg Price') return [`$${valStr}/MWh`, undefined]; return [valStr, undefined]; }} /> } /> ); }