"use client"; import { useEffect, useState } from "react"; import { CartesianGrid, Line, LineChart as ReLineChart, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts"; import { Panel } from "@/components/dashboard/scan-terminal/Panel"; type StreamPoint = { timestamp: string; temp: number; source: string }; type Threshold = { label: string; threshold_c: number; breached: boolean }; type StreamPayload = { points: StreamPoint[]; thresholds: Threshold[]; }; const POLL_INTERVAL_MS = 30_000; export function RealtimeScrollChart({ city, isEn, }: { city: string; isEn: boolean; }) { const [payload, setPayload] = useState({ points: [], thresholds: [] }); useEffect(() => { if (!city) return; let cancelled = false; const fetchStream = () => { fetch(`/api/city/${encodeURIComponent(city)}/realtime-stream`, { cache: "no-store", headers: { Accept: "application/json" }, }) .then(async (res) => { if (!res.ok) return null; return res.json() as Promise; }) .then((data) => { if (cancelled || !data) return; setPayload(data); }) .catch(() => {}); }; fetchStream(); const interval = setInterval(fetchStream, POLL_INTERVAL_MS); return () => { cancelled = true; clearInterval(interval); }; }, [city]); const { points, thresholds } = payload; const latestTemp = points.length ? points[points.length - 1].temp : null; const breached = thresholds.filter((t) => t.breached); const domainMin = thresholds.length ? Math.min(...thresholds.map((t) => t.threshold_c)) - 2 : "auto"; const domainMax = thresholds.length ? Math.max(...thresholds.map((t) => t.threshold_c)) + 2 : "auto"; return (
{/* Status bar */}
{isEn ? "Latest" : "最新"}:{" "} {latestTemp !== null ? `${latestTemp.toFixed(1)}°` : "--"} {isEn ? "Points" : "数据点"}: {points.length} {breached.length > 0 && ( {isEn ? "Breached" : "已触发"}: {breached.map((t) => t.label).join(", ")} )}
{/* Chart */}
{points.length < 2 ? (
{isEn ? "Collecting data..." : "数据采集中..."}
) : ( `${Number(v).toFixed(1)}°`} axisLine={{ stroke: "#cbd5e1" }} tickLine={false} domain={[domainMin, domainMax]} width={40} /> [`${Number(value).toFixed(2)}°`, "Temp"]} labelFormatter={(label) => `${label}`} /> {/* Temperature line */} {/* Threshold lines */} {thresholds.map((t) => ( ))} )}
); }