web/src/components/ConfirmDialog/README.md
ConfirmDialog standardizes confirmation flows across the app. It replaces ad‑hoc window.confirm usage with an accessible, themeable dialog that supports asynchronous operations.
Dialog primitives (focus trap, ARIA roles)onConfirm.container class (SCSS module)ConfirmDialog
├── State: loading (tracks pending confirm action)
├── Dialog primitives: Header (title + description), Footer (buttons)
└── External control: parent owns open state via onOpenChange
import { useTranslate } from "@/utils/i18n";
import ConfirmDialog from "@/components/ConfirmDialog";
const t = useTranslate();
<ConfirmDialog
open={open}
onOpenChange={setOpen}
title={t("memo.delete-confirm")}
description={t("memo.delete-confirm-description")}
confirmLabel={t("common.delete")}
cancelLabel={t("common.cancel")}
onConfirm={handleDelete}
confirmVariant="destructive"
/>;
| Prop | Type | Required | Acceptable Values |
|---|---|---|---|
open | boolean | Yes | true (visible) / false (hidden) |
onOpenChange | (open: boolean) => void | Yes | Callback receiving next state; should update parent state |
title | React.ReactNode | Yes | Short localized action summary (text / node) |
description | React.ReactNode | No | Optional contextual message |
confirmLabel | string | Yes | Non-empty localized action text (1–2 words) |
cancelLabel | string | Yes | Localized cancel label |
onConfirm | `() => void | Promise<void>` | Yes |
confirmVariant | `"default" | "destructive"` | No |
const handleConfirm = async () => {
setLoading(true);
try {
await onConfirm(); // resolve -> close
onOpenChange(false);
} catch (e) {
console.error(e); // remain open for retry
} finally {
setLoading(false);
}
};
<Dialog open={open} onOpenChange={(next) => !loading && onOpenChange(next)} />
Dialog primitives work (modern browsers)The ConfirmDialog.module.scss file provides a .container hook. It currently only hosts a harmless custom property so the stylesheet is non-empty. Add real layout or variant tokens there instead of inline styles.
All visible strings must come from the translation system. Use useTranslate() and pass localized values into props. Separate keys for title/description.
Errors thrown in onConfirm are caught and logged. The dialog stays open so the caller can surface a toast or inline message and allow retry. (Consider routing serious errors to a higher-level handler.)
If you extend this component, update this README to keep usage discoverable.