feat(ui): themed confirm dialogs, mobile nav + backend safety fixes#147
Merged
Conversation
Replace native window.confirm() with a promise-based useConfirm() hook backed by the app's themed Modal (focus trap, Esc, body-scroll lock, dark-mode tokens) instead of the browser's unstyled native dialog. Converts all 11 live delete/dismiss/clear call sites: rules, integrations, notification channels, automation schedules, tags, optimization profiles (incl. the 409 force-delete), housekeeping, account "sign out other sessions", and the dashboard + rules suggestion dismissals. Delete actions get a red (danger) confirm button; the confirm button is never auto-focused so a stray Enter can't fire a destructive action. The hook degrades to the native dialog when no <ConfirmProvider> is mounted (isolated unit tests rendering a component out of the shell), so the real app always gets the themed dialog while existing tests keep passing unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Mobile navigation: add a Topbar hamburger that opens the sidebar as a slide-in drawer (dimmed tap-to-dismiss backdrop, auto-close on route change) at <=640px, where the rail is otherwise display:none and navigation depended entirely on the ⌘K palette — unreachable on a touch device with no keyboard. - Notification dropdown: give the topbar row a positioned ancestor so the dropdown anchors to the bell cluster, and clamp its width to the viewport so the 380px panel can't overflow / cause horizontal scroll on narrow screens. - DataGrid: bump the primitive's hardcoded 13px/11px body/header to the 14px/11.5px design baseline so it matches the hand-rolled tables. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… status - VirusTotal drain: when neither an explicit batch_size (manual drain) nor a configured lookups_per_minute is present, fall back to the free-tier per-minute ceiling (4) — which is already the documented schema default for lookups_per_minute — instead of 20. The old fallback silently exceeded VirusTotal's 4/min free-tier limit on configs created before the field existed, tripping a 429 and the next-midnight backoff that stalls all VT lookups for ~24h. - reap_stale_scans: emit each reaped run's real prior status (queued vs running) in the scan.reaped WS event instead of a hardcoded "queued". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #146 (UI bug fixes), landing the higher-value UX / accessibility and backend-safety improvements surfaced by the same full app audit. All changes are verified against the live backend (Vite dev proxy + Playwright) with the full unit suite + targeted backend tests green.
UX / accessibility
window.confirm()with a promise-baseduseConfirm()hook backed by the app's Modal (focus trap, Esc, body-scroll lock, dark-mode tokens) across all 11 destructive call sites: rules, integrations, notification channels, automation schedules, tags, optimization profiles (incl. the 409 force-delete), housekeeping, account "sign out other sessions", and the dashboard + rules suggestion dismissals. Delete actions get a red (danger) confirm button, and it's never auto-focused so a stray Enter can't fire a destructive action. The hook degrades to the native dialog when no<ConfirmProvider>is mounted (isolated unit tests), so existing tests keep passing unchanged.ConfirmDialog.tsx,AppProviders.tsx(+ 11 sites)display:noneand navigation depended entirely on the ⌘K palette, unreachable on a touch device. Added a Topbar hamburger that opens the sidebar as a slide-in drawer with a dimmed tap-to-dismiss backdrop and auto-close on navigation.AppShell.tsx,Sidebar.tsx,Topbar.tsx,shell.cssshell.cssDataGrid.tsxBackend hardening
app/automation/jobs.py) — the per-minute drain cap fell back to 20 whenlookups_per_minutewas unset (e.g. configs predating the field), silently exceeding VirusTotal's 4/min free-tier limit → 429 → ~24h backoff. Now falls back to the free-tier ceiling (4), which is already the documented schema default.app/worker.py) —reap_stale_scansnow emits each run's real prior status (queued vs running) in thescan.reapedWS event instead of a hardcoded"queued".Verification
tsc --noEmitclean ·eslint . --max-warnings=0clean ·vitest run552 passed / 3 skippedtest_virustotal_drain/test_virustotal_quota/test_scan_reaper/test_virustotal_stage19— 34 passed; changed modules import-smoke clean.confirmgone)Reviewer checklist
cd frontend && npm ci && npm run typecheck && npm run lint && npm testNot included (deferred follow-ups)
sortKey: the cursor compares byidbut sorts bydate; switching tosortKey=idneeds validation against a live Sonarr/Radarr before merging.retriable_statesincludes"completed", but the UI never offers retry on completed items, so there's no user-facing bug; left as-is to avoid surprising direct API callers.🤖 Generated with Claude Code