"use client" import { useState, useEffect } from "react" import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from "recharts" import { Loader2, TrendingUp, MemoryStick } from "lucide-react" const TIMEFRAME_OPTIONS = [ { value: "hour", label: "1 Hour" }, { value: "day", label: "24 Hours" }, { value: "week", label: "7 Days" }, { value: "month", label: "30 Days" }, ] interface NodeMetricsData { time: string timestamp: number cpu: number load: number memoryTotal: number memoryUsed: number memoryFree: number memoryZfsArc: number } const CustomCpuTooltip = ({ active, payload, label }: any) => { if (active && payload && payload.length) { return (

{label}

{payload.map((entry: any, index: number) => (
{entry.name}: {entry.value}
))}
) } return null } const CustomMemoryTooltip = ({ active, payload, label }: any) => { if (active && payload && payload.length) { return (

{label}

{payload.map((entry: any, index: number) => (
{entry.name}: {entry.value} GB
))}
) } return null } export function NodeMetricsCharts() { const [timeframe, setTimeframe] = useState("day") const [data, setData] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [visibleLines, setVisibleLines] = useState({ cpu: { cpu: true, load: true }, memory: { memoryTotal: true, memoryUsed: true, memoryZfsArc: true, memoryFree: true }, }) useEffect(() => { console.log("[v0] NodeMetricsCharts component mounted") fetchMetrics() }, [timeframe]) const fetchMetrics = async () => { console.log("[v0] fetchMetrics called with timeframe:", timeframe) setLoading(true) setError(null) try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}` console.log("[v0] Fetching node metrics from:", apiUrl) const response = await fetch(apiUrl) console.log("[v0] Response status:", response.status) console.log("[v0] Response ok:", response.ok) if (!response.ok) { const errorText = await response.text() console.log("[v0] Error response text:", errorText) throw new Error(`Failed to fetch node metrics: ${response.status}`) } const result = await response.json() console.log("[v0] Node metrics result:", result) console.log("[v0] Result keys:", Object.keys(result)) console.log("[v0] Data array length:", result.data?.length || 0) if (!result.data || !Array.isArray(result.data)) { console.error("[v0] Invalid data format - data is not an array:", result) throw new Error("Invalid data format received from server") } if (result.data.length === 0) { console.warn("[v0] No data points received") setData([]) setLoading(false) return } console.log("[v0] First data point sample:", result.data[0]) console.log("[v0] First data point loadavg field:", result.data[0]?.loadavg) console.log("[v0] loadavg type:", typeof result.data[0]?.loadavg) console.log("[v0] loadavg is array:", Array.isArray(result.data[0]?.loadavg)) if (result.data[0]?.loadavg) { console.log("[v0] loadavg length:", result.data[0].loadavg.length) console.log("[v0] loadavg[0]:", result.data[0].loadavg[0]) } const transformedData = result.data.map((item: any) => { const date = new Date(item.time * 1000) let timeLabel = "" if (timeframe === "hour") { timeLabel = date.toLocaleString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false, }) } else if (timeframe === "day") { timeLabel = date.toLocaleString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false, }) } else if (timeframe === "week") { timeLabel = date.toLocaleString("en-US", { month: "short", day: "numeric", hour: "2-digit", hour12: false, }) } else { timeLabel = date.toLocaleString("en-US", { month: "short", day: "numeric", }) } return { time: timeLabel, timestamp: item.time, cpu: item.cpu ? Number((item.cpu * 100).toFixed(2)) : 0, load: item.loadavg ? typeof item.loadavg === "number" ? Number(item.loadavg.toFixed(2)) : Array.isArray(item.loadavg) && item.loadavg.length > 0 ? Number(item.loadavg[0].toFixed(2)) : 0 : 0, memoryTotal: item.memtotal ? Number((item.memtotal / 1024 / 1024 / 1024).toFixed(2)) : 0, memoryUsed: item.memused ? Number((item.memused / 1024 / 1024 / 1024).toFixed(2)) : 0, memoryFree: item.memfree ? Number((item.memfree / 1024 / 1024 / 1024).toFixed(2)) : 0, memoryZfsArc: item.zfsarc ? Number((item.zfsarc / 1024 / 1024 / 1024).toFixed(2)) : 0, } }) setData(transformedData) } catch (err: any) { console.error("[v0] Error fetching node metrics:", err) console.error("[v0] Error message:", err.message) console.error("[v0] Error stack:", err.stack) setError(err.message || "Error loading metrics") } finally { console.log("[v0] fetchMetrics finally block - setting loading to false") setLoading(false) } } const tickInterval = Math.ceil(data.length / 8) const handleLegendClick = (chartType: "cpu" | "memory", dataKey: string) => { setVisibleLines((prev) => ({ ...prev, [chartType]: { ...prev[chartType], [dataKey as keyof (typeof prev)[typeof chartType]]: !prev[chartType][dataKey as keyof (typeof prev)[typeof chartType]], }, })) } const renderLegend = (chartType: "cpu" | "memory") => (props: any) => { const { payload } = props return (
{payload.map((entry: any, index: number) => { const isVisible = visibleLines[chartType][entry.dataKey as keyof (typeof visibleLines)[typeof chartType]] return (
handleLegendClick(chartType, entry.dataKey)} style={{ opacity: isVisible ? 1 : 0.4 }} >
{entry.value}
) })}
) } console.log("[v0] Render state - loading:", loading, "error:", error, "data length:", data.length) if (loading) { console.log("[v0] Rendering loading state") return (
) } if (error) { console.log("[v0] Rendering error state:", error) return (

Metrics data not available yet

{error}

Metrics data not available yet

{error}

) } if (data.length === 0) { console.log("[v0] Rendering no data state") return (

No metrics data available

No metrics data available

) } console.log("[v0] Rendering charts with", data.length, "data points") return (
{/* Timeframe Selector */}
{/* Charts Grid */}
{/* CPU Usage + Load Average Chart */} CPU Usage & Load Average } /> {/* Memory Usage Chart */} Memory Usage } />
) }