milestone 29: activity — history, live and policy simulation on one surface
Gates / test (push) Successful in 1m40s
Gates / package (push) Successful in 3m58s
Gates / container (push) Successful in 14s
CI / gates (push) Successful in 12m51s
Gates / frontend (push) Successful in 1m18s
Gates / test-aarch64 (push) Successful in 6m57s
Gates / test (push) Successful in 1m40s
Gates / package (push) Successful in 3m58s
Gates / container (push) Successful in 14s
CI / gates (push) Successful in 12m51s
Gates / frontend (push) Successful in 1m18s
Gates / test-aarch64 (push) Successful in 6m57s
query log, live and lookup merge into /activity. history filters live in the url, so a pasted link or back/forward reproduces the exact view; the result column separates servfail and nxdomain from success in the list. live is follow-by-default with freeze, and a streamed row opens its in-memory provenance detail — no correlation invented for rows sqlite has not written. lookup survives as the current policy simulation under /activity/test. investigation links carry absolute bounds, and the diagnostics page now honors since/until instead of ignoring them. the old routes are gone without aliases.
This commit is contained in:
@@ -12,18 +12,13 @@ import * as api from "@/lib/api";
|
||||
import InlineError from "@/lib/InlineError";
|
||||
import { formatDuration, formatTime } from "@/lib/format";
|
||||
import { diagnosticPurgeMutation, diagnosticsInfiniteQuery, diagnosticsPurgeResolvedMutation } from "@/lib/queries";
|
||||
import type {
|
||||
DiagnosticEvent,
|
||||
DiagnosticSeverity,
|
||||
DiagnosticState,
|
||||
DiagnosticsFilter,
|
||||
DiagnosticsPage as Page,
|
||||
} from "@/lib/types";
|
||||
import type { DiagnosticEvent, DiagnosticSeverity, DiagnosticState, DiagnosticsPage as Page } from "@/lib/types";
|
||||
import ConfirmDialog from "@/ui/ConfirmDialog";
|
||||
import Select from "@/ui/Select";
|
||||
import { styles as shared } from "@/ui/styles";
|
||||
import { colors } from "@/ui/tokens.stylex";
|
||||
import SeverityBadge from "./SeverityBadge";
|
||||
import { diagnosticsFilterOf } from "./filter";
|
||||
import { DIAGNOSTIC_COMPONENTS, componentLabel, copyFor } from "./eventCopy";
|
||||
|
||||
const DARK = "@media (prefers-color-scheme: dark)";
|
||||
@@ -138,6 +133,19 @@ const styles = stylex.create({
|
||||
lineHeight: "1rem",
|
||||
color: colors.textMuted,
|
||||
},
|
||||
rangeNotice: {
|
||||
marginTop: "0.75rem",
|
||||
borderRadius: "0.25rem",
|
||||
borderWidth: 1,
|
||||
borderStyle: "solid",
|
||||
borderColor: colors.border,
|
||||
backgroundColor: colors.surfaceHover,
|
||||
paddingInline: "0.75rem",
|
||||
paddingBlock: "0.5rem",
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: "1.25rem",
|
||||
color: colors.textSecondary,
|
||||
},
|
||||
tableWrap: {
|
||||
marginTop: "0.75rem",
|
||||
overflowX: "auto",
|
||||
@@ -263,6 +271,33 @@ function MoreButton({ section }: { section: Section }) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The window the page is bounded to, whenever it is bounded.
|
||||
*
|
||||
* A link from a query detail arrives with an absolute five-minute window, and
|
||||
* an empty Active section inside it means something very different from an
|
||||
* empty Active section over the whole history. The page has to say which it is
|
||||
* showing, and offer the way out of it.
|
||||
*/
|
||||
function RangeNotice({ since, until }: { since?: number; until?: number }) {
|
||||
const navigate = useNavigate({ from: "/diagnostics" });
|
||||
if (since === undefined && until === undefined) return null;
|
||||
const from = since === undefined ? "the start of the history" : formatTime(since);
|
||||
const to = until === undefined ? "now" : formatTime(until);
|
||||
return (
|
||||
<p role="status" {...stylex.props(styles.rangeNotice)}>
|
||||
Showing events that overlap {from} to {to}.{" "}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void navigate({ search: (prev) => ({ ...prev, since: undefined, until: undefined }) })}
|
||||
{...stylex.props(shared.linkButton, shared.focusRing)}
|
||||
>
|
||||
Clear the time range
|
||||
</button>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
function ActiveCard({ event, now }: { event: DiagnosticEvent; now: number }) {
|
||||
const copy = copyFor(event.code);
|
||||
return (
|
||||
@@ -327,9 +362,7 @@ export default function DiagnosticsPage() {
|
||||
const navigate = useNavigate({ from: "/diagnostics" });
|
||||
const state = search.state ?? "all";
|
||||
|
||||
const base: DiagnosticsFilter = {};
|
||||
if (search.severity !== undefined) base.severity = search.severity;
|
||||
if (search.component !== undefined) base.component = search.component;
|
||||
const base = diagnosticsFilterOf(search);
|
||||
|
||||
const active = useInfiniteQuery(diagnosticsInfiniteQuery({ ...base, state: "active" }, state !== "resolved"));
|
||||
const history = useInfiniteQuery(diagnosticsInfiniteQuery({ ...base, state: "resolved" }, state !== "active"));
|
||||
@@ -368,6 +401,8 @@ export default function DiagnosticsPage() {
|
||||
repeats, and closes when the subject recovers.
|
||||
</p>
|
||||
|
||||
<RangeNotice since={search.since} until={search.until} />
|
||||
|
||||
<div {...stylex.props(styles.filterGrid)}>
|
||||
<Select
|
||||
variant="compactField"
|
||||
|
||||
Reference in New Issue
Block a user