import { useState, type ReactNode } from 'react' import { Modal } from '@/components/ui/Modal' import { Alert } from '@/components/ui/Alert' import { Button } from '@/components/ui/Button' import { errorMessage } from '@/lib/api' /** * A confirm-and-act dialog: a message, then Cancel / Confirm. It owns the shared * shape every confirmation repeated by hand — the busy lock, the inline error on * failure (so a 409 like PLANT_IN_USE is shown, not swallowed), and the footer — * so each caller supplies only its message, its action, and its labels. * * onConfirm runs the action; it resolving closes the dialog, it throwing keeps * the dialog open with the error and re-enables the button for a retry. This is * confirmations only — dialogs with their own inputs (a rename, a form) keep * using Modal directly. */ export function ConfirmModal({ title, children, confirmLabel, busyLabel, confirmVariant = 'danger', confirmDisabled = false, errorFallback, onConfirm, onClose, }: { title: string /** The body — what's being confirmed. */ children: ReactNode confirmLabel: string /** Label while the action is in flight (e.g. "Deleting…"). */ busyLabel: string confirmVariant?: 'danger' | 'primary' /** Extra guard beyond busy (e.g. nothing to clear, no current user). */ confirmDisabled?: boolean /** Message if the action throws something without its own user-facing text. */ errorFallback: string onConfirm: () => Promise onClose: () => void }) { const [busy, setBusy] = useState(false) const [error, setError] = useState(null) async function handleConfirm() { setError(null) setBusy(true) try { await onConfirm() onClose() } catch (err) { setError(errorMessage(err, errorFallback)) setBusy(false) // keep the dialog open so the message shows and retry works } } return (
{children} {error && {error}}
) }