Skip to content

feat(web): confirmation flow for destructive actions#120

Merged
CoderCoco merged 14 commits into
mainfrom
claude/issue-67-confirmation-flow-destructive-actions
May 6, 2026
Merged

feat(web): confirmation flow for destructive actions#120
CoderCoco merged 14 commits into
mainfrom
claude/issue-67-confirmation-flow-destructive-actions

Conversation

@CoderCoco
Copy link
Copy Markdown
Owner

Closes #67

Summary

  • New ConfirmDialog component wrapping shadcn AlertDialog with two optional modes: type-to-confirm input (blocks Confirm until text matches) and "don't ask again" session checkbox
  • New lib/confirm-skip.ts session store: isSuppressed(key) / suppress(key) backed by a module-level Set — resets on hard reload, persists across SPA navigation
  • Stop server (GameCard): simple confirm with "Don't ask again for this session"; suppressed path fires api.stop() directly
  • Remove allowed guild (GuildsSection): type-to-confirm — user must type the guild ID exactly to enable Confirm; no "don't ask again"
  • Clear per-game permissions (PermissionRow): simple confirm with "don't ask again"
  • Remove admin user/role ID (AdminsSection): simple confirm with "don't ask again"; added onRemoveChip prop to SnowflakeChipsInput so Backspace-to-pop also routes through confirmation
  • 20 new tests across ConfirmDialog.test.tsx, GameCard.test.tsx, and DiscordPage.test.tsx; 335 total passing

Implementation notes

  • The "don't ask again" state is intentionally in-memory (module-level Set) — not localStorage — so it resets on hard reload per the spec. SPA navigation leaves the module loaded, so suppression persists across routes.
  • ESC, focus-trap, and prefers-reduced-motion are handled automatically by the Radix AlertDialog primitive — no custom code needed for AC#4.
  • Admin removal confirms before local-state update, not before the API call (the API call is deferred to "Save admins" per the existing AdminsSection draft-editor design). The dialog description reflects this.
  • Merge note: if issue-114 (file naming convention) lands after this PR, the new files (confirm-skip.ts, ConfirmDialog.tsx, GameCard.test.tsx) will need to be renamed to the kebab-case + type-suffix convention.

Test plan

  • npm run app:test — 335 tests pass
  • npm run app:lint — exits clean
  • Manual: click Stop on a running server → dialog appears; check "Don't ask again" and confirm → subsequent clicks skip dialog until page reload
  • Manual: Remove an allowed guild → Confirm disabled until guild ID is typed exactly; confirm → guild removed
  • Manual: Clear per-game permissions → confirm → permissions cleared; cancel → no change
  • Manual: X a chip in Admin User/Role IDs → dialog appears; confirm → chip removed (API call deferred to Save admins)

Generated with Claude Code

CoderCoco and others added 11 commits May 5, 2026 21:00
…on skip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove redundant setStopDialogOpen(false) from onConfirm (Radix AlertDialogAction already closes the dialog on click); add GameCard.test.tsx covering all four Stop confirmation branches.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 6, 2026 03:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a reusable confirmation-dialog flow for destructive actions in the web UI, including optional “type to confirm” and per-session “don’t ask again” suppression, and wires it into multiple existing destructive actions.

Changes:

  • Added a shared ConfirmDialog wrapper around shadcn/Radix AlertDialog (optional type-to-confirm + optional “don’t ask again this session”).
  • Added an in-memory session suppression store (confirm-skip.ts) and integrated suppression checks for selected actions.
  • Updated Discord and Dashboard-related UI flows (Stop server, remove guild, clear permissions, remove admin chips) and added/expanded tests.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
app/packages/web/src/pages/DiscordPage.tsx Adds confirmation dialogs for guild removal, admin ID removal (including chip backspace), and per-game permission clearing; uses suppression checks for selected actions.
app/packages/web/src/pages/DiscordPage.test.tsx Adds tests covering the new confirmation flows for Clear, Remove guild (type-to-confirm), and Remove admin.
app/packages/web/src/lib/confirm-skip.ts Introduces module-level in-memory suppression store (isSuppressed / suppress).
app/packages/web/src/components/GameCard.tsx Adds Stop-server confirmation dialog with optional per-session suppression.
app/packages/web/src/components/GameCard.test.tsx Adds tests for Stop confirmation behavior (suppressed vs. dialog confirm/cancel).
app/packages/web/src/components/ConfirmDialog.tsx New reusable confirm dialog component (type-to-confirm + “don’t ask again” support).
app/packages/web/src/components/ConfirmDialog.test.tsx Adds unit tests for rendering, cancel/confirm behavior, type-to-confirm disabling, and suppression.

Comment on lines +568 to +570
onConfirm={() => {
if (pendingRemoveId) onRemove(pendingRemoveId);
setPendingRemoveId(null);
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2f8423e — prefixed with void to explicitly discard the promise. wrap() already owns the busy state and error feedback, so discarding the returned promise is intentional.


Generated by Claude Code

claude added 2 commits May 6, 2026 03:13
…ction

`onRemove` calls `wrap()` which is async; the returned Promise was
silently discarded in the onConfirm handler, risking unhandled
rejection noise if the API call failed. `void` makes the intent
explicit — `wrap` already owns the busy state and error feedback.

https://claude.ai/code/session_019cf4SN4tAs4R7R8mu2zHse
…onvention

Merges the file naming convention from #119 (issue-114). Renames the
three files introduced by this PR to match:
  ConfirmDialog.tsx         → confirm-dialog.component.tsx
  ConfirmDialog.test.tsx    → confirm-dialog.component.test.tsx
  GameCard.test.tsx         → game-card.component.test.tsx
  confirm-skip.ts           → confirm-skip.utils.ts

Updates all internal imports in those files and in the two conflict sites
(game-card.component.tsx, discord.page.tsx) to use the new paths.

https://claude.ai/code/session_019cf4SN4tAs4R7R8mu2zHse
Copilot AI review requested due to automatic review settings May 6, 2026 03:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment on lines +102 to +104
<AlertDialogFooter>
<AlertDialogCancel onClick={handleCancel}>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={handleConfirm} disabled={confirmDisabled}>
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5dc1e08 — removed the handleCancel function and the onClick from AlertDialogCancel. The Radix controlled root already fires onOpenChange(false) on cancel, so the explicit handler was a duplicate call.


Generated by Claude Code

…ialog

AlertDialogCancel already calls onOpenChange(false) via the controlled
Radix AlertDialog root. The explicit handleCancel onClick was causing a
double invocation on every cancel click.

https://claude.ai/code/session_019cf4SN4tAs4R7R8mu2zHse
@CoderCoco
Copy link
Copy Markdown
Owner Author

@copilot do one final check please

Copy link
Copy Markdown
Contributor

Copilot AI commented May 6, 2026

@copilot do one final check please

Final check done:

  • 335 tests pass (npm run app:test)
  • Lint clean (npm run app:lint)
  • Both previous reviewer fixes are in place: void prefix on the unhandled promise in GuildsSection (2f8423e), and the redundant onClick removed from AlertDialogCancel (5dc1e08)

No issues found — PR looks good to merge.

@CoderCoco CoderCoco merged commit c3b08dd into main May 6, 2026
8 checks passed
@CoderCoco CoderCoco deleted the claude/issue-67-confirmation-flow-destructive-actions branch May 6, 2026 03:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): confirmation flow for destructive actions

4 participants