import { Card, CardContent } from "@/components/ui/card" import { ChartConfig, ChartContainer } from "@/components/ui/chart" import { useWebSocketContext } from "@/hooks/use-websocket-context" import { formatBytes } from "@/lib/format" import { formatNezhaInfo, formatRelativeTime } from "@/lib/utils" import { NezhaServer, NezhaWebsocketResponse } from "@/types/nezha-api" import { useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { Area, AreaChart, CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts" import { ServerDetailChartLoading } from "./loading/ServerDetailLoading" import AnimatedCircularProgressBar from "./ui/animated-circular-progress-bar" type gpuChartData = { timeStamp: string gpu: number } type cpuChartData = { timeStamp: string cpu: number } type processChartData = { timeStamp: string process: number } type diskChartData = { timeStamp: string disk: number } type memChartData = { timeStamp: string mem: number swap: number } type networkChartData = { timeStamp: string upload: number download: number } type connectChartData = { timeStamp: string tcp: number udp: number } export default function ServerDetailChart({ server_id }: { server_id: string }) { const { lastMessage, connected } = useWebSocketContext() if (!connected) { return } const nezhaWsData = lastMessage ? (JSON.parse(lastMessage.data) as NezhaWebsocketResponse) : null if (!nezhaWsData) { return } const server = nezhaWsData.servers.find((s) => s.id === Number(server_id)) if (!server) { return } const { online } = formatNezhaInfo(nezhaWsData.now, server) if (!online) { return } const gpuStats = server.state.gpu || [] const gpuList = server.host.gpu || [] return (
{gpuStats.length >= 1 && gpuList.length === gpuStats.length ? ( gpuList.map((gpu, index) => ( )) ) : gpuStats.length > 0 ? ( gpuStats.map((gpu, index) => ( )) ) : ( <> )}
) } function GpuChart({ now, gpuStat, gpuName }: { now: number; gpuStat: number; gpuName?: string }) { const [gpuChartData, setGpuChartData] = useState([] as gpuChartData[]) useEffect(() => { if (gpuStat) { const timestamp = Date.now().toString() let newData = [] as gpuChartData[] if (gpuChartData.length === 0) { newData = [ { timeStamp: timestamp, gpu: gpuStat }, { timeStamp: timestamp, gpu: gpuStat }, ] } else { newData = [...gpuChartData, { timeStamp: timestamp, gpu: gpuStat }] } if (newData.length > 30) { newData.shift() } setGpuChartData(newData) } }, [now, gpuStat]) const chartConfig = { gpu: { label: "GPU", }, } satisfies ChartConfig return (
{!gpuName &&

GPU

} {gpuName &&

GPU: {gpuName}

}

{gpuStat.toFixed(2)}%

formatRelativeTime(value)} /> `${value}%`} />
) } function CpuChart({ now, data }: { now: number; data: NezhaServer }) { const [cpuChartData, setCpuChartData] = useState([] as cpuChartData[]) const { cpu } = formatNezhaInfo(now, data) useEffect(() => { if (data) { const timestamp = Date.now().toString() let newData = [] as cpuChartData[] if (cpuChartData.length === 0) { newData = [ { timeStamp: timestamp, cpu: cpu }, { timeStamp: timestamp, cpu: cpu }, ] } else { newData = [...cpuChartData, { timeStamp: timestamp, cpu: cpu }] } if (newData.length > 30) { newData.shift() } setCpuChartData(newData) } }, [data]) const chartConfig = { cpu: { label: "CPU", }, } satisfies ChartConfig return (

CPU

{cpu.toFixed(2)}%

formatRelativeTime(value)} /> `${value}%`} />
) } function ProcessChart({ now, data }: { now: number; data: NezhaServer }) { const { t } = useTranslation() const [processChartData, setProcessChartData] = useState([] as processChartData[]) const { process } = formatNezhaInfo(now, data) useEffect(() => { if (data) { const timestamp = Date.now().toString() let newData = [] as processChartData[] if (processChartData.length === 0) { newData = [ { timeStamp: timestamp, process: process }, { timeStamp: timestamp, process: process }, ] } else { newData = [...processChartData, { timeStamp: timestamp, process: process }] } if (newData.length > 30) { newData.shift() } setProcessChartData(newData) } }, [data]) const chartConfig = { process: { label: "Process", }, } satisfies ChartConfig return (

{t("serverDetailChart.process")}

{process}

formatRelativeTime(value)} />
) } function MemChart({ now, data }: { now: number; data: NezhaServer }) { const { t } = useTranslation() const [memChartData, setMemChartData] = useState([] as memChartData[]) const { mem, swap } = formatNezhaInfo(now, data) useEffect(() => { if (data) { const timestamp = Date.now().toString() let newData = [] as memChartData[] if (memChartData.length === 0) { newData = [ { timeStamp: timestamp, mem: mem, swap: swap }, { timeStamp: timestamp, mem: mem, swap: swap }, ] } else { newData = [...memChartData, { timeStamp: timestamp, mem: mem, swap: swap }] } if (newData.length > 30) { newData.shift() } setMemChartData(newData) } }, [data]) const chartConfig = { mem: { label: "Mem", }, swap: { label: "Swap", }, } satisfies ChartConfig return (

{t("serverDetailChart.mem")}

{mem.toFixed(0)}%

{t("serverDetailChart.swap")}

{swap.toFixed(0)}%

{formatBytes(data.state.mem_used)} / {formatBytes(data.host.mem_total)}
{data.host.swap_total ? ( <> swap: {formatBytes(data.state.swap_used)} / {formatBytes(data.host.swap_total)} ) : ( <>no swap )}
formatRelativeTime(value)} /> `${value}%`} />
) } function DiskChart({ now, data }: { now: number; data: NezhaServer }) { const { t } = useTranslation() const [diskChartData, setDiskChartData] = useState([] as diskChartData[]) const { disk } = formatNezhaInfo(now, data) useEffect(() => { if (data) { const timestamp = Date.now().toString() let newData = [] as diskChartData[] if (diskChartData.length === 0) { newData = [ { timeStamp: timestamp, disk: disk }, { timeStamp: timestamp, disk: disk }, ] } else { newData = [...diskChartData, { timeStamp: timestamp, disk: disk }] } if (newData.length > 30) { newData.shift() } setDiskChartData(newData) } }, [data]) const chartConfig = { disk: { label: "Disk", }, } satisfies ChartConfig return (

{t("serverDetailChart.disk")}

{disk.toFixed(0)}%

{formatBytes(data.state.disk_used)} / {formatBytes(data.host.disk_total)}
formatRelativeTime(value)} /> `${value}%`} />
) } function NetworkChart({ now, data }: { now: number; data: NezhaServer }) { const { t } = useTranslation() const [networkChartData, setNetworkChartData] = useState([] as networkChartData[]) const { up, down } = formatNezhaInfo(now, data) useEffect(() => { if (data) { const timestamp = Date.now().toString() let newData = [] as networkChartData[] if (networkChartData.length === 0) { newData = [ { timeStamp: timestamp, upload: up, download: down }, { timeStamp: timestamp, upload: up, download: down }, ] } else { newData = [...networkChartData, { timeStamp: timestamp, upload: up, download: down }] } if (newData.length > 30) { newData.shift() } setNetworkChartData(newData) } }, [data]) let maxDownload = Math.max(...networkChartData.map((item) => item.download)) maxDownload = Math.ceil(maxDownload) if (maxDownload < 1) { maxDownload = 1 } const chartConfig = { upload: { label: "Upload", }, download: { label: "Download", }, } satisfies ChartConfig return (

{t("serverDetailChart.upload")}

{up >= 1024 ? `${(up / 1024).toFixed(2)}G/s` : up >= 1 ? `${up.toFixed(2)}M/s` : `${(up * 1024).toFixed(2)}K/s`}

{t("serverDetailChart.download")}

{down >= 1024 ? `${(down / 1024).toFixed(2)}G/s` : down >= 1 ? `${down.toFixed(2)}M/s` : `${(down * 1024).toFixed(2)}K/s`}

formatRelativeTime(value)} /> `${value.toFixed(0)}M/s`} />
) } function ConnectChart({ now, data }: { now: number; data: NezhaServer }) { const [connectChartData, setConnectChartData] = useState([] as connectChartData[]) const { tcp, udp } = formatNezhaInfo(now, data) useEffect(() => { if (data) { const timestamp = Date.now().toString() let newData = [] as connectChartData[] if (connectChartData.length === 0) { newData = [ { timeStamp: timestamp, tcp: tcp, udp: udp }, { timeStamp: timestamp, tcp: tcp, udp: udp }, ] } else { newData = [...connectChartData, { timeStamp: timestamp, tcp: tcp, udp: udp }] } if (newData.length > 30) { newData.shift() } setConnectChartData(newData) } }, [data]) const chartConfig = { tcp: { label: "TCP", }, udp: { label: "UDP", }, } satisfies ChartConfig return (

TCP

{tcp}

UDP

{udp}

formatRelativeTime(value)} />
) }