import type { ChartConfiguration } from "chart.js"; import { useMemo } from "react"; import { BarChart3 } from "lucide-react"; import type { CityDetail } from "@/lib/dashboard-types"; import { useChart } from "@/hooks/useChart"; import { useI18n } from "@/hooks/useI18n"; import { getTemperatureChartData } from "@/lib/dashboard-utils"; export function AiCityTemperatureChart({ detail }: { detail: CityDetail }) { const { locale } = useI18n(); const chartData = useMemo( () => getTemperatureChartData(detail, locale), [detail, locale], ); const forecastLabel = chartData?.datasets.hasMgmHourly ? locale === "en-US" ? "MGM forecast" : "MGM 预测" : locale === "en-US" ? "DEB forecast" : "DEB 预测"; const observationLabel = chartData?.observationLabel || (locale === "en-US" ? "METAR obs" : "METAR 实况"); const canvasRef = useChart(() => { if (!chartData) { return { data: { datasets: [], labels: [] }, type: "line", } satisfies ChartConfiguration<"line">; } const forecastPoints = chartData.datasets.hasMgmHourly ? chartData.datasets.mgmHourlyPoints : chartData.datasets.debPast.map( (value, index) => value ?? chartData.datasets.debFuture[index], ); return { data: { datasets: [ { borderColor: "#4DA3FF", borderWidth: 2, data: forecastPoints, fill: false, label: forecastLabel, pointRadius: 0, spanGaps: true, tension: 0.32, }, { backgroundColor: "#22C55E", borderColor: "#22C55E", borderWidth: 0, data: chartData.datasets.metarPoints, fill: false, label: observationLabel, pointHoverRadius: 5, pointRadius: 3.5, showLine: false, }, ], labels: chartData.times, }, options: { interaction: { intersect: false, mode: "index" }, layout: { padding: { bottom: 2, left: 0, right: 8, top: 8 } }, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: "rgba(11, 18, 32, 0.96)", borderColor: "rgba(77, 163, 255, 0.38)", borderWidth: 1, }, }, responsive: true, scales: { x: { grid: { color: "rgba(159, 178, 199, 0.08)" }, ticks: { callback: (_value, index) => typeof index === "number" && index % 4 === 0 ? chartData.times[index] : "", color: "#6B7A90", font: { size: 10 }, maxTicksLimit: 6, maxRotation: 0, }, }, y: { grid: { color: "rgba(159, 178, 199, 0.08)" }, max: chartData.max, min: chartData.min, ticks: { callback: (value) => `${Number(value).toFixed(chartData.yTickStep < 1 ? 1 : 0)}${detail.temp_symbol || "°C"}`, color: "#6B7A90", font: { size: 10 }, maxTicksLimit: 5, stepSize: chartData.yTickStep, }, }, }, }, type: "line", } satisfies ChartConfiguration<"line">; }, [chartData, detail.temp_symbol, forecastLabel, observationLabel]); return (
{locale === "en-US" ? "Evidence · intraday path" : "证据 · 今日日内路径"}
{chartData ? (
{forecastLabel} {observationLabel}
) : null}
); }