66 lines
2.3 KiB
TypeScript
66 lines
2.3 KiB
TypeScript
import { Sparkline } from '@/components/charts/sparkline.js';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card.js';
|
|
import { BarChart3 } from 'lucide-react';
|
|
|
|
import { fetchLatestPrices, fetchPriceSparklines } from '@/actions/prices.js';
|
|
import { deserialize } from '@/lib/superjson.js';
|
|
|
|
export async function PricesByRegion() {
|
|
const [pricesResult, sparklinesResult] = await Promise.all([fetchLatestPrices(), fetchPriceSparklines()]);
|
|
|
|
const prices = pricesResult.ok
|
|
? deserialize<
|
|
Array<{
|
|
price_mwh: number;
|
|
region_code: string;
|
|
region_name: string;
|
|
}>
|
|
>(pricesResult.data)
|
|
: [];
|
|
|
|
const sparklines = sparklinesResult.ok
|
|
? deserialize<Array<{ region_code: string; points: { value: number }[] }>>(sparklinesResult.data)
|
|
: [];
|
|
|
|
const sparklineMap: Record<string, { value: number }[]> = {};
|
|
for (const s of sparklines) {
|
|
sparklineMap[s.region_code] = s.points;
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<BarChart3 className="h-5 w-5 text-chart-2" />
|
|
Recent Prices by Region
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{prices.length > 0 ? (
|
|
<div className="max-h-100 space-y-3 overflow-y-auto pr-1">
|
|
{prices.map(p => {
|
|
const regionSparkline = sparklineMap[p.region_code];
|
|
return (
|
|
<div key={p.region_code} className="flex items-center gap-3 text-sm">
|
|
<span className="w-16 shrink-0 font-medium">{p.region_code}</span>
|
|
<div className="min-w-0 flex-1">
|
|
{regionSparkline && regionSparkline.length >= 2 && (
|
|
<Sparkline data={regionSparkline} color="hsl(210, 90%, 55%)" height={24} />
|
|
)}
|
|
</div>
|
|
<div className="flex shrink-0 items-baseline gap-1.5">
|
|
<span className="font-mono font-semibold">${p.price_mwh.toFixed(2)}</span>
|
|
<span className="text-xs text-muted-foreground">/MWh</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">No price data available yet.</p>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|