'use client'; import { useCallback, useRef, useState } from 'react'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { cn } from '@/lib/utils'; interface ConfirmOptions { title: string; description: string; /** Confirm button label. Default: "Delete". */ confirmLabel?: string; /** Cancel button label. Default: "Cancel". */ cancelLabel?: string; /** When true, confirm button is rendered in destructive red. Default: true. */ destructive?: boolean; } /** * Programmatic, awaitable confirmation dialog — the imperative counterpart * to `` (which needs a trigger element). * * Replaces the native `window.confirm()` pattern in 17 destructive flows * the audit's UI-UX pass flagged. `confirm(...)` is synchronous and * browser-styled (escape-hatch UX); this hook is async, accessible * (alert-dialog semantics + focus trap), keyboard-navigable, and matches * the rest of the app's visual language. * * Usage: * const { confirm, dialog } = useConfirmation(); * * async function handleDelete(file) { * const ok = await confirm({ * title: 'Delete file', * description: `Delete "${file.filename}"? This cannot be undone.`, * confirmLabel: 'Delete', * }); * if (!ok) return; * // ... actually delete * } * * return <>{children}{dialog}; * * The dialog renders into the component's tree once, and `confirm()` * resolves with the user's choice. Multiple sequential `confirm()` * calls are safe — each gets its own promise. */ export function useConfirmation() { const [state, setState] = useState<(ConfirmOptions & { open: boolean }) | null>(null); const resolverRef = useRef<((ok: boolean) => void) | null>(null); const confirm = useCallback((opts: ConfirmOptions): Promise => { // If a previous confirm is somehow still open, resolve it as a cancel // before starting the next. Defensive against rapid double-fires. if (resolverRef.current) { resolverRef.current(false); resolverRef.current = null; } setState({ ...opts, open: true }); return new Promise((resolve) => { resolverRef.current = resolve; }); }, []); const handleConfirm = useCallback(() => { resolverRef.current?.(true); resolverRef.current = null; setState((prev) => (prev ? { ...prev, open: false } : null)); }, []); const handleCancel = useCallback(() => { resolverRef.current?.(false); resolverRef.current = null; setState((prev) => (prev ? { ...prev, open: false } : null)); }, []); const dialog = state ? ( { if (!open) handleCancel(); }} > {state.title} {state.description} {state.cancelLabel ?? 'Cancel'} {state.confirmLabel ?? 'Delete'} ) : null; return { confirm, dialog }; }