"use client"; import { useEffect, useState, useCallback, useRef } from "react"; import { DashboardStats } from "@/lib/types"; import SummaryCards from "@/components/SummaryCards"; import LeaderboardTable from "@/components/LeaderboardTable"; import AlertsPanel from "@/components/AlertsPanel"; import RefreshButton from "@/components/RefreshButton"; import SettingsModal from "@/components/SettingsModal"; type Tab = "leaderboard" | "alerts"; const LS_KEY = "oversnitch_stats"; function timeAgo(iso: string): string { const diff = Date.now() - new Date(iso).getTime(); const mins = Math.floor(diff / 60_000); if (mins < 1) return "just now"; if (mins < 60) return `${mins}m ago`; const hrs = Math.floor(mins / 60); if (hrs < 24) return `${hrs}h ago`; return new Date(iso).toLocaleDateString(); } export default function Page() { const [tab, setTab] = useState("leaderboard"); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [settingsOpen, setSettingsOpen] = useState(false); const didInit = useRef(false); const load = useCallback(async (force = false) => { setData((prev) => { if (prev) setRefreshing(true); else setLoading(true); return prev; }); setError(null); try { const res = await fetch(force ? "/api/stats?force=1" : "/api/stats"); const json = await res.json(); if (!res.ok) throw new Error(json.error ?? `HTTP ${res.status}`); const stats = json as DashboardStats; setData(stats); try { localStorage.setItem(LS_KEY, JSON.stringify(stats)); } catch {} } catch (e) { setError(e instanceof Error ? e.message : String(e)); } finally { setLoading(false); setRefreshing(false); } }, []); useEffect(() => { if (didInit.current) return; didInit.current = true; try { const raw = localStorage.getItem(LS_KEY); if (raw) setData(JSON.parse(raw) as DashboardStats); } catch {} load(); }, [load]); const hasTautulli = data?.summary.totalWatchHours !== null; const openAlertCount = data?.summary.openAlertCount ?? 0; const generatedAt = data?.generatedAt ?? null; return (
{/* Header */}

OverSnitch

Request & usage analytics

load(true)} loading={refreshing || loading} />
{generatedAt && ( {refreshing ? Refreshing… : <>Updated {timeAgo(generatedAt)} } )}
{/* First-ever load spinner */} {loading && !data && (

Fetching data…

This only takes a moment on first load.

)} {/* Error */} {error && (
Error: {error}
)} {data && ( <> setTab("alerts")} /> {/* Tab bar */}
{(["leaderboard", "alerts"] as const).map((t) => ( ))}
{tab === "leaderboard" && ( )} {tab === "alerts" && } )} setSettingsOpen(false)} onSaved={() => load(true)} />
); }