import { Link } from 'react-router-dom'; import type { CurrentUser, ResourceSample } from '@reforger-panel/shared'; import { useConfiguration, useResourceHistory, useServerResources, useServers, } from '../api/hooks.js'; import { formatBytes, formatDuration } from '../lib/format.js'; import { Card, Spinner } from '../components/ui.js'; import { TimeSeriesChart } from '../components/charts.js'; import { ConfigSummaryRows, CurrentPlayersCard, OpsHealthCard, RecentActivityCard, } from '../components/widgets.js'; export function OverviewPage({ user }: { user: CurrentUser }) { const { data: serversData, isLoading } = useServers(); const server = serversData?.servers[0]; if (isLoading) return ; if (!server) { return (

No servers found. Run npm run db:seed{' '} to create the training server.

); } return ; } function seriesOf( samples: ResourceSample[] | undefined, pick: (s: ResourceSample) => number, ): { t: number; v: number }[] { return (samples ?? []).map((s) => ({ t: s.t, v: pick(s) })); } function Dashboard({ user, slug }: { user: CurrentUser; slug: string }) { const { data: serversData } = useServers(); const server = serversData?.servers.find((s) => s.slug === slug); const { data: resources } = useServerResources(slug); const { data: config } = useConfiguration(slug); const { data: history } = useResourceHistory(slug); if (!server) return null; const installedMods = config?.config.mods ?? []; const samples = history?.samples; const memoryLimit = resources?.memoryLimitBytes ?? samples?.at(-1)?.memoryLimitBytes ?? null; const cpuLimit = resources?.cpuLimitPercent ?? samples?.at(-1)?.cpuLimitPercent ?? 100; const diskUsed = resources?.diskBytes ?? null; const diskLimit = resources?.diskLimitBytes ?? null; const diskPercent = diskUsed !== null && diskLimit ? (diskUsed / diskLimit) * 100 : null; return (

{resources ? `${resources.cpuPercent.toFixed(0)}%` : '—'} {cpuLimit && cpuLimit !== 100 ? ` / ${cpuLimit}%` : ''}

s.cpuPercent), color: 'var(--color-accent-400)', }, ]} />

{resources ? formatBytes(resources.memoryBytes) : '—'} {memoryLimit ? ` / ${formatBytes(memoryLimit)}` : ''}

s.memoryBytes), color: '#7dd3fc', }, ]} />

↓ {formatBytes(samples?.at(-1)?.networkRxRate ?? 0)}/s ↑ {formatBytes(samples?.at(-1)?.networkTxRate ?? 0)}/s up{' '} {resources && resources.uptimeMs > 0 ? formatDuration(resources.uptimeMs / 1000) : '—'}

s.networkRxRate), color: 'var(--color-accent-400)', label: 'rx', }, { points: seriesOf(samples, (s) => s.networkTxRate), color: 'var(--color-warn-400)', fill: false, label: 'tx', }, ]} />

{diskUsed !== null ? formatBytes(diskUsed) : '—'} {diskLimit ? ` / ${formatBytes(diskLimit)}` : ''}

{diskPercent !== null && (
90 ? 'var(--color-danger-400)' : diskPercent > 75 ? 'var(--color-warn-400)' : '#a3e635', }} />

{diskPercent.toFixed(1)}% used

)}
View configuration } > {config ? : } Manage } > {installedMods.length === 0 ? (

The server runs vanilla (no mods).

) : (

{installedMods.length} mod{installedMods.length === 1 ? '' : 's'} in config.json

    {installedMods.slice(0, 5).map((mod) => (
  • {mod.name ?? mod.modId}
  • ))} {installedMods.length > 5 && (
  • + {installedMods.length - 5} more
  • )}
)}
); }