Files
Genarrative/apps/admin-web/src/components/useAdminWriteConfirm.tsx
T
kdletters 071faa482c 统一 Rust 与 TypeScript 格式化门禁
纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口

完成项目 TypeScript/Prettier 与 Rust 全量格式化

修复 Pingora expected executable 门禁的空白敏感误报

同步开发运维文档与 AGC skill pack 格式化忽略规则
2026-09-01 16:28:34 +08:00

112 lines
2.9 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from 'react';
interface AdminWriteConfirmOptions {
action: string;
target: string;
}
interface PendingConfirm extends AdminWriteConfirmOptions {
resolve: (confirmed: boolean) => void;
}
export function useAdminWriteConfirm() {
const [pendingConfirm, setPendingConfirm] = useState<PendingConfirm | null>(
null,
);
const cancelButtonRef = useRef<HTMLButtonElement | null>(null);
const confirmWrite = useCallback((options: AdminWriteConfirmOptions) => {
return new Promise<boolean>((resolve) => {
setPendingConfirm((current) => {
if (current) {
current.resolve(false);
}
return { ...options, resolve };
});
});
}, []);
const closeConfirm = useCallback(
(confirmed: boolean) => {
const current = pendingConfirm;
if (!current) {
return;
}
setPendingConfirm(null);
current.resolve(confirmed);
},
[pendingConfirm],
);
useEffect(() => {
if (!pendingConfirm) {
return;
}
cancelButtonRef.current?.focus();
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
event.preventDefault();
closeConfirm(false);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [closeConfirm, pendingConfirm]);
const confirmDialog = pendingConfirm ? (
<div
aria-modal="true"
aria-labelledby="admin-write-confirm-title"
className="admin-confirm-backdrop"
role="dialog"
onMouseDown={(event) => {
if (event.target === event.currentTarget) {
closeConfirm(false);
}
}}
>
<section className="admin-confirm-panel">
<div className="admin-panel-heading">
<h3 id="admin-write-confirm-title">确认操作</h3>
<span>{pendingConfirm.action}</span>
</div>
<dl className="admin-info-list">
<div>
<dt>操作</dt>
<dd>{pendingConfirm.action}</dd>
</div>
<div>
<dt>对象</dt>
<dd>{pendingConfirm.target}</dd>
</div>
</dl>
<div className="admin-confirm-warning">该操作会立即影响线上数据</div>
<div className="admin-confirm-actions">
<button
className="admin-secondary-button"
ref={cancelButtonRef}
type="button"
onClick={() => closeConfirm(false)}
>
取消
</button>
<button
className="admin-danger-button"
type="button"
onClick={() => closeConfirm(true)}
>
确认
</button>
</div>
</section>
</div>
) : null;
return {
confirmWrite,
confirmDialog,
isConfirming: pendingConfirm !== null,
};
}