swarm-ui: real hive-roster overview page
Fixes hyperhive#3223. swarm-controller: GET /api/hives (utoipa-annotated same as /health), serving the swarm's hive directory (name + domain) loaded once at startup from a new SWARM_CONTROLLER_HIVES env var. The controller's NixOS module sets it from services.hyperhive.swarm.hives, JSON-encoded the same way hive-c0re already builds HYPERHIVE_PEERS for its own peer list (environment.nix) — the full directory here rather than peers-minus-self, since a swarm-level daemon has no 'self' hive to exclude. Unset/malformed both fall back to an empty list with a warning rather than failing startup, so /health stays answerable even if this one env var is wrong. swarm-ui: App.tsx's Home route fetches /api/hives and renders it through the already-merged <Table>/<StatusChip>/<Panel> primitives — name, domain (linking out to that hive's own gateway-routed dashboard), and a static "configured" status chip until a real online/stale/offline rollup exists server-side. Also gave swarm-ui a base <a> color (theme's --blue) — base.css covers body/typography but not links, and this is genuinely page-level rather than any one component's concern. Verified end to end, not just source-reading: ran the real swarm-controller binary with SWARM_CONTROLLER_HIVES set, curled /api/hives + /health over its actual unix socket; separately served the real swarm-ui dist against a mock /api/hives and screenshotted the rendered table. Also re-verified the nginx wiring evaluates (same throwaway nixosSystem eval technique as #3212) — SWARM_CONTROLLER_HIVES resolves to the expected JSON shape. cargo test/clippy -p swarm-controller clean (2 tests, including a new load_hives one covering missing/malformed/valid env var states). npm run build + typecheck clean.
This commit is contained in:
parent
5300c242f3
commit
e7f4a19939
6 changed files with 208 additions and 22 deletions
|
|
@ -1,15 +1,57 @@
|
|||
// Root shell component. Real route (`/`) is still a placeholder — the
|
||||
// swarm's hive roster is the next piece of work to land into it — but
|
||||
// it now mounts inside <Shell> and uses the ui/ primitives, so that
|
||||
// page lands as a content change, not a structural one.
|
||||
// Root shell component. `/` is now the real hive-roster overview page —
|
||||
// fetches swarm-controller's `GET /api/hives` and renders it through the
|
||||
// shared primitives. Status starts as a static "configured" chip; grows
|
||||
// into a real online/stale/offline tone once a status rollup exists
|
||||
// server-side — same component, richer data later, no rebuild.
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { Route, Switch } from 'wouter-preact';
|
||||
import { Shell } from './shell/Shell.js';
|
||||
import { Panel } from './ui/panel/Panel.js';
|
||||
import { StatusChip } from './ui/status-chip/StatusChip.js';
|
||||
import { Table, type TableColumn } from './ui/table/Table.js';
|
||||
|
||||
interface Hive {
|
||||
name: string;
|
||||
domain: string;
|
||||
}
|
||||
|
||||
const COLUMNS: TableColumn<Hive>[] = [
|
||||
{ key: 'name', header: 'name', render: (h) => h.name },
|
||||
{
|
||||
key: 'domain',
|
||||
header: 'domain',
|
||||
render: (h) => (
|
||||
<a href={`https://${h.domain}/`} target="_blank" rel="noreferrer">
|
||||
{h.domain}
|
||||
</a>
|
||||
),
|
||||
},
|
||||
// Static "configured" tone until a swarm-wide status rollup exists —
|
||||
// there is no data source for online/stale/offline yet, and a chip
|
||||
// that always renders "positive" would misreport a genuinely offline
|
||||
// hive. See StatusChip's own doc comment for the same reasoning.
|
||||
{ key: 'status', header: 'status', render: () => <StatusChip label="configured" /> },
|
||||
];
|
||||
|
||||
function Home() {
|
||||
const [hives, setHives] = useState<Hive[] | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/hives')
|
||||
.then((r) => {
|
||||
if (!r.ok) throw new Error(`http ${r.status}`);
|
||||
return r.json() as Promise<Hive[]>;
|
||||
})
|
||||
.then(setHives)
|
||||
.catch((e: unknown) => setError(String(e)));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Panel title="overview">
|
||||
<p>hive roster lands here, sequenced after this shell.</p>
|
||||
{error ? <p>failed to load the hive roster: {error}</p> : null}
|
||||
{!error && hives === null ? <p>loading…</p> : null}
|
||||
{hives ? <Table columns={COLUMNS} rows={hives} rowKey={(h) => h.name} /> : null}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,3 +5,10 @@
|
|||
every page needs regardless of which components a route happens to
|
||||
use. */
|
||||
@import "@hive/shared/base.css";
|
||||
|
||||
/* base.css covers body/typography but not links — every page here links
|
||||
out (hive-roster rows to each hive's own dashboard, nav, footer…), so
|
||||
this is genuinely page-level, not any one component's concern. */
|
||||
a {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue