Add saves card and tidy the overview grid

Add a "Saves loaded" card (loaded vs total save size) to the overview, lay
the cards out four-per-row on desktop, and add a compact date format option
to MetricRelativeDate used for the controller uptime card.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 4.8 committed 2026-06-25 02:40:22 +00:00
1 parent 79c6bd1ac4
commit 09ec6d6ec5
2 files changed
+20 -5

No files matched your search

@@ -9,6 +9,7 @@ import { useHosts } from "../model/host";
import { useInstances } from "../model/instance";
import { useUsers, isUserOnline } from "../model/user";
import { useSystems } from "../model/system";
import { useSaves } from "../model/saves";
import ControlContext from "./ControlContext";
import PageHeader from "./PageHeader";
import PageLayout from "./PageLayout";
@@ -42,11 +43,13 @@ export default function OverviewPage() {
const [instances] = useInstances();
const [users] = useUsers();
const [systems] = useSystems();
const [saves] = useSaves();
const [grafanaUrl, setGrafanaUrl] = useState<string | null>(null);
const canHosts = account.hasPermission("core.host.list");
const canInstances = account.hasPermission("core.instance.list");
const canUsers = account.hasPermission("core.user.list");
const canSaves = account.hasAllPermission("core.instance.save.list", "core.instance.save.subscribe");
const canLogs = account.hasPermission("core.log.follow");
const canConfig = account.hasPermission("core.controller.get_config");
@@ -72,8 +75,11 @@ export default function OverviewPage() {
// Resource totals across the connected host machines (the controller is shown only for uptime).
const hostSystems = [...systems.values()].filter(system => system.id !== "controller");
const controllerSystem = systems.get("controller");
const saveList = [...saves.values()];
const loadedSaveSize = sum(saveList.filter(save => save.loaded).map(save => save.size));
const totalSaveSize = sum(saveList.map(save => save.size));
const colProps = { xs: 24, sm: 12, lg: 8 };
const colProps = { xs: 24, sm: 12, lg: 6 };
return <PageLayout nav={[{ name: "Overview" }]}>
<PageHeader
@@ -103,6 +109,10 @@ export default function OverviewPage() {
suffix={`/ ${userList.length}`} />
</Card>
</Col>}
{canSaves && <Col {...colProps}>
<ResourceCard title="Saves loaded" used={loadedSaveSize} total={totalSaveSize}
format={lib.formatBytes} />
</Col>}
{canHosts && <>
<Col {...colProps}>
<ResourceCard title="CPU" used={sum(hostSystems.map(s => s.cpuUsed))}
@@ -118,8 +128,9 @@ export default function OverviewPage() {
</Col>
<Col {...colProps}>
<Card>
<Statistic title="Controller running since"
formatter={() => <MetricRelativeDate timeMs={controllerSystem?.processStartedAtMs} />} />
<Statistic title="Controller running since" formatter={() => (
<MetricRelativeDate timeMs={controllerSystem?.processStartedAtMs} compact />
)} />
</Card>
</Col>
</>}
@@ -106,9 +106,13 @@ function humanAbsTimeDiff(date1: number, date2 = 0) {
return `${seconds}s`;
}
export function MetricRelativeDate(props: { timeMs?: number }) {
export function MetricRelativeDate(props: { timeMs?: number, compact?: boolean }) {
if (!props.timeMs || props.timeMs === 0) {
return "N/A";
}
return `${new Date(props.timeMs).toLocaleString()} (${humanAbsTimeDiff(props.timeMs, Date.now())})`;
const diff = humanAbsTimeDiff(props.timeMs, Date.now());
const absolute = props.compact
? new Date(props.timeMs).toLocaleString(undefined, { dateStyle: "short", timeStyle: "short" })
: new Date(props.timeMs).toLocaleString();
return `${absolute} (${diff})`;
}