Reusable Confirm-Destructive Modal

Feature Owner (original implementation): Patrick Babala (Patpatty19)
Updated by / current owner: Patrick Babala
Module: Authoring — [7.3] Verification Prompt (Reusable Confirm-Destructive Modal)
Priority: P1 (audit quick win — unblocks [1.6], [3.7], [6.4], [9.5])
Status: Handoff — New (v1.0)
Date: 2026-10-01
Issue: wyzlab/WyzQuests #777 — Reusable Confirm-Destructive Modal.
PRs covered by this revision: #780 — feat/reusable-confirm-destructive-modal (primary; merged 2026-08-20). Related/integrated work: #772 (learner-group destructive flows adopted the component).
Audit: docs/FEATURE_AUDIT_MAY2026.md Module 7, item 7.3 — “Build a reusable <ConfirmDestructiveModal> component to standardise. Quick win.”
QA cases: QA-024 (permanent delete), QA-045 / QA-046 (secure asset delete), QA-073.5 (secure user delete), QA-081 (verification prompt shows).
Companion doc: Internal Technical Guide — docs/INTERNAL_TECHNICAL_GUIDE_reusable-confirm-destructive-modal.md (architecture, DB schema, API specs, helpers, ops, troubleshooting). This Hand-Off stays product-facing; where engineering depth is needed it cross-references the ITG instead of duplicating it.


EXECUTIVE SUMMARY

What is this feature?

The Reusable Confirm-Destructive Modal is a single shared <ConfirmDestructiveModal> component that standardises every destructive confirmation in WyzQuests behind a typed-token challenge: the destructive button stays disabled until the user types the exact token (DELETE, RESET, or an entity title). It replaces ten one-off confirmation dialogs and adds server-side re-validation of the typed token on the four highest-risk endpoints (permanent delete, asset purge, learner reset, admin user delete).

Why does it matter?

Before this feature, destructive confirmations were inconsistent — some flows used a bespoke type-to-confirm input with a different token, some used a soft “Are you sure?”, and several ran with no confirmation at all (gamification badge/trigger deletes, notification deletes, activity deletes). The May 2026 audit flagged this as “Inconsistent” (item 7.3) and called the shared component the single highest-leverage item to land. It protects irreversible actions (deleting content, purging assets, resetting learner progress, deleting accounts) and gives those actions a consistent, hard-to-misclick confirmation.

MVP scope (current implementation):

  • One controlled component with a typed-token gate, busy state, brand styling, and an optional content slot.

  • Exact-token enforcement client-side; the confirm button is disabled until the token matches.

  • Server-side token double-check on the four named endpoints.

  • Adoption across creator hub, content library, quest editor, learner, forum, reviewer, admin, and agency.

  • Removal of ten ad-hoc confirmation modals.

Shipped scope (v1.0):

  • Component — components/shared/confirm-destructive-modal.tsx (title, description, confirmToken, confirmLabel, onConfirm, isSubmitting, matchMode, extraContent, cancelLabel, submittingLabel).

  • Server checks — permanent-delete (DELETE), purge-assets (Zod literal DELETE), reset-quest (Zod literal RESET), delete-user (Zod literal DELETE + re-auth).

  • Helper — components/learner/resetQuest.ts (resetQuestProgress) owns the reset request, token, toasts, and returns a success boolean.

  • Schema — purgeAssetsSchema in shared/schemas/contentManagementSchema.ts.

  • Cleanup — ten ad-hoc modals removed: DeleteConfirmationModal, BulkDeleteModal, PurgeAssetModal, ResetQuestDialog, DeleteUserModal, FolderDeleteConfirmationModal, DeleteAllDialog, DeleteFolderDialog, DeletePostDialog, DeleteCommentDialog.

