84 lines
3.3 KiB
TypeScript
84 lines
3.3 KiB
TypeScript
'use client';
|
|
|
|
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@/components/ui/sheet.js';
|
|
import type { DatacenterMarkerData } from './datacenter-marker.js';
|
|
import type { RegionHeatmapData } from './region-overlay.js';
|
|
|
|
interface RegionDetailPanelProps {
|
|
region: RegionHeatmapData | null;
|
|
datacenters: DatacenterMarkerData[];
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function RegionDetailPanel({ region, datacenters, onClose }: RegionDetailPanelProps) {
|
|
const regionDatacenters = region
|
|
? datacenters.filter(dc => dc.region_code === region.code).sort((a, b) => b.capacity_mw - a.capacity_mw)
|
|
: [];
|
|
|
|
return (
|
|
<Sheet open={region !== null} onOpenChange={open => !open && onClose()}>
|
|
<SheetContent side="right" className="overflow-y-auto bg-zinc-950 text-zinc-100">
|
|
{region && (
|
|
<>
|
|
<SheetHeader>
|
|
<SheetTitle className="text-zinc-100">{region.name}</SheetTitle>
|
|
<SheetDescription className="text-zinc-400">Grid region {region.code}</SheetDescription>
|
|
</SheetHeader>
|
|
|
|
<div className="flex flex-col gap-4 p-4">
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<MetricItem
|
|
label="Avg Price (24h)"
|
|
value={region.avgPrice !== null ? `$${region.avgPrice.toFixed(2)}/MWh` : 'No data'}
|
|
/>
|
|
<MetricItem
|
|
label="Max Price (24h)"
|
|
value={region.maxPrice !== null ? `$${region.maxPrice.toFixed(2)}/MWh` : 'No data'}
|
|
/>
|
|
<MetricItem
|
|
label="Avg Demand"
|
|
value={region.avgDemand !== null ? `${Math.round(region.avgDemand).toLocaleString()} MW` : 'No data'}
|
|
/>
|
|
<MetricItem label="Datacenters" value={String(region.datacenterCount ?? 0)} />
|
|
<MetricItem
|
|
label="Total DC Capacity"
|
|
value={
|
|
region.totalDcCapacityMw !== null
|
|
? `${Math.round(region.totalDcCapacityMw).toLocaleString()} MW`
|
|
: '0 MW'
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
{regionDatacenters.length > 0 && (
|
|
<div>
|
|
<h3 className="mb-2 text-sm font-semibold text-zinc-300">Datacenters in Region</h3>
|
|
<div className="flex flex-col gap-2">
|
|
{regionDatacenters.map(dc => (
|
|
<div key={dc.id} className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
<div className="text-sm font-medium text-zinc-200">{dc.name}</div>
|
|
<div className="mt-0.5 text-xs text-zinc-500">
|
|
{dc.operator} · {dc.capacity_mw} MW · {dc.status}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</>
|
|
)}
|
|
</SheetContent>
|
|
</Sheet>
|
|
);
|
|
}
|
|
|
|
function MetricItem({ label, value }: { label: string; value: string }) {
|
|
return (
|
|
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
<div className="mb-0.5 text-xs font-medium text-zinc-500">{label}</div>
|
|
<div className="text-sm font-semibold text-zinc-200">{value}</div>
|
|
</div>
|
|
);
|
|
}
|