Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/src/pages/JobsPage.tsx
iris a2f000bab4 swarm-ui: builds are a card under the job graph on the jobs tab
The builds tab is gone from the nav; its card sits below the jobs card on
/jobs. The swarm-builder commit status now links to /jobs?run=<id>, and the old
/builds address redirects there with its query so links already on PRs still
open the build. Docs and the link test follow.
2026-10-26 20:02:57 +01:00

80 lines
3.3 KiB
TypeScript

// <JobsPage> — 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<hive_jobq_wire::GraphNode>),
// different endpoint, no component fork. swarm-controller's graph has
// no real node kinds wired in yet (see swarm-controller/src/jobq.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 4G3NTS tab shows — no `queueHref`, since a "view
// queue →" link back to this same page would be noise.
//
// Auto-refresh reuses `HivesCard`'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 { BuildsCard } from "./builds/BuildsPage.js";
import {
RefreshIntervalPicker,
useRefreshInterval,
type RefreshIntervalMs,
} from "../ui/refresh-interval/RefreshInterval.js";
import "./JobsPage.css";
// Same 30s default + same reasoning as `HivesCard`: 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;
function JobsCard() {
const [intervalMs, setIntervalMs] =
useState<RefreshIntervalMs>(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 (
<Panel
title="jobs"
icon="🧩"
actions={
<RefreshIntervalPicker
id="jobs-refresh"
value={intervalMs}
onChange={setIntervalMs}
/>
}
>
<JobqRollup endpoint="/api/jobq/rollup" refreshToken={refreshToken} />
<JobqGraph endpoint="/api/jobq/graph" refreshToken={refreshToken} />
</Panel>
);
}
// The jobs tab holds two cards: the controller's job graph, and the swarm
// builder's recent builds under it.
export function JobsPage() {
return (
<div class="ui-jobs-page">
<JobsCard />
<BuildsCard />
</div>
);
}