"use client"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; import { fetchMonitor } from "@/lib/nezha-api"; import { formatTime } from "@/lib/utils"; import { formatRelativeTime } from "@/lib/utils"; import { useQuery } from "@tanstack/react-query"; import * as React from "react"; import { useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"; import NetworkChartLoading from "./NetworkChartLoading"; import { NezhaMonitor, ServerMonitorChart } from "@/types/nezha-api"; interface ResultItem { created_at: number; [key: string]: number | null; } export function NetworkChart({ server_id, show, }: { server_id: number; show: boolean; }) { const { t } = useTranslation(); const { data: monitorData } = useQuery({ queryKey: ["monitor", server_id], queryFn: () => fetchMonitor(server_id), enabled: show, refetchOnMount: true, refetchOnWindowFocus: true, refetchInterval: 10000, }); if (!monitorData) return ; if (monitorData?.success && !monitorData.data) { return ( <>

{t("monitor.noData")}

); } const transformedData = transformData(monitorData.data); const formattedData = formatData(monitorData.data); const chartDataKey = Object.keys(transformedData); const initChartConfig = { avg_delay: { label: t("monitor.avgDelay"), }, ...chartDataKey.reduce((acc, key) => { acc[key] = { label: key, }; return acc; }, {} as ChartConfig), } satisfies ChartConfig; return ( ); } export const NetworkChartClient = React.memo(function NetworkChart({ chartDataKey, chartConfig, chartData, serverName, formattedData, }: { chartDataKey: string[]; chartConfig: ChartConfig; chartData: ServerMonitorChart; serverName: string; formattedData: ResultItem[]; }) { const { t } = useTranslation(); const defaultChart = "All"; const [activeCharts, setActiveCharts] = React.useState([ defaultChart, ]); const handleButtonClick = useCallback((chart: string) => { setActiveCharts((prev) => { if (chart === defaultChart) { return [defaultChart]; } const newCharts = prev.filter((c) => c !== defaultChart); const chartIndex = newCharts.indexOf(chart); if (chartIndex === -1) { return newCharts.length === 0 ? [chart] : [...newCharts, chart]; } else { const result = newCharts.filter((c) => c !== chart); return result.length === 0 ? [defaultChart] : result; } }); }, []); const getColorByIndex = useCallback( (chart: string) => { const index = chartDataKey.indexOf(chart); return `hsl(var(--chart-${(index % 10) + 1}))`; }, [chartDataKey], ); const chartButtons = useMemo( () => chartDataKey.map((key) => ( )), [chartDataKey, activeCharts, chartData, handleButtonClick], ); const chartLines = useMemo(() => { if (activeCharts.includes(defaultChart)) { return chartDataKey.map((key) => ( )); } return activeCharts.map((chart) => ( )); }, [activeCharts, chartDataKey, getColorByIndex]); return (
{serverName} {chartDataKey.length} {t("monitor.monitorCount")}
{chartButtons}
formatRelativeTime(value)} /> `${value}ms`} /> { return formatTime(payload[0].payload.created_at); }} /> } /> {activeCharts.includes(defaultChart) && ( } /> )} {chartLines}
); }); const transformData = (data: NezhaMonitor[]) => { const monitorData: ServerMonitorChart = {}; data.forEach((item) => { const monitorName = item.monitor_name; if (!monitorData[monitorName]) { monitorData[monitorName] = []; } for (let i = 0; i < item.created_at.length; i++) { monitorData[monitorName].push({ created_at: item.created_at[i], avg_delay: item.avg_delay[i], }); } }); return monitorData; }; const formatData = (rawData: NezhaMonitor[]) => { const result: { [time: number]: ResultItem } = {}; const allTimes = new Set(); rawData.forEach((item) => { item.created_at.forEach((time) => allTimes.add(time)); }); const allTimeArray = Array.from(allTimes).sort((a, b) => a - b); rawData.forEach((item) => { const { monitor_name, created_at, avg_delay } = item; allTimeArray.forEach((time) => { if (!result[time]) { result[time] = { created_at: time }; } const timeIndex = created_at.indexOf(time); result[time][monitor_name] = timeIndex !== -1 ? avg_delay[timeIndex] : null; }); }); return Object.values(result).sort((a, b) => a.created_at - b.created_at); };