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
196 lines
5.9 KiB
TypeScript
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()} />
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
}
|