import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { metricsApi, devicesApi } from '../../services/api'; import type { Interface, TrafficPoint, ResourcePoint } from '../../types'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend, } from 'recharts'; interface Props { deviceId: number; } function formatBps(val: number): string { if (val >= 1e9) return `${(val / 1e9).toFixed(2)} GB/s`; if (val >= 1e6) return `${(val / 1e6).toFixed(2)} MB/s`; if (val >= 1e3) return `${(val / 1e3).toFixed(1)} KB/s`; return `${Math.round(val)} B/s`; } function formatTime(iso: string, compact: boolean): string { const d = new Date(iso); if (compact) { return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } return d.toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); } const TRAFFIC_RANGES = ['1h', '3h', '6h', '12h', '24h'] as const; const RESOURCE_RANGES = ['6h', '12h', '24h', '7d'] as const; export default function GraphsTab({ deviceId }: Props) { const [trafficIface, setTrafficIface] = useState(''); const [trafficRange, setTrafficRange] = useState('1h'); const [resourceRange, setResourceRange] = useState('24h'); const { data: interfaces = [] } = useQuery({ queryKey: ['interfaces', deviceId], queryFn: () => devicesApi.getInterfaces(deviceId).then((r) => r.data as Interface[]), }); const { data: rawTraffic = [] } = useQuery({ queryKey: ['traffic', deviceId, trafficIface, trafficRange], queryFn: () => metricsApi.interfaceTraffic(deviceId, trafficIface, trafficRange).then((r) => r.data), enabled: !!trafficIface, refetchInterval: 60_000, }); const { data: rawResources = [] } = useQuery({ queryKey: ['resources-history', deviceId, resourceRange], queryFn: () => metricsApi.deviceResources(deviceId, resourceRange).then((r) => r.data as ResourcePoint[]), refetchInterval: 60_000, }); const compact = trafficRange === '1h' || trafficRange === '3h'; const resourceCompact = resourceRange === '6h' || resourceRange === '12h' || resourceRange === '24h'; const trafficData = (rawTraffic as TrafficPoint[]).map((p) => ({ time: formatTime(p.time, compact), rx: Math.round(p.rx), tx: Math.round(p.tx), })); const resourceData = (rawResources as ResourcePoint[]).map((p) => { const memPct = p.memory_total && p.memory_total > 0 ? Math.round(((p.memory_used || 0) / p.memory_total) * 100) : 0; return { time: formatTime(p.time, resourceCompact), cpu: Math.round(p.cpu_load || 0), mem: memPct, }; }); const RangeButtons = ({ ranges, value, onChange, }: { ranges: readonly string[]; value: string; onChange: (v: string) => void; }) => (
{ranges.map((r) => ( ))}
); return (
{/* ── Interface Traffic ── */}

Interface Traffic

{!trafficIface ? (
Select an interface to view traffic history
) : trafficData.length === 0 ? (
No traffic data yet for this interface in the selected range
) : ( formatBps(v as number)} contentStyle={{ fontSize: 12 }} /> )}
{/* ── CPU & Memory ── */}

CPU & Memory Usage

{resourceData.length === 0 ? (
No resource history available yet — data accumulates over time
) : ( `${v}%`} tick={{ fontSize: 10 }} /> `${v}%`} contentStyle={{ fontSize: 12 }} /> )}
); }