import { useMemo } from "react"; import { Box, Card, CardContent, Divider, Grid, LinearProgress, Stack, Typography, } from "@mui/material"; import { useNavigate } from "react-router-dom"; import { useCounts, useLibraries, useActivity } from "../hooks/useDashboard"; import { useMonitoringMetrics, useDiskSpace } from "../hooks/useMonitoring"; import { NowPlaying } from "../components/NowPlaying"; import { MetricCard } from "../components/MetricCard"; import { LibraryOverview } from "../components/LibraryOverview"; function formatBytes(bytes: number): string { if (!bytes || bytes === 0) return "0 B"; const units = ["B", "KB", "MB", "GB", "TB"]; let value = bytes; let unitIdx = 0; while (value >= 1000 && unitIdx < units.length - 1) { value /= 1000; unitIdx++; } return `${value.toFixed(1)} ${units[unitIdx]}`; } function formatRate(bytes: number): string { return `${formatBytes(bytes)}/s`; } function formatPct(value: number): string { return `${value.toFixed(1)}%`; } function summarize(values: number[]) { if (values.length === 0) return null; const total = values.reduce((sum, value) => sum + value, 0); return { avg: total / values.length, min: Math.min(...values), max: Math.max(...values), }; } function DiskSpaceCard({ used, available, size, usedPct, }: { used: number; available: number; size: number; usedPct: string; }) { const pct = Math.max(0, Math.min(100, Number.parseFloat(usedPct) || 0)); const barColor = pct < 70 ? "success" : pct < 90 ? "warning" : "error"; return ( Disk space {usedPct} used Used {formatBytes(used)} Free {formatBytes(available)} Total {formatBytes(size)} ); } export function Dashboard() { const navigate = useNavigate(); const { data: counts } = useCounts(); const { data: libraries } = useLibraries(); const { data: activity } = useActivity(); const { data: metrics } = useMonitoringMetrics(); const { data: disk } = useDiskSpace(); const monitoringWindow = useMemo(() => { const samples = metrics?.samples ?? []; if (samples.length === 0) return []; const latestTs = samples.at(-1)?.ts ?? 0; const windowStart = latestTs - 10 * 60; const windowed = samples.filter((sample) => sample.ts >= windowStart); return windowed.length > 0 ? windowed : samples; }, [metrics?.samples]); const cpuSummary = summarize( monitoringWindow.map((sample) => sample.cpu_pct), ); const iowaitSummary = summarize( monitoringWindow .map((sample) => sample.iowait_pct) .filter((value): value is number => value !== undefined), ); const memSummary = summarize( monitoringWindow.map((sample) => sample.mem_pct), ); const netRxSummary = summarize( monitoringWindow.map((sample) => sample.net_rx_bytes_per_sec), ); const netTxSummary = summarize( monitoringWindow.map((sample) => sample.net_tx_bytes_per_sec), ); const diskReadSummary = summarize( monitoringWindow.map((sample) => sample.disk_read_bps), ); const diskWriteSummary = summarize( monitoringWindow.map((sample) => sample.disk_write_bps), ); return ( Activity {activity && ( navigate(`/users?user=${encodeURIComponent(session.user)}`) } /> )} Monitoring Overview {disk && ( )} Library Stats {counts && ( )} {libraries && } ); }