"use client" import { useEffect, useState } from "react" import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts" import useSWR from "swr" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" interface VMNetworkChartProps { vmid: number vmType: "qemu" | "lxc" initialTimeframe?: "hour" | "day" | "week" | "month" | "year" } const fetcher = async (url: string) => { const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json" }, signal: AbortSignal.timeout(10000), }) if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`) return response.json() } const formatBytes = (bytes: number): string => { if (bytes === 0) return "0 B" const k = 1024 const sizes = ["B", "KB", "MB", "GB", "TB"] const i = Math.floor(Math.log(bytes) / Math.log(k)) return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}` } export function VMNetworkChart({ vmid, vmType, initialTimeframe = "day" }: VMNetworkChartProps) { const [timeframe, setTimeframe] = useState<"hour" | "day" | "week" | "month" | "year">(initialTimeframe) const [visibleLines, setVisibleLines] = useState({ received: true, sent: true }) const [chartData, setChartData] = useState([]) const { data: rrdData } = useSWR(`/api/${vmType}/${vmid}/rrddata?timeframe=${timeframe}`, fetcher, { refreshInterval: 30000, revalidateOnFocus: false, }) useEffect(() => { if (!rrdData) return const transformedData = rrdData.map((point: any) => { const timestamp = new Date(point.time * 1000) const hours = timestamp.getHours().toString().padStart(2, "0") const minutes = timestamp.getMinutes().toString().padStart(2, "0") const month = (timestamp.getMonth() + 1).toString().padStart(2, "0") const day = timestamp.getDate().toString().padStart(2, "0") let timeLabel = `${hours}:${minutes}` if (timeframe === "week" || timeframe === "month") { timeLabel = `${month}/${day}` } else if (timeframe === "year") { timeLabel = `${month}/${day}` } // Calculate traffic in GB for the interval const intervalSeconds = timeframe === "hour" ? 60 : timeframe === "day" ? 60 : timeframe === "week" ? 1800 : 3600 const receivedGB = ((point.netin || 0) * intervalSeconds) / (1024 * 1024 * 1024) const sentGB = ((point.netout || 0) * intervalSeconds) / (1024 * 1024 * 1024) return { time: timeLabel, received: receivedGB, sent: sentGB, } }) setChartData(transformedData) }, [rrdData, timeframe]) const toggleLine = (line: "received" | "sent") => { setVisibleLines((prev) => ({ ...prev, [line]: !prev[line] })) } const getTimeframeLabel = () => { switch (timeframe) { case "hour": return "1 Hour" case "day": return "24 Hours" case "week": return "7 Days" case "month": return "30 Days" case "year": return "1 Year" default: return "24 Hours" } } const CustomTooltip = ({ active, payload }: any) => { if (active && payload && payload.length) { return (

{payload[0].payload.time}

{payload.map((entry: any, index: number) => (
{entry.name}: {formatBytes(entry.value * 1024 * 1024 * 1024)}
))}
) } return null } return (
{/* Timeframe Selector */}
{/* Interactive Legend */}
{/* Chart */} { if (value === 0) return "0" if (value < 0.01) return `${(value * 1024).toFixed(0)} MB` return `${value.toFixed(2)} GB` }} label={{ value: "GB", angle: -90, position: "insideLeft", style: { fill: "hsl(var(--muted-foreground))" } }} /> } /> {visibleLines.received && ( )} {visibleLines.sent && ( )}
) }