// — the swarm-level job graph. Reuses the shared JobqGraph // component (originally built for the per-hive dashboard's rebuild // queue) pointed at swarm-controller's own GET /api/jobq/graph instead // of hive-c0re's — same wire shape (Vec), // different endpoint, no component fork. swarm-controller's graph has // no real node kinds wired in yet (see swarm-controller/src/main.rs's // SwarmNodeKind/SwarmResourceKind), so this renders an empty tree today // — the page exists so the wiring is in place before the first real // swarm-level job (e.g. CreateAgent) lands. // // JobqRollup sits above the graph, same "N running / M queued" banner // the dashboard's SW4RM tab shows — no `queueHref`, since a "view // queue →" link back to this same page would be noise. // // Auto-refresh reuses `HivesPage`'s exact pattern (`RefreshIntervalPicker` // in the `Panel`'s `actions` slot, `useRefreshInterval` driving the // re-fetch), but ticks a `refreshToken` bump instead of doing its own // fetch — both `JobqGraph`/`JobqRollup` already accept that prop // (bumping it is their documented "refetch now" lever) since the // dashboard's own rebuild-queue view needed the same thing first. import { useState } from "preact/hooks"; import { JobqGraph } from "@hive/shared/jobq-graph.js"; import { JobqRollup } from "@hive/shared/jobq-rollup.js"; import { Panel } from "../ui/panel/Panel.js"; import { RefreshIntervalPicker, useRefreshInterval, type RefreshIntervalMs, } from "../ui/refresh-interval/RefreshInterval.js"; import "./JobsPage.css"; // Same 30s default + same reasoning as `HivesPage`: no inputs on this // page for a refresh to clobber, so the out-of-the-box behaviour should // just solve staleness rather than require an opt-in every visit. const DEFAULT_INTERVAL_MS: RefreshIntervalMs = 30_000; export function JobsPage() { const [intervalMs, setIntervalMs] = useState(DEFAULT_INTERVAL_MS); const [refreshToken, setRefreshToken] = useState(0); // `useRefreshInterval` ticks once immediately on mount (by design — // see its own doc comment), which lands on top of `JobqGraph`'s / // `JobqRollup`'s own unconditional mount-time fetch and costs one // harmless duplicate request on page load. Not worth a special case // in the shared hook for: the alternative (skip the first tick) would // need every other `useRefreshInterval` caller to reason about // whether it's the sole fetcher or not, for a one-page, one-time, // zero-user-visible-effect cost here. useRefreshInterval(intervalMs, () => setRefreshToken((t) => t + 1)); return ( } > ); }