swarm-ui: rename OverviewPage/'overview' to HivesPage/'hives'

Closes #3460.

Renamed for accuracy — the page/nav item is the hive roster, not a
general dashboard overview. Nav label, page title, component name,
and file all renamed together so the internal name doesn't drift from
what's displayed (a component still called OverviewPage under a
'hives' nav label would be exactly the kind of stale prior-art that
makes the next contributor search harder, not less).
This commit is contained in:
iris 2026-08-18 21:26:01 +02:00 committed by mara
commit 537d6f9dac
4 changed files with 7 additions and 7 deletions

View file

@ -1,85 +0,0 @@
// <OverviewPage> — the hive-roster overview, `/`'s content. Fetches
// swarm-controller's `GET /api/hives/status`, the swarm-wide status
// aggregate: one row per roster hive, freshness derived at read time
// from the status bucket. Supersedes the earlier `GET /api/hives` +
// static "configured" chip placeholder now that the real rollup
// exists — same component, richer data, no rebuild, exactly the plan
// that placeholder's comment laid out. Its own component/file rather
// than living in `App.tsx`, matching `JobsPage`'s shape: `App.tsx` is
// routing, a page owns its own fetch + render.
import { useEffect, useState } from 'preact/hooks';
import { ApiErrorPanel } from '@hive/shared/api-error-panel.js';
import { readApiError, type ProblemDetails } from '@hive/shared/api-error.js';
import { Panel } from '../ui/panel/Panel.js';
import { StatusChip, type ChipTone } from '../ui/status-chip/StatusChip.js';
import { Table, type TableColumn } from '../ui/table/Table.js';
import { fmtAgo } from '../util.js';
type Freshness = 'fresh' | 'stale' | 'never_reported' | 'unknown';
interface HiveStatus {
name: string;
domain: string | null;
freshness: Freshness;
last_seen_unix: number | null;
age_seconds: number | null;
}
// One tone + label per freshness value. `unknown` (reporting but absent
// from the roster) reads `negative` — it's the one case this endpoint
// cannot vouch for at all, per `status.rs`'s doc comment.
const FRESHNESS: Record<Freshness, { tone: ChipTone; label: string }> = {
fresh: { tone: 'positive', label: 'fresh' },
stale: { tone: 'warning', label: 'stale' },
never_reported: { tone: 'neutral', label: 'never reported' },
unknown: { tone: 'negative', label: 'unknown' },
};
const COLUMNS: TableColumn<HiveStatus>[] = [
{ key: 'name', header: 'name', render: (h) => h.name },
{
key: 'domain',
header: 'domain',
render: (h) =>
h.domain ? (
<a href={`https://${h.domain}/`} target="_blank" rel="noreferrer">
{h.domain}
</a>
) : (
'—'
),
},
{
key: 'status',
header: 'status',
render: (h) => {
const { tone, label } = FRESHNESS[h.freshness];
const age = h.age_seconds !== null ? ` (${fmtAgo(h.age_seconds)})` : '';
return <StatusChip tone={tone} label={label + age} />;
},
},
];
export function OverviewPage() {
const [hives, setHives] = useState<HiveStatus[] | null>(null);
const [error, setError] = useState<ProblemDetails | null>(null);
useEffect(() => {
(async () => {
const r = await fetch('/api/hives/status');
if (!r.ok) {
setError(await readApiError(r));
return;
}
setHives((await r.json()) as HiveStatus[]);
})().catch((e: unknown) => setError({ detail: String(e) }));
}, []);
return (
<Panel title="overview">
{error ? <ApiErrorPanel context="failed to load the hive roster" problem={error} /> : null}
{!error && hives === null ? <p>loading</p> : null}
{hives ? <Table columns={COLUMNS} rows={hives} rowKey={(h) => h.name} /> : null}
</Panel>
);
}