hyperhive/frontend/packages/swarm-ui/src/App.tsx
iris 527ed8f3e2 swarm-ui: add a jobs tab, and type JobqGraph as real TypeScript
New /jobs route in swarm-ui, reusing the shared JobqGraph component
against swarm-controller's own GET /api/jobq/graph (same wire shape
hive-c0re's dashboard already consumes, different endpoint, no fork).

Converted JobqGraph.jsx to JobqGraph.tsx with real prop/wire types
(mirrors hive_jobq_wire's GraphNode/GraphDep/State by hand) instead of
a hand-maintained ambient .d.ts at the swarm-ui consumer side — the
.d.ts would duplicate the prop list and drift from the source the
moment the component's signature changes without the declaration being
touched. Both dashboard (untyped consumer, esbuild strips types) and
swarm-ui (tsc --noEmit) build/typecheck clean off the one file.
2026-08-16 17:30:06 +02:00

80 lines
2.5 KiB
TypeScript

// 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 { ComponentsPage } from './pages/ComponentsPage.js';
import { JobsPage } from './pages/JobsPage.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">
{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>
);
}
function NotFound() {
return (
<Panel title="404">
<p>no route here.</p>
</Panel>
);
}
export function App() {
return (
<Shell>
<Switch>
<Route path="/" component={Home} />
<Route path="/jobs" component={JobsPage} />
<Route path="/components" component={ComponentsPage} />
<Route component={NotFound} />
</Switch>
</Shell>
);
}