Source: codebase audit, 2026-06-11 (audit finding F2, Tier 1)
Severity: Medium
Category: Accessibility + UX consistency
Problem
Six destructive operator flows use native window.confirm(). The browser dialog:
- has no focus trap (the page underneath remains tab-reachable)
- has no visual consistency with the rest of the admin shell (no Tailwind tokens, no Alert/Card palette)
- has no aria-modal / role="dialog"
- doesn't return focus to the trigger button on close
Sites:
Suggested approach
Add frontend/src/ui/ConfirmDialog.tsx:
```tsx
type Props = {
open: boolean;
title: string;
body: ReactNode;
confirmLabel: string;
cancelLabel?: string;
tone?: 'danger' | 'default';
onConfirm: () => void;
onCancel: () => void;
};
```
Required behavior:
role="dialog", aria-modal="true", aria-labelledby on the title
- Focus moves to the confirm button on open; returns to the trigger on close
- Escape → cancel; Enter → confirm
- Click outside the dialog body → cancel
- Body scroll lock while open
- Matches the existing Alert/Card Tailwind palette (use
danger-bg / danger tokens for tone='danger')
Colocated test: open/close, Escape cancels, Enter confirms, Tab cycles within dialog, focus returns on close.
Test migration
Each rewired view's existing test that spies on window.confirm swaps to asserting ConfirmDialog renders and clicking the confirm button completes the action.
Source: codebase audit, 2026-06-11 (audit finding F2, Tier 1)
Severity: Medium
Category: Accessibility + UX consistency
Problem
Six destructive operator flows use native
window.confirm(). The browser dialog:Sites:
Suggested approach
Add
frontend/src/ui/ConfirmDialog.tsx:```tsx
type Props = {
open: boolean;
title: string;
body: ReactNode;
confirmLabel: string;
cancelLabel?: string;
tone?: 'danger' | 'default';
onConfirm: () => void;
onCancel: () => void;
};
```
Required behavior:
role="dialog",aria-modal="true",aria-labelledbyon the titledanger-bg/dangertokens fortone='danger')Colocated test: open/close, Escape cancels, Enter confirms, Tab cycles within dialog, focus returns on close.
Test migration
Each rewired view's existing test that spies on
window.confirmswaps to assertingConfirmDialogrenders and clicking the confirm button completes the action.