Files
nxdns/admin/src/features/diagnostics/HealthStrip.tsx
T
mokhtar 7e0df5fd94
Gates / frontend (push) Successful in 1m22s
Gates / test (push) Successful in 1m54s
Gates / test-aarch64 (push) Successful in 7m57s
Gates / package (push) Successful in 5m29s
Gates / container (push) Successful in 10s
CI / gates (push) Successful in 32m51s
milestone 32: task-shaped configuration, file mode as a rendering, config status api
2026-08-22 22:42:50 +02:00

196 lines
5.9 KiB
TypeScript

/**
* The five health conditions, compactly, at the top of the page that explains
* failures. Healthy conditions stay quiet; a degraded one is highlighted and
* offers the way out.
*
* A degraded condition whose explanation is on this page narrows this page
* rather than navigating away: the filter link sets `component` and drops every
* other filter. A time window, a severity or a `state=resolved` left from an
* earlier investigation would each hide the very episode the reader was sent to
* read, and a link that lands on "no events" states something false.
*
* The load contract is the one the deleted Overview status section carried: a
* visible loading state before the first reading, and a refetch failure that
* says so — the conditions on screen become the last reading that arrived, never
* a claim about the current state, until a poll succeeds again.
*/
import { useQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import * as stylex from "@stylexjs/stylex";
import InlineError from "@/lib/InlineError";
import { healthQuery } from "@/lib/queries";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";
import { healthFacts, type FactLink, type FactTone, type HealthFact } from "./healthFacts";
const DARK = "@media (prefers-color-scheme: dark)";
const styles = stylex.create({
list: {
marginTop: "0.75rem",
display: "grid",
gap: "0.5rem",
// Five is prime, so every count between one and five leaves a short last
// row; three columns made it 3 then 2, which reads as a layout that ran out
// of room rather than one that chose. So the strip goes from one column
// straight to two and then to a single row of five, and is never ragged.
gridTemplateColumns: {
default: "minmax(0, 1fr)",
"@media (min-width: 640px)": "repeat(2, minmax(0, 1fr))",
"@media (min-width: 1100px)": "repeat(5, minmax(0, 1fr))",
},
listStyleType: "none",
padding: 0,
},
fact: {
display: "flex",
flexWrap: "wrap",
alignItems: "baseline",
gap: "0.375rem",
borderRadius: "0.25rem",
borderWidth: 1,
borderStyle: "solid",
paddingInline: "0.625rem",
paddingBlock: "0.5rem",
fontSize: "0.875rem",
lineHeight: "1.25rem",
},
/** Quiet: a healthy condition is the normal state and gets no emphasis. */
quiet: {
borderColor: colors.border,
backgroundColor: "transparent",
},
highlighted: {
borderColor: colors.borderStrong,
backgroundColor: colors.surfaceRaised,
},
label: {
color: colors.textSecondary,
},
value: {
fontWeight: 500,
},
detail: {
flexBasis: "100%",
fontSize: "0.75rem",
lineHeight: "1rem",
color: colors.textMuted,
},
link: {
fontSize: "0.875rem",
lineHeight: "1.25rem",
color: colors.primaryOnSurface,
textDecorationLine: "none",
},
ok: {
color: { default: "oklch(43.2% 0.095 166.913)", [DARK]: "oklch(84.5% 0.143 164.978)" },
},
notice: {
color: { default: "oklch(47.3% 0.137 46.201)", [DARK]: "oklch(87.9% 0.169 91.605)" },
},
warn: {
color: { default: "oklch(47.3% 0.137 46.201)", [DARK]: "oklch(87.9% 0.169 91.605)" },
},
danger: {
color: colors.dangerText,
},
message: {
marginTop: "0.5rem",
fontSize: "0.875rem",
lineHeight: "1.25rem",
color: colors.textMuted,
},
});
const TONES = { ok: styles.ok, notice: styles.notice, warn: styles.warn, danger: styles.danger } as const;
/** Text and icon carry the state; the colour only agrees with them. */
const ICONS: Record<FactTone, string> = { ok: "●", notice: "‖", warn: "!", danger: "✕" };
function FactLinkAnchor({ link }: { link: FactLink }) {
if (link.kind === "filter") {
return (
<Link
to="/diagnostics"
// Sets the component and clears every filter that could hide what it
// points at: a time window from an older investigation, and a severity
// or state — `resolved` above all — that would exclude the very episode
// explaining the condition this link came from.
search={() => ({ component: link.component })}
{...stylex.props(styles.link, shared.focusRing)}
>
{link.label}
</Link>
);
}
// The tab belongs to the destination, not to the fact: a source that stopped
// loading is read on Protection's Sources tab, an upstream on Resolution's.
if (link.to === "/configuration/protection") {
return (
<Link
to="/configuration/protection"
search={{ tab: "sources" }}
{...stylex.props(styles.link, shared.focusRing)}
>
{link.label}
</Link>
);
}
return (
<Link
to="/configuration/resolution"
search={{ tab: "upstreams" }}
{...stylex.props(styles.link, shared.focusRing)}
>
{link.label}
</Link>
);
}
function Fact({ fact }: { fact: HealthFact }) {
return (
<li {...stylex.props(styles.fact, fact.tone === "ok" ? styles.quiet : styles.highlighted)}>
<span aria-hidden="true" {...stylex.props(TONES[fact.tone])}>
{ICONS[fact.tone]}
</span>
<span {...stylex.props(styles.label)}>{fact.label}</span>
<span {...stylex.props(styles.value, TONES[fact.tone])}>{fact.value}</span>
{fact.link !== undefined && <FactLinkAnchor link={fact.link} />}
{fact.detail !== undefined && <span {...stylex.props(styles.detail)}>{fact.detail}</span>}
</li>
);
}
export default function HealthStrip() {
const health = useQuery(healthQuery());
if (health.data === undefined) {
return health.isError ? (
<InlineError error={health.error} onRetry={() => void health.refetch()} />
) : (
<p role="status" {...stylex.props(styles.message, shared.pulse)}>
Loading status
</p>
);
}
return (
<>
<ul aria-label="Current status" {...stylex.props(styles.list)}>
{healthFacts(health.data).map((fact) => (
<Fact key={fact.key} fact={fact} />
))}
</ul>
{health.isError && (
<>
<p role="status" {...stylex.props(styles.message)}>
This is the last reading that arrived. The current state is unknown.
</p>
<InlineError error={health.error} onRetry={() => void health.refetch()} />
</>
)}
</>
);
}