swarm-ui: the job graph shows placeholder rows in the shape of a job tree while it loads
JobqGraph takes an optional loading element, so the dashboard keeps its text and the swarm UI's jobs tab passes a placeholder built from the graph's own node and row classes: a root with two children and a second root, with the real indent and rail.
This commit is contained in:
parent
f276ee342c
commit
bb789e4603
2 changed files with 48 additions and 3 deletions
|
|
@ -28,6 +28,7 @@
|
|||
// loader is `text` (for unrelated shadow-DOM components' CSS-as-string
|
||||
// needs), and that loader is global per call, not per-module.
|
||||
|
||||
import type { ComponentChildren } from "preact";
|
||||
import { useState, useEffect, useCallback, useRef } from "preact/hooks";
|
||||
|
||||
// Mirrors `hive_jobq_wire::StateSchema` verbatim (variant names, no
|
||||
|
|
@ -283,6 +284,9 @@ export interface JobqGraphProps {
|
|||
onUpdate?: (nodes: GraphNode[]) => void;
|
||||
onCancel?: (id: number) => void;
|
||||
refreshToken?: number;
|
||||
/** What shows while the first answer loads. A host that has a placeholder in
|
||||
* the shape of its graph passes it; the default is a line of text. */
|
||||
loading?: ComponentChildren;
|
||||
}
|
||||
|
||||
// `refreshToken` is not read anywhere in the body — its only job is to
|
||||
|
|
@ -295,6 +299,7 @@ export function JobqGraph({
|
|||
onUpdate,
|
||||
onCancel,
|
||||
refreshToken = 0,
|
||||
loading,
|
||||
}: JobqGraphProps) {
|
||||
const [selectedStates, setSelectedStates] = useState<Set<NodeState>>(
|
||||
() => new Set(ALL_STATES.filter((s) => !DEFAULT_HIDDEN_STATES.has(s))),
|
||||
|
|
@ -344,7 +349,7 @@ export function JobqGraph({
|
|||
{error ? (
|
||||
<p class="jg-error-msg">fetch failed: {error}</p>
|
||||
) : nodes === null ? (
|
||||
<p class="jg-empty">loading…</p>
|
||||
(loading ?? <p class="jg-empty">loading…</p>)
|
||||
) : !nodes.length ? (
|
||||
<p class="jg-empty">empty</p>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -18,10 +18,12 @@
|
|||
// 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 type { ComponentChildren } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { JobqGraph } from "@hive/shared/jobq-graph.js";
|
||||
import { JobqRollup } from "@hive/shared/jobq-rollup.js";
|
||||
import { LazyDisclosure } from "../ui/disclosure/LazyDisclosure.js";
|
||||
import { Skeleton, SkeletonRegion } from "../ui/skeleton/Skeleton.js";
|
||||
import { Toolbar } from "../ui/toolbar/Toolbar.js";
|
||||
import { BuildsCard } from "./builds/BuildsPage.js";
|
||||
import { ReviewPage } from "./review/ReviewPage.js";
|
||||
|
|
@ -37,6 +39,39 @@ import "./JobsPage.css";
|
|||
// just solve staleness rather than require an opt-in every visit.
|
||||
const DEFAULT_INTERVAL_MS: RefreshIntervalMs = 30_000;
|
||||
|
||||
// The graph's twin while it loads: the graph's own node and row classes, so the
|
||||
// rows and the indent of the children are the real ones, with a state glyph and a
|
||||
// label as placeholders. Two roots, the first with two children.
|
||||
function SkeletonNode({
|
||||
label,
|
||||
children,
|
||||
}: {
|
||||
label: string;
|
||||
children?: ComponentChildren;
|
||||
}) {
|
||||
return (
|
||||
<div class="jg-node">
|
||||
<div class="jg-row">
|
||||
<Skeleton shape="circle" width="1em" height="1em" />
|
||||
<Skeleton width={label} />
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function JobsGraphSkeleton() {
|
||||
return (
|
||||
<SkeletonRegion label="the jobs">
|
||||
<SkeletonNode label="14em">
|
||||
<SkeletonNode label="10em" />
|
||||
<SkeletonNode label="12em" />
|
||||
</SkeletonNode>
|
||||
<SkeletonNode label="9em" />
|
||||
</SkeletonRegion>
|
||||
);
|
||||
}
|
||||
|
||||
function JobsBody() {
|
||||
const [intervalMs, setIntervalMs] =
|
||||
useState<RefreshIntervalMs>(DEFAULT_INTERVAL_MS);
|
||||
|
|
@ -62,7 +97,11 @@ function JobsBody() {
|
|||
/>
|
||||
</Toolbar>
|
||||
<JobqRollup endpoint="/api/jobq/rollup" refreshToken={refreshToken} />
|
||||
<JobqGraph endpoint="/api/jobq/graph" refreshToken={refreshToken} />
|
||||
<JobqGraph
|
||||
endpoint="/api/jobq/graph"
|
||||
refreshToken={refreshToken}
|
||||
loading={<JobsGraphSkeleton />}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue