Migration (multi-agent sweep over 216 files, 60 batches):
- Every user-visible dashboard + TUI string moved to t() with the exact
English inline default (en rendering byte-identical)
- Catalogs merged from per-batch fragments: en/zh-CN/zh-TW/fr/es now
carry ~5,930 keys each across common/app/errors/cli namespaces;
CLI bundles regenerated (6 locales incl. ko)
Integration fixes:
- 18 type errors: reserved {{count}} interpolations renamed, malformed
plural call, hand-rolled t-param types replaced with TFunction<"app">
- 23 lint errors: superseded label constants/helpers removed
- ExecutorStatusBar hook-order violation (keyboard-open early return
moved below hooks)
- TUI tests wrapped in I18nextProvider (uninitialized fallback renders
literal {{placeholders}}); dashboard vitest.setup boots a minimal en
i18next instance for the same reason
Known WIP (next commits): ~457 residual strings across 50 batches,
Korean drafts for swept keys, and a dashboard test-suite pass that is
still being stabilized (~283 failures under investigation — fake-timer
waitFor interaction, likely stale node_modules vs merged lockfile).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
101 lines
3.0 KiB
TypeScript
101 lines
3.0 KiB
TypeScript
import { useEffect, useRef } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import type { ConfirmOptions } from "../hooks/useConfirm";
|
|
import "./ConfirmDialog.css";
|
|
|
|
export interface ConfirmDialogProps {
|
|
isOpen: boolean;
|
|
options: ConfirmOptions | null;
|
|
onConfirm: () => void;
|
|
onTertiary?: () => void;
|
|
onCancel: () => void;
|
|
checkboxLabel?: string;
|
|
checkboxDescription?: string;
|
|
checkboxChecked?: boolean;
|
|
onCheckboxChange?: (next: boolean) => void;
|
|
}
|
|
|
|
export function ConfirmDialog({
|
|
isOpen,
|
|
options,
|
|
onConfirm,
|
|
onTertiary,
|
|
onCancel,
|
|
checkboxLabel,
|
|
checkboxDescription,
|
|
checkboxChecked = false,
|
|
onCheckboxChange,
|
|
}: ConfirmDialogProps) {
|
|
const { t } = useTranslation("app");
|
|
const cancelButtonRef = useRef<HTMLButtonElement | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!isOpen) {
|
|
return;
|
|
}
|
|
|
|
cancelButtonRef.current?.focus();
|
|
|
|
const onKeyDown = (event: KeyboardEvent) => {
|
|
if (event.key === "Escape") {
|
|
event.preventDefault();
|
|
onCancel();
|
|
}
|
|
};
|
|
|
|
document.addEventListener("keydown", onKeyDown);
|
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
}, [isOpen, onCancel]);
|
|
|
|
if (!isOpen || !options) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="modal-overlay open confirm-dialog-overlay" onClick={onCancel}>
|
|
<div
|
|
className="modal confirm-dialog"
|
|
onClick={(event) => event.stopPropagation()}
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={options.title}
|
|
>
|
|
<div className="modal-header">
|
|
<h3>{options.title}</h3>
|
|
<button className="modal-close" onClick={onCancel} aria-label={t("confirm.closeDialog", "Close confirmation dialog")}>
|
|
×
|
|
</button>
|
|
</div>
|
|
|
|
<div className="confirm-dialog__body">{options.message}</div>
|
|
|
|
{checkboxLabel ? (
|
|
<label className="checkbox-label confirm-dialog__checkbox">
|
|
<input
|
|
type="checkbox"
|
|
checked={checkboxChecked}
|
|
onChange={(event) => onCheckboxChange?.(event.target.checked)}
|
|
/>
|
|
<span>{checkboxLabel}</span>
|
|
{checkboxDescription ? <small className="confirm-dialog__checkbox-description">{checkboxDescription}</small> : null}
|
|
</label>
|
|
) : null}
|
|
|
|
<div className="modal-actions confirm-dialog__actions">
|
|
<button ref={cancelButtonRef} className="btn" onClick={onCancel}>
|
|
{options.cancelLabel ?? t("confirm.cancel", "Cancel")}
|
|
</button>
|
|
{options.tertiaryLabel && onTertiary ? (
|
|
<button className={`btn ${options.tertiaryDanger ? "btn-danger" : ""}`.trim()} onClick={onTertiary}>
|
|
{options.tertiaryLabel}
|
|
</button>
|
|
) : null}
|
|
<button className={`btn ${options.danger ? "btn-danger" : "btn-primary"}`} onClick={onConfirm}>
|
|
{options.confirmLabel ?? t("confirm.confirm", "Confirm")}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|