import { ChevronDown, Loader2, RefreshCw } from "lucide-react"; import { useEffect, useState } from "react"; import { api, errorMessage } from "../api"; import { Alert, EmptyRow, Metric, PageFrame } from "../components/ui"; import { displayUsername, formatBytes, formatQuantity } from "../lib/format"; import type { Navigate } from "../routing"; import type { AccountStorageRow, StorageStatsResponse } from "../types"; export function StoragePage({ navigate: _navigate }: { navigate: Navigate }) { const [stats, setStats] = useState(null); const [rows, setRows] = useState([]); const [hasMore, setHasMore] = useState(false); const [offset, setOffset] = useState(0); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); async function loadStats() { try { setStats(await api.storageStats()); } catch { // Stats are a header nicety; a failure here shouldn't block the list. } } async function loadAccounts(next = false) { setBusy(true); setError(""); const at = next ? offset : 0; const params = new URLSearchParams({ limit: "50", offset: String(at) }); try { const result = await api.storageAccounts(params); const page = result.rows ?? []; setRows((current) => (next ? [...current, ...page] : page)); setOffset(result.next_offset); setHasMore(Boolean(result.has_more)); } catch (err) { setError(errorMessage(err)); } finally { setBusy(false); } } function refresh() { void loadStats(); void loadAccounts(false); } useEffect(() => { refresh(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Physical is what actually consumes disk/S3 (deduplicated); logical is the // sum of what the per-account table below adds up to. They legitimately // differ when the same content is shared by more than one document/photo. const dedupBytes = stats ? Math.max(0, Number(stats.LogicalBytes) - Number(stats.PhysicalBytes)) : 0; return ( {"Refresh"} } > {error && {error}}
0 ? "good" : "neutral"} />
0 ? "warn" : "neutral"} />
{rows.map((row) => ( ))} {rows.length === 0 && }
{"User ID"} {"Account"} {"Storage used"} {"Files"}
{row.UserID} {displayUsername(row.Username) || row.FirstName || "-"} {formatBytes(row.Bytes)} {formatQuantity(row.FileCount)}
{hasMore && (
)}
); }