"use client"; import { useCallback, useEffect, useState } from "react"; import { Nav, useAuth } from "@/components/Nav"; interface Dashboard { nodesOnline: number; nodesOffline: number; totalStorage: string; freeStorage: string; movies: number; series: number; episodes: number; availableMovies?: number; availableSeries?: number; mediaFiles?: number; availableFiles?: number; moviesMissingImdb?: number; moviesWithImdbStreams?: number; seriesMissingImdb?: number; seriesWithImdb?: number; scannedFiles?: number; activeStreams: number; currentBandwidth: string; } function formatBytes(bytes: string | number | null | undefined): string { if (bytes === null || bytes === undefined || bytes === "") return "0 B"; let n: bigint; try { n = typeof bytes === "bigint" ? bytes : BigInt(String(bytes)); } catch { return "0 B"; } const units = ["B", "KB", "MB", "GB", "TB"]; let val = Number(n); if (!Number.isFinite(val)) return "0 B"; let i = 0; while (val >= 1024 && i < units.length - 1) { val /= 1024; i++; } return `${val.toFixed(1)} ${units[i]}`; } export default function DashboardPage() { useAuth(); const [data, setData] = useState(null); const [error, setError] = useState(""); const [dedupeMsg, setDedupeMsg] = useState(""); const [imdbMsg, setImdbMsg] = useState(""); const [repairMsg, setRepairMsg] = useState(""); const load = useCallback(() => { fetch("/api/v1/admin/dashboard", { credentials: "include" }) .then(async (r) => { const json = await r.json(); if (!r.ok) { throw new Error(json.error?.message ?? `HTTP ${r.status}`); } setData(json); }) .catch((err) => { console.error(err); setError(err instanceof Error ? err.message : "Laden mislukt"); }); }, []); useEffect(() => { load(); }, [load]); async function repairLibrary() { if ( !confirm( "Bibliotheek herstellen na pad-migratie?\n\n• Duplicaten samenvoegen naar IMDb/TMDB-records\n• Dode (unavailable) bestanden verwijderen\n• Ontbrekende IMDb-IDs backfillen\n\nDit kan enkele minuten duren." ) ) { return; } setRepairMsg("Bezig…"); const res = await fetch("/api/v1/admin/library/repair", { method: "POST", credentials: "include", }); const json = await res.json(); if (!res.ok) { setRepairMsg(json.error?.message ?? "Mislukt"); return; } const imdb = json.imdb ?? {}; setRepairMsg( `Klaar: ${json.moviesMerged ?? 0} film-merges, ${json.seriesMerged ?? 0} serie-merges, ${json.staleFilesRemoved ?? 0} dode files weg · IMDb films +${imdb.movies?.updated ?? 0}, series +${imdb.series?.updated ?? 0}. Herlaad daarna de Stremio-addon.` ); load(); } async function backfillImdb() { if ( !confirm( "IMDb-IDs ophalen via TMDB voor ALLE films én series die die nog missen? Dit kan enkele minuten duren. Series-afleveringenlijst blijft op privé-IDs; IMDb is alleen voor Cinemeta-bronnen." ) ) { return; } setImdbMsg("Bezig…"); const res = await fetch("/api/v1/admin/library/backfill-imdb", { method: "POST", credentials: "include", }); const json = await res.json(); if (!res.ok) { setImdbMsg(json.error?.message ?? "Mislukt"); return; } const movies = json.movies ?? json; const series = json.series ?? {}; setImdbMsg( `Films: gecontroleerd ${movies.checked ?? 0}, bijgewerkt ${movies.updated ?? 0}, open ${movies.remaining ?? 0} · Series: gecontroleerd ${series.checked ?? 0}, bijgewerkt ${series.updated ?? 0}, open ${series.remaining ?? 0}` ); load(); } async function dedupe() { if ( !confirm( "Duplicaten samenvoegen? Titels met dezelfde naam worden samengevoegd (TMDB-versie wint)." ) ) { return; } setDedupeMsg("Bezig…"); const res = await fetch("/api/v1/admin/library/dedupe", { method: "POST", credentials: "include", }); const json = await res.json(); if (!res.ok) { setDedupeMsg(json.error?.message ?? "Mislukt"); return; } setDedupeMsg( `Klaar: ${json.moviesMerged} film-duplicaten, ${json.seriesMerged} serie-duplicaten, ${json.emptied} lege records opgeruimd.` ); load(); } return ( <>