fix: prevent page crash when deleting an agent (#514)
This commit is contained in:
committed by
GitHub
Unverified
parent
9e10c12f67
commit
158e84ce8f
@@ -2,7 +2,7 @@
|
||||
* ConfirmDialog - In-DOM confirmation dialog (replaces window.confirm)
|
||||
* Keeps focus within the renderer to avoid Windows focus loss after native dialogs.
|
||||
*/
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
@@ -13,8 +13,9 @@ interface ConfirmDialogProps {
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
variant?: 'default' | 'destructive';
|
||||
onConfirm: () => void;
|
||||
onConfirm: () => void | Promise<void>;
|
||||
onCancel: () => void;
|
||||
onError?: (error: unknown) => void;
|
||||
}
|
||||
|
||||
export function ConfirmDialog({
|
||||
@@ -26,8 +27,19 @@ export function ConfirmDialog({
|
||||
variant = 'default',
|
||||
onConfirm,
|
||||
onCancel,
|
||||
onError,
|
||||
}: ConfirmDialogProps) {
|
||||
const cancelRef = useRef<HTMLButtonElement>(null);
|
||||
const [confirming, setConfirming] = useState(false);
|
||||
const [prevOpen, setPrevOpen] = useState(open);
|
||||
|
||||
// Reset confirming when dialog closes (during render to avoid setState-in-effect)
|
||||
if (prevOpen !== open) {
|
||||
setPrevOpen(open);
|
||||
if (!open) {
|
||||
setConfirming(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (open && cancelRef.current) {
|
||||
@@ -38,12 +50,27 @@ export function ConfirmDialog({
|
||||
if (!open) return null;
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
if (e.key === 'Escape' && !confirming) {
|
||||
e.preventDefault();
|
||||
onCancel();
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (confirming) return;
|
||||
const result = onConfirm();
|
||||
if (result instanceof Promise) {
|
||||
setConfirming(true);
|
||||
result.catch((error) => {
|
||||
if (onError) {
|
||||
onError(error);
|
||||
}
|
||||
}).finally(() => {
|
||||
setConfirming(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
|
||||
@@ -68,12 +95,14 @@ export function ConfirmDialog({
|
||||
ref={cancelRef}
|
||||
variant="outline"
|
||||
onClick={onCancel}
|
||||
disabled={confirming}
|
||||
>
|
||||
{cancelLabel}
|
||||
</Button>
|
||||
<Button
|
||||
variant={variant === 'destructive' ? 'destructive' : 'default'}
|
||||
onClick={onConfirm}
|
||||
onClick={handleConfirm}
|
||||
disabled={confirming}
|
||||
>
|
||||
{confirmLabel}
|
||||
</Button>
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"agentCreated": "Agent created",
|
||||
"agentCreateFailed": "Failed to create agent: {{error}}",
|
||||
"agentDeleted": "Agent deleted",
|
||||
"agentDeleteFailed": "Failed to delete agent: {{error}}",
|
||||
"agentUpdated": "Agent updated",
|
||||
"agentUpdateFailed": "Failed to update agent: {{error}}",
|
||||
"channelAssigned": "{{channel}} assigned to agent",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"agentCreated": "Agent を作成しました",
|
||||
"agentCreateFailed": "Agent の作成に失敗しました: {{error}}",
|
||||
"agentDeleted": "Agent を削除しました",
|
||||
"agentDeleteFailed": "Agent の削除に失敗しました: {{error}}",
|
||||
"agentUpdated": "Agent を更新しました",
|
||||
"agentUpdateFailed": "Agent の更新に失敗しました: {{error}}",
|
||||
"channelAssigned": "{{channel}} を Agent に割り当てました",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"agentCreated": "Agent 已创建",
|
||||
"agentCreateFailed": "创建 Agent 失败:{{error}}",
|
||||
"agentDeleted": "Agent 已删除",
|
||||
"agentDeleteFailed": "删除 Agent 失败:{{error}}",
|
||||
"agentUpdated": "Agent 已更新",
|
||||
"agentUpdateFailed": "更新 Agent 失败:{{error}}",
|
||||
"channelAssigned": "{{channel}} 已分配给 Agent",
|
||||
|
||||
@@ -153,12 +153,17 @@ export function Agents() {
|
||||
variant="destructive"
|
||||
onConfirm={async () => {
|
||||
if (!agentToDelete) return;
|
||||
try {
|
||||
await deleteAgent(agentToDelete.id);
|
||||
const deletedId = agentToDelete.id;
|
||||
setAgentToDelete(null);
|
||||
if (activeAgentId === agentToDelete.id) {
|
||||
if (activeAgentId === deletedId) {
|
||||
setActiveAgentId(null);
|
||||
}
|
||||
toast.success(t('toast.agentDeleted'));
|
||||
} catch (error) {
|
||||
toast.error(t('toast.agentDeleteFailed', { error: String(error) }));
|
||||
}
|
||||
}}
|
||||
onCancel={() => setAgentToDelete(null)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user