BMS/frontend/components/dashboard/temperature-trend-chart.tsx
2026-03-19 11:32:17 +00:00

75 lines
3.3 KiB
TypeScript

"use client";
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, ReferenceLine } from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import type { TempBucket } from "@/lib/api";
interface Props {
data: TempBucket[];
loading?: boolean;
}
type ChartRow = { time: string; [roomId: string]: string | number };
function formatTime(iso: string) {
return new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
}
export function TemperatureTrendChart({ data, loading }: Props) {
// Pivot flat rows [{bucket, room_id, avg_temp}] into [{time, "hall-a": 23.1, "hall-b": 24.2}]
const bucketMap = new Map<string, ChartRow>();
for (const row of data) {
const time = formatTime(row.bucket);
if (!bucketMap.has(time)) bucketMap.set(time, { time });
bucketMap.get(time)![row.room_id] = row.avg_temp;
}
const chartData = Array.from(bucketMap.values());
const roomIds = [...new Set(data.map((d) => d.room_id))].sort();
const LINE_COLORS = ["oklch(0.62 0.17 212)", "oklch(0.7 0.15 162)", "oklch(0.75 0.18 84)"];
return (
<Card>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-sm font-semibold">Temperature (°C)</CardTitle>
<div className="flex items-center gap-3 text-xs text-muted-foreground">
{roomIds.map((id, i) => (
<span key={id} className="flex items-center gap-1">
<span className="w-3 h-0.5 inline-block" style={{ backgroundColor: LINE_COLORS[i] }} />
{id}
</span>
))}
</div>
</div>
</CardHeader>
<CardContent>
{loading ? (
<Skeleton className="h-64 w-full" />
) : chartData.length === 0 ? (
<div className="h-[200px] flex items-center justify-center text-sm text-muted-foreground">
Waiting for data...
</div>
) : (
<ResponsiveContainer width="100%" height={200}>
<LineChart data={chartData} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
<CartesianGrid strokeDasharray="3 3" stroke="oklch(1 0 0 / 8%)" />
<XAxis dataKey="time" tick={{ fontSize: 10, fill: "oklch(0.65 0.04 257)" }} tickLine={false} axisLine={false} />
<YAxis tick={{ fontSize: 10, fill: "oklch(0.65 0.04 257)" }} tickLine={false} axisLine={false} domain={["auto", "auto"]} />
<Tooltip
contentStyle={{ backgroundColor: "oklch(0.16 0.04 265)", border: "1px solid oklch(1 0 0 / 9%)", borderRadius: "6px", fontSize: "12px" }}
formatter={(value, name) => [`${value}°C`, name]}
/>
<ReferenceLine y={26} stroke="oklch(0.78 0.17 84)" strokeDasharray="4 4" strokeWidth={1}
label={{ value: "Warn", fontSize: 9, fill: "oklch(0.78 0.17 84)", position: "right" }} />
{roomIds.map((id, i) => (
<Line key={id} type="monotone" dataKey={id} stroke={LINE_COLORS[i]} strokeWidth={2} dot={false} activeDot={{ r: 4 }} />
))}
</LineChart>
</ResponsiveContainer>
)}
</CardContent>
</Card>
);
}