admin: live query detail becomes a modal, live/history and period pickers become tabs and radios, client delete confirms in a dialog

the streamed-query detail panel is now a rac modal dialog with focus containment and restore. the live/history switch is rac tabs driven by the url, the overview period picker is a rac radio group, and the clients delete flow uses the shared confirm dialog; an authority turn keeps the dialog open and withdraws only the destructive action.
This commit is contained in:
2026-08-29 11:55:24 +02:00
parent 79578e1d2a
commit 317d5dd4f8
11 changed files with 709 additions and 375 deletions
@@ -189,7 +189,7 @@ test("a request in flight leaves the heading and the picker usable behind one lo
renderApp();
await screen.findByRole("heading", { name: "Overview", level: 1 });
expect(screen.getByRole("button", { name: "1h" })).toBeTruthy();
expect(screen.getByRole("radio", { name: "1h" })).toBeTruthy();
// One loading state for the whole page, not one per panel.
const loading = await screen.findByText("Loading…");
expect(loading.getAttribute("role")).toBe("status");
@@ -335,14 +335,23 @@ test("an empty window says so in every panel instead of drawing nothing", async
test("a deep link opens on the period it names", async () => {
renderApp("/overview?period=1h");
await screen.findByText("12");
expect(screen.getByRole("button", { name: "1h" }).getAttribute("aria-pressed")).toBe("true");
expect(screen.getByRole("button", { name: "24h" }).getAttribute("aria-pressed")).toBe("false");
// One radio group named Period, holding the four periods and exactly one
// selection: the segmented picker is a single choice, not four toggles.
const picker = within(screen.getByRole("radiogroup", { name: "Period" }));
expect(picker.getAllByRole("radio").map((radio) => radio.getAttribute("value"))).toEqual([
"1h",
"24h",
"7d",
"30d",
]);
expect(picker.getByRole("radio", { name: "1h", checked: true })).toBeTruthy();
expect(picker.getByRole("radio", { name: "24h", checked: false })).toBeTruthy();
});
test("a period the API does not have falls back to the default without carrying it in the url", async () => {
const router = renderApp("/overview?period=90d");
await screen.findByText("1,000");
expect(screen.getByRole("button", { name: "24h" }).getAttribute("aria-pressed")).toBe("true");
expect(screen.getByRole("radio", { name: "24h", checked: true })).toBeTruthy();
expect(router.state.location.search).toEqual({});
});
@@ -350,7 +359,7 @@ test("the picker rescopes every panel and writes the period into the url", async
const router = renderApp();
await screen.findByText("1,000");
fireEvent.click(screen.getByRole("button", { name: "1h" }));
fireEvent.click(screen.getByRole("radio", { name: "1h" }));
await screen.findByText("12");
await waitFor(() => expect(router.state.location.search).toEqual({ period: "1h" }));
@@ -369,7 +378,7 @@ test("a failed request is one error for the whole page, stated once and retryabl
expect(screen.getAllByRole("button", { name: "Retry" })).toHaveLength(1);
// The heading and the picker survive it, so the reader can rescope or retry.
expect(screen.getByRole("heading", { name: "Overview", level: 1 })).toBeTruthy();
expect(screen.getByRole("button", { name: "1h" })).toBeTruthy();
expect(screen.getByRole("radio", { name: "1h" })).toBeTruthy();
expect(screen.queryByText("Something went wrong")).toBeNull();
failing = false;