refactor(web): shared ConfirmDialog, FormControls, and Modal a11y

Extract reusable confirm/modal shells and bordered form controls, adopt them across list pages and editors, and improve icon-button labels plus KvPage keyboard rows.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-21 22:08:23 +07:00
co-authored by Cursor
parent 9985ff3440
commit 3d25d8a614
27 changed files with 606 additions and 626 deletions
@@ -0,0 +1,55 @@
import { Modal } from "./Modal.jsx";
/**
* Shared confirm / delete dialog.
*
* @param {object} props
* @param {boolean} props.open
* @param {string} props.title
* @param {import("react").ReactNode} [props.message]
* @param {string} [props.confirmLabel]
* @param {string} [props.confirmClass]
* @param {boolean} [props.loading]
* @param {boolean} [props.pending] alias for loading
* @param {boolean} [props.confirmDisabled]
* @param {import("react").ReactNode} [props.error]
* @param {() => void} props.onCancel
* @param {() => void} props.onConfirm
*/
export function ConfirmDialog({
open,
title,
message,
confirmLabel = "Delete",
confirmClass = "btn-error",
loading,
pending,
confirmDisabled = false,
error,
onCancel,
onConfirm,
}) {
const busy = Boolean(loading ?? pending);
return (
<Modal open={open} onClose={onCancel} boxClassName="max-w-md" aria-label={title}>
<h3 className="font-bold">{title}</h3>
{message ? <div className="mt-2 text-sm opacity-70">{message}</div> : null}
{error ? <p className="text-error text-sm mt-2">{error}</p> : null}
<div className="modal-action">
<button type="button" className="btn btn-ghost" onClick={onCancel} disabled={busy}>
Cancel
</button>
<button
type="button"
className={`btn ${confirmClass}`}
disabled={busy || confirmDisabled}
onClick={onConfirm}
>
{busy ? <span className="loading loading-spinner loading-xs" /> : null}
{confirmLabel}
</button>
</div>
</Modal>
);
}