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.
80 lines
2.5 KiB
TypeScript
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>
|
|
);
|
|
}
|