"use client"; import { useEffect, useState, useRef } from "react"; import { getStats, type Stats, type NetworkInterface } from "./lib/getStats"; import NavBar from "./components/NavBar"; import Hero from "./components/Hero"; import StatsGrid from "./components/StatsGrid"; import ServicesCard from "./components/ServicesCard"; import UptimeCard from "./components/UptimeCard"; import NetworkCard from "./components/NetworkCard"; import LinksGrid from "./components/LinksGrid"; import { useCheckAuth } from "@/hooks/useCheckAuth"; import { useRouter } from "next/navigation"; export default function Home() { const router = useRouter(); useCheckAuth(); const [stats, setStats] = useState(null); const [netSpeed, setNetSpeed] = useState< Record >({}); // We use Refs for these because changing them shouldn't trigger a "refresh" // but we need them to calculate the delta (speed) between fetches. const prevNetRef = useRef | null>(null); const lastFetchRef = useRef(0); useEffect(() => { const fetchData = async () => { try { const now = Date.now(); const data = await getStats(); // 1. Calculate speeds if we have previous data if (prevNetRef.current && lastFetchRef.current > 0) { const elapsed = (now - lastFetchRef.current) / 1000; const speeds: Record = {}; for (const iface of Object.keys(data.network)) { const prev = prevNetRef.current[iface]; if (prev) { speeds[iface] = { rx: Math.max(0, (data.network[iface].rx - prev.rx) / elapsed), tx: Math.max(0, (data.network[iface].tx - prev.tx) / elapsed), }; } } setNetSpeed(speeds); } // 2. Update our "silent" trackers prevNetRef.current = data.network; lastFetchRef.current = now; // 3. Update the UI state with new data // React's Virtual DOM will only update the changed text/numbers. setStats(data); } catch (e) { if (e instanceof Error && e.message === "UNAUTHORIZED") { router.push( "/auth?callbackUrl=" + encodeURIComponent(window.location.pathname), ); return; } console.error("Dashboard fetch failed:", e); } }; // Initial fetch fetchData(); // Start the 4s loop const id = setInterval(fetchData, 4000); // Clean up on unmount so we don't have multiple intervals running return () => clearInterval(id); }, []); // Empty array means this setup only happens ONCE. // Derived values for the UI const primaryIface = stats ? Object.keys(stats.network).find( (k) => !k.startsWith("docker") && !k.startsWith("br-") && stats.network[k].rx > 0, ) : null; const primarySpeed = primaryIface ? netSpeed[primaryIface] || null : null; return (

System Stats

); }