Still deferred: server-side token validation for the 13+ non-named destructive flows; unit/component tests for the shared modal and schemas; correcting the stale Playwright selectors (#confirm-delete, #confirm-purge-asset); a configurable input id.


1. USER PAIN POINT & SOLUTION

Current State (Without Feature)

Every destructive action had its own dialog, or none. Tokens differed (DELETE vs. the entity title vs. nothing), copy varied, and the highest-risk flows (admin delete, asset purge) used one-off inputs that were deleted or diverged over time. Users could trigger an irreversible delete from a single click in several surfaces.

Pain Point

  • Emotional: Users are uneasy about irreversible actions and cannot tell whether a confirmation is “real” (typed) or just a click-through.

  • Functional: Inconsistent confirmation UX; some destructive paths had no confirmation guard.

  • Business Impact: Higher risk of accidental data loss, slower QA (each flow tested differently), and duplicated maintenance across ten dialogs.

Future State (With Feature)

Every destructive action presents the same modal: a warning icon, a plain-language description of what will be destroyed, and a required typed token. The button cannot be pressed until the token matches, and the four highest-risk endpoints re-check the token server-side. One component to maintain, one interaction to learn.

Marketing Hook

“Type it to delete it — every destructive action, one consistent, server-verified guard.”


2. CODEBASE ASSESSMENT

Current Implementation Status

The feature is a single shared client component plus server-side token checks on four endpoints and adoption across the creator, content-library, quest-editor, learner, forum, reviewer, admin, and agency surfaces. Ten ad-hoc confirmation modals were deleted. All four named endpoints enforce their token server-side; the remaining flows are client-gated only (documented gap).

Primary Files

  • components/shared/confirm-destructive-modal.tsx — the shared component (token gate, busy state, extraContent slot).

  • components/learner/resetQuest.ts — shared reset helper (confirm_text: "RESET", returns boolean).

  • shared/schemas/contentManagementSchema.ts — permanentDeleteSchema, purgeAssetsSchema (confirm_text: z.literal("DELETE")).

  • app/api/creator/(content)/permanent-delete/route.ts — exact DELETE enforcement for single + bulk; PERMANENT_DELETE_FAILED audit.

  • app/api/creator/(background-assets)/purge-assets/route.ts — Zod-literal token + ApiResponseHelper envelope.

  • app/api/learner/reset-quest/route.ts — Zod-literal RESET.

  • app/api/admin/delete-user/route.ts — Zod-literal DELETE + re-auth token.

  • app/admin/manage-users/page.tsx, app/admin/agencies/page.tsx, app/agency/team/page.tsx — admin/agency destructive flows (typed confirm → ReAuth for delete).

  • components/creator/{MyContent,TrashContent,ContentListRow}.tsx, app/creator/archive/page.tsx — single + bulk permanent delete.

  • components/creator/background-assets/AssetList.tsx — purge (single + bulk).

  • components/content-library/{ContentCard,FolderCard,FolderTreeView,FolderPermissionsModal,ContentLibraryPage,FolderWrapper}.tsx — folder/content delete + permission removal.

  • components/creator/groups/DeleteGroupDialog.tsx, GroupMembersTable.tsx, components/creator/enrollment/GroupEnrollmentTable.tsx, app/creator/learner-groups/[id]/page.tsx — learner-group destructive flows.

  • app/quest-editor/** (canvas linear/exploration, form-editor, enrollment, SettingsDropdown, ArchivedNodesModal) — editor deletes.

  • components/forum/{PostDetail,CommentSection}.tsx, components/reviewer/{GuestInviteModal,comments/CommentThread}.tsx, components/notification/Notifications.tsx, components/admin/gamification/{BadgeList,TriggerList,AgencyBadgeRequirements}.tsx.

  • Migrations: supabase/migrations/20260414_create_audit_logs_table.sql, 20260526_create_admin_reauth_tokens.sql, 20260612_update_rls_policies_multi_role.sql, 20260824_fix_not_null_set_null_user_fks.sql, 20260504_create_quest_folder.sql.

Current Behavior Summary

  • The modal is fully controlled by its parent (open / onOpenChange); the parent owns the request and the isSubmitting state.

  • The token input resets on every open; Enter submits only when valid; both buttons disable and a spinner shows while submitting.

  • matchMode defaults to "exact"; canvas “Delete All” uses "case-insensitive" with the quest title as the token.

  • An empty token can never be confirmed (guards untitled drafts).

  • The four named endpoints re-validate the token before any destructive side effect; mismatch is rejected with a 400 (and, for permanent delete, an audit row).

  • Successful deletes write to audit_logs in the admin and permanent-delete paths.

Strengths Already Present

  • Single component for all destructive confirmations; ten ad-hoc dialogs removed.

  • Exact-token gate plus a non-empty-token guard; mismatch hint and aria-invalid feedback.

  • Parent-owned busy state keeps the modal open until the request resolves (adopted in GroupMembersTable, learner-group unassign, FolderPermissionsModal, Notifications).

  • Server-side token re-validation on the four highest-risk endpoints (AC3).

  • Reset flow gates destructive client state (progress clear, reload, dialog close) on the server result, so a failed reset is never shown as success.

  • extraContent slot preserves per-flow needs (bulk item lists, member-reassignment select) without branching the component.

  • Folder-delete copy matches the actual server behaviour (items move to the library root via ON DELETE SET NULL; only the folder structure is deleted).

Gaps / Hardening Opportunities

  • Server-side token enforcement exists only on the four named endpoints; ~13 other destructive flows are client-gated only (scoped follow-up).

  • No unit or component tests for the shared modal or the token schemas.

  • The Playwright specs for permanent delete and asset purge still target the removed modals’ ids (#confirm-delete, #confirm-purge-asset); the component uses #confirm-destructive.

  • The token input id (confirm-destructive) is hardcoded and not configurable, so multiple mounted modals produce duplicate DOM ids.

  • permanentDeleteSchema.confirm_text is z.string().min(1) (not a Zod literal); the exact-match rule is an imperative guard in the route. Its docstring still mentions a title-match branch that no longer exists.

  • No DB transaction around delete + storage cleanup, and no rate limiting or re-auth on permanent delete / purge / reset.


3. 4D FRAMEWORK MAPPING

Diagnose

Destructive actions had inconsistent or missing confirmations, differing tokens, and duplicated one-off dialogs — a data-loss and maintainability risk.

Design

Standardise on one controlled component that requires a typed token, keeps the parent in charge of the request, and re-validates the token server-side on the highest-risk endpoints.

Develop

Build ConfirmDestructiveModal, migrate every destructive flow to it with per-flow content via a slot, delete the ad-hoc dialogs, and add Zod/guard token checks to permanent-delete, purge-assets, reset-quest, and delete-user.

Deliver

Every destructive action looks and behaves the same, cannot be triggered without typing the token, and the four riskiest operations are verified again on the server.


4. USER FLOWS

Entry Point

The user triggers any destructive action from an existing surface (e.g. a delete icon in My Content / Trash, a Purge button in the asset trash, Reset Progress in the learner dashboard, or Delete in Admin → Manage Users). The parent opens <ConfirmDestructiveModal>.

Success Criteria

  • The modal shows a clear title, a plain-language description of the destruction, and the required token.

  • The destructive button is disabled until the token matches exactly (or case-insensitively for title tokens).

  • The request runs once, with the modal showing a busy spinner until it resolves.

  • On success the modal closes and a confirmation toast is shown; on failure the modal stays open (or reverts the optimistic UI) so the user can retry.

  • The four named endpoints reject a missing/incorrect token server-side.

Main Flow (Happy Path)

  • User clicks the destructive action; the parent opens the modal.

  • The modal shows the warning icon, title, description, and “Type <TOKEN> to confirm”.

  • The user types the token; once it matches, the destructive button enables.

  • The user presses the button (or Enter); the parent sends the request with the token attached and sets isSubmitting.

  • The endpoint re-validates the token, performs the destructive operation, and returns success.

  • The parent shows a success toast and closes the modal; the list refreshes.

Edge Cases

  • Wrong token: input shows a red border and “Text does not match. Type exactly <TOKEN>.”; button stays disabled.

  • Empty token (untitled draft): an empty confirmToken can never be confirmed, even with empty input.

  • Case-insensitive title token: canvas “Delete All” matches the quest title trimmed/lowercased.

  • Submitting: both buttons disable, the dialog cannot be closed, and Enter is a no-op until the request resolves.

  • Server token mismatch (permanent delete): 400 'Please type "DELETE" to confirm permanent deletion' and a PERMANENT_DELETE_FAILED audit row.

  • Admin delete: typed DELETE then a separate Re-Auth challenge; a wrong password blocks the delete.

  • Reset failure: the dialog stays open with an error toast; progress is not cleared client-side.

  • Folder delete: empty folders delete instantly; typed confirm only when the folder has content or sub-folders, and the copy says items move to the library root rather than being deleted.

  • Bulk delete: the modal lists the selected items; the response reports any missingIds (already deleted concurrently).

Decision Points

  • IF the caller is a creator/agency context → permanent delete and purge are allowed subject to ownership/folder-admin checks.

  • IF the caller is an ADMIN → delete-user requires the typed DELETE and a valid re-auth token.

  • IF the caller is a learner → reset-quest requires RESET and an existing enrollment.

  • IF matchMode is case-insensitive → the title token is compared trimmed and lowercased.

  • IF the token is empty → the destructive button is never enabled.


5. INFORMATION ARCHITECTURE

Primary Information (Always visible)

  • Modal title (per-flow, e.g. “Delete User Account”, “Permanently Delete File”, “Are you absolutely sure?”).

  • Warning icon and destructive description.

  • “Type <TOKEN> to confirm” label.

  • Token input.

  • Cancel and destructive confirm buttons.

Secondary Information

  • Optional extraContent above the input: bulk item list, folder-delete scope, or member-reassignment select.

  • Busy spinner and submitting label (“Deleting…”, “Purging…”, “Resetting…”, “Removing…”) while the request runs.

  • Mismatch hint under the input when the token is wrong.

Tertiary Information (Hidden until needed)

  • The exact token per flow (DELETE, RESET, or the entity title).

  • The endpoint and payload (owned by the parent, not the modal).

Actions

  • Primary CTA: the destructive confirm button (disabled until the token matches).

  • Secondary Actions: Cancel (and, where implemented, keeping the modal open on failure).


6. WIREFRAMES

The feature is a single centered modal dialog, reused on every destructive path. The title and description change per flow; the token and labels change per flow.

Key Screens:

  • Confirm Destructive modal (warning icon circle, title, description, optional content slot, token label + input, Cancel / destructive confirm).

  • Busy state (spinner in the confirm button; both buttons disabled; dialog not closable).

  • Mismatch state (red input border + “Text does not match. Type exactly <TOKEN>.”).

Annotations:

  • The confirm button uses the destructive variant; the title uses the brand heading style.

  • The token input resets every time the modal opens.

  • The optional content slot carries bulk item lists (permanent delete / purge) and the group member-reassignment select (delete group).

  • For admin delete-user, the modal chains before the existing Re-Auth modal, not in place of it.

The architecture/system diagram lives with the companion ITG (docs/INTERNAL_TECHNICAL_GUIDE_reusable-confirm-destructive-modal.md), not here.


7. WIREFLOWS

User triggers a destructive action → modal opens with the token challenge → user types the token → button enables → user confirms → parent sends the request with the token → server re-validates (on the four named endpoints) → success toast + modal closes + list refreshes, or failure keeps the modal open with an error so the user can retry. For admin delete-user the chain is typed DELETE → Re-Auth → delete.


8. PROTOTYPE

Figma Prototype Link: Not currently available for this feature.

How to test:

  • Open My Content → Trash, delete an item, and confirm the modal requires typing DELETE before the button enables.

  • Type a wrong token and confirm the mismatch hint appears and the button stays disabled.

  • Purge a trashed background asset and confirm DELETE is required and the server rejects a wrong token.

  • As a learner, open Reset Progress on a quest, confirm RESET is required, and verify a failed reset leaves the dialog open.

  • As an admin, delete a user: type DELETE, then complete the Re-Auth challenge and confirm the delete is audited.

  • Open a canvas with nodes, use “Delete All”, and confirm the token is the quest title (case-insensitive). Empty drafts must never be confirmable.


9. DATA MODEL

The feature adds no tables or columns; it reuses existing stores.

Core Tables

  • audit_logs — sensitive-action trail written by the feature: PERMANENT_DELETE_FAILED (permanent-delete token mismatch), USER_DELETE, REAUTH_TOKEN_INVALID (admin delete). Columns: id, user_id (FK → app_users.id, ON DELETE SET NULL), action, entity_type, entity_id, details (JSONB), ip_address, user_agent, created_at.

  • admin_reauth_tokens — short-lived single-use re-auth tokens consumed before an admin user delete.

  • quest_enrollments — reset writes progress back to { percentage: 0, visited_cards: [], last_visited_at: null }; milestone XP is intentionally not re-armed.

  • quest_folders / quests.quest_folder_id — ON DELETE SET NULL, which is why folder deletion moves content to the root rather than deleting it.

Key Constraint / Note

  • audit_logs.user_id is nullable after 20260824_fix_not_null_set_null_user_fks.sql; before that migration the cascade ON DELETE SET NULL on user delete fails with 23502.

Full column types, indexes, RLS policies, and migration supersession are in the ITG §3 (Data Model / Schema).


10. API CONTRACTS

All four endpoints use the standard ApiResponseHelper envelope: success { success: true, message?, data, error: null }, error { success: false, message, data: null, error: { code, message, details? } }.

Permanent Delete

  • Endpoint: DELETE /api/creator/permanent-delete

  • Auth: creator context (CREATOR / AGENCY / ADMIN) + per-item ownership or folder-admin check.

  • Body: { content_type: "quests" | "adventures", content_id | content_ids, confirm_text: "DELETE" }.

  • Behavior: validates the payload, verifies ownership, enforces confirm_text === "DELETE" (mismatch → 400 + PERMANENT_DELETE_FAILED audit), deletes the rows, then cleans up storage/asset_metadata.

  • Response: { deletedIds, missingIds }.

Purge Assets

  • Endpoint: DELETE /api/creator/purge-assets

  • Auth: creator context + per-asset ownership check.

  • Body: { asset_id | asset_ids, confirm_text: "DELETE" } (purgeAssetsSchema, z.literal("DELETE")).

  • Behavior: validates the token, deletes DB rows first, then removes storage files for the rows that were deleted.

  • Response: { deletedIds, missingIds }.

Reset Quest Progress

  • Endpoint: POST /api/learner/reset-quest

  • Auth: any authenticated user, plus an existing quest_enrollments row for the learner.

  • Body: { quest_id, confirm_text: "RESET" } (z.literal("RESET")).

  • Behavior: resets progress; milestone XP is not re-armed.

  • Response: { progress }.

Delete User

  • Endpoint: DELETE /api/admin/delete-user

  • Auth: ADMIN + valid reauthToken + typed confirm_text: "DELETE".

  • Body: { userId, reauthToken, confirm_text: "DELETE" }.

  • Behavior: consumes the re-auth token, refuses ADMIN targets, deletes the Supabase Auth identity, cleans up agency memberships, deletes the profile row, and audits USER_DELETE.

  • Response: 200 "User <email> has been permanently deleted".

Auth checks, exact error strings, and full request/response shapes are in the ITG §3 (API Specification).


11. DATA REQUIREMENTS

Frontend Needs

  • The destructive flow’s title, description, token, and confirm label (parent-supplied).

  • The parent-owned isSubmitting flag.

  • Optional extraContent data (bulk item names, folder scope, member-reassignment options).

  • The modal open state and a close callback.

Backend Needs

  • Zod-validated request bodies with the confirm_text token (the four named endpoints).

  • Ownership / role authorization per endpoint.

  • audit_logs for the permanent-delete mismatch and admin-delete trail.

  • Supabase Storage (public-assets) cleanup for permanent delete and purge.

  • Re-auth token issue/consume for admin user delete.


12. SECURITY & AUTHORIZATION

Who can access this feature?

  • Creator (agency or standalone): ✔ permanent delete, purge, folders/content, editor, activities (subject to ownership).

  • Agency OWNER/ADMIN: ✔ agency member removal, agency delete (typed confirm → Re-Auth), and creator flows.

  • Admin: ✔ user delete (typed DELETE + Re-Auth), gamification deletes, notifications, forum/reviewer moderation.

  • Reviewer: ✔ forum/reviewer comment and invite revoke flows.

  • Learner: ✔ Reset Progress on their own enrollment (RESET).

Authorization Logic

  • The modal itself performs no authorization — it only gates the click. The parent and the endpoint enforce access.

  • The four named endpoints re-validate the typed token server-side (AC3): permanent-delete (exact DELETE), purge-assets (z.literal("DELETE")), reset-quest (z.literal("RESET")), delete-user (z.literal("DELETE") + re-auth).

  • Admin user delete requires a single-use, time-bounded (≤5 min) re-authentication token in addition to the typed token.

  • Ownership/folder-admin checks and role checks are unchanged by this feature.

  • Authentication is Supabase Auth; the legacy-named app_users.clerk_id column stores the Supabase auth UUID.

Note: server-side token enforcement covers only the four named endpoints; the remaining ~13 destructive flows are client-gated only (see Open Questions).


13. ERROR HANDLING

Common Errors

  • Client mismatch ("Text does not match. Type exactly \"DELETE\"." / "...\"RESET\"." / the title).

  • Permanent delete token rejected ('Please type "DELETE" to confirm permanent deletion').

  • Invalid permanent delete payload ("Invalid permanent delete request payload").

  • Invalid purge payload ("Invalid permanent purge request payload").

  • No IDs provided ("No content IDs provided" / "No asset IDs provided").

  • Not permitted ("You do not have permission to delete this content").

  • Content/asset not found ("Content not found or access denied" / "Assets not found").

  • Already deleted ("Content not found. It may have been already deleted.").

  • Reset errors ("Enrollment not found" / "Failed to reset quest progress").

  • Admin delete errors ("Invalid input", "Re-authentication token is invalid or expired. Please re-authenticate.", "Cannot delete admin users", "Failed to delete user").

Handling Guidance

  • Keep the modal open on failure so the user can retry (parent owns this).

  • For a server token mismatch, re-check the token wired at the call site; do not bypass the client gate.

  • For an expired/used re-auth token, re-run the Re-Auth modal; tokens are single-use and last ≤5 minutes.

  • Treat storage-cleanup warnings as non-fatal (the DB row is already deleted) but flag orphaned files operationally.

  • Surface exact messages to the user; do not silently swallow delete or reset failures.


14. TESTING CHECKLIST

Happy Path

  • Destructive button stays disabled until the token is typed exactly.

  • Typing the token enables the button; confirming runs the request once with a spinner.

  • Success closes the modal and shows a toast.

  • Permanent delete and purge accept DELETE; learner reset accepts RESET; canvas “Delete All” accepts the quest title (case-insensitive).

  • Admin delete-user requires DELETE and then a successful Re-Auth.

Edge Cases

  • Wrong token → mismatch hint, button disabled.

  • Empty input with an empty token → button never enables.

  • isSubmitting → both buttons disabled, dialog not closable, Enter no-op.

  • Server rejects a wrong permanent-delete token → 400 + PERMANENT_DELETE_FAILED.

  • Reset failure → dialog stays open, progress not cleared client-side.

  • Admin Re-Auth failure → delete blocked.

  • Folder delete → empty folders delete instantly; non-empty folders warn that items move to the root.

  • Bulk delete → item list shown; missingIds reported.

  • Multiple modals mounted on one page → currently share the input id (known limitation).

Coverage Notes

  • E2E: tests/e2e/creator/permanent-delete.spec.ts, tests/e2e/creator/secure-asset-delete.spec.ts, tests/e2e/creator/archive-restore-delete.spec.ts, tests/e2e/admin/secure-user-delete.spec.ts.

  • No unit/component tests exist for the shared modal or the token schemas.

  • The permanent-delete and asset-purge specs still reference the removed modals’ input ids and need their selectors updated.


15. OPEN QUESTIONS

For Product

  • Should the remaining ~13 destructive flows get server-side token enforcement like the four named endpoints? — Still open. Today they are client-gated only.

  • Should permanent delete and purge require re-authentication (like admin user delete)? — Still open. Only admin delete-user has re-auth.

  • Should the shared modal be exposed as a documented pattern for future destructive features? — Still open. Currently it is code-standard only.

For Engineering

  • Should the shared modal get unit/component tests (token gate, matchMode, empty-token guard, isSubmitting)? — Still open.

  • Should the stale Playwright selectors (#confirm-delete, #confirm-purge-asset) be updated to the component’s #confirm-destructive? — Still open.

  • Should the token input id be configurable to avoid duplicate DOM ids when multiple modals mount? — Still open.

  • Should permanentDeleteSchema.confirm_text become z.literal("DELETE") for parity with the other endpoints, and its stale docstring corrected? — Still open.


16. OUT OF SCOPE (v1.1+)

  • Server-side token validation for the non-named destructive flows — out of scope (follow-up issue).

  • Replacing W0.5 re-authentication for admin actions — out of scope (unchanged; the modal chains before it).

  • Converting soft delete / archive / restore dialogs to the destructive modal — out of scope (those are non-destructive).

  • A design-system variant gallery of the modal — out of scope.

  • Making the modal fetch or own request lifecycle — out of scope (parent-owned by design).


17. SUCCESS METRICS

  • Destructive-flow confirmation coverage (share of destructive actions gated by the shared modal).

  • Accidental-destruction reports (target: zero from a stray click).

  • Server token-rejection rate on the four named endpoints (should be near zero in normal use).

  • Reduction in duplicate confirmation dialogs (10 removed).

  • QA time for destructive flows (one consistent interaction to test).


18. DEPENDENCIES

This feature depends on

  • Shadcn/UI Dialog, Button, Input, Label and lucide-react icons.

  • Zod v4 for the server-side token schemas.

  • ApiResponseHelper (W1.4 response envelope).

  • Supabase Postgres (content, assets, enrollments), Supabase Auth (admin delete), and Supabase Storage (public-assets).

  • The W0.5 re-authentication service (admin_reauth_tokens) for admin user delete.

  • audit_logs for the permanent-delete mismatch and admin-delete trail.

These features depend on this

  • Content Management [1.6] permanent delete (single + bulk).

  • Background Asset Library [3.7] secure purge.

  • Manage Users [6.4] permanent user delete.

  • Learner quest [9.5] Reset Progress.

  • Learner groups [11.5], content library folders, quest editor, forum, reviewer, notifications, and gamification destructive flows.


19. TIMELINE & OWNERSHIP

  • Original owner / Hand-Off author: Patrick Babala (Patpatty19).

  • Implementation updates & current owner: Patrick Babala.

  • Reviewers: Christian Denzon (@cdnzn) — Round 1; clydetims (@clydetims) — Round 2 (PR #780).

  • QA: Manual end-to-end verification across the migrated flows; e2e specs QA-024, QA-045/QA-046, QA-073.5, QA-081.

  • Shipped: PR #780 merged 2026-08-20 (13373adb core, 1210c258 learner groups + content library, 07530207 round-1 fixes); integrated via b65e7991 on 2026-08-21.

  • Estimated Completion: core complete; remaining items are the Open Questions and Out of Scope sections above.


REFERENCES

  • Issue: wyzlab/WyzQuests #777 — Reusable Confirm-Destructive Modal.

  • Primary PR: #780 — feat/reusable-confirm-destructive-modal (merged 2026-08-20).

  • Related PR: #772 — learner-group destructive flows.

  • Audit: docs/FEATURE_AUDIT_MAY2026.md Module 7, item 7.3 (and D1/§5 priority notes).

  • Migrations: supabase/migrations/20260414_create_audit_logs_table.sql, 20260526_create_admin_reauth_tokens.sql, 20260612_update_rls_policies_multi_role.sql, 20260824_fix_not_null_set_null_user_fks.sql, 20260504_create_quest_folder.sql.

  • QA: tests/e2e/creator/permanent-delete.spec.ts (QA-024), tests/e2e/creator/secure-asset-delete.spec.ts (QA-045/QA-046), tests/e2e/admin/secure-user-delete.spec.ts (QA-073.5), tests/e2e/creator/archive-restore-delete.spec.ts.

  • Companion ITG: docs/INTERNAL_TECHNICAL_GUIDE_reusable-confirm-destructive-modal.md.


VERSION HISTORY

  • 1.0 — 2026-10-01 — Patrick Babala. Initial Hand-Off for the Reusable Confirm-Destructive Modal ([7.3], PR #780). Covers the shared component, typed-token gate, server-side token checks, migrated destructive flows, removed ad-hoc modals, API contracts, security/authorization, testing checklist, and known limitations.


Was this article helpful?