From 158e84ce8f6842038b2641065cf24a0c399aef21 Mon Sep 17 00:00:00 2001 From: paisley <8197966+su8su@users.noreply.github.com> Date: Mon, 16 Mar 2026 10:47:30 +0800 Subject: [PATCH] fix: prevent page crash when deleting an agent (#514) --- src/components/ui/confirm-dialog.tsx | 37 +++++++++++++++++++++++++--- src/i18n/locales/en/agents.json | 1 + src/i18n/locales/ja/agents.json | 1 + src/i18n/locales/zh/agents.json | 1 + src/pages/Agents/index.tsx | 15 +++++++---- 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/components/ui/confirm-dialog.tsx b/src/components/ui/confirm-dialog.tsx index 061a51bad..38e988350 100644 --- a/src/components/ui/confirm-dialog.tsx +++ b/src/components/ui/confirm-dialog.tsx @@ -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; 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(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 (
{cancelLabel} diff --git a/src/i18n/locales/en/agents.json b/src/i18n/locales/en/agents.json index 46a261a4b..31fcf8c49 100644 --- a/src/i18n/locales/en/agents.json +++ b/src/i18n/locales/en/agents.json @@ -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", diff --git a/src/i18n/locales/ja/agents.json b/src/i18n/locales/ja/agents.json index 39f8222b5..d1b9403d3 100644 --- a/src/i18n/locales/ja/agents.json +++ b/src/i18n/locales/ja/agents.json @@ -41,6 +41,7 @@ "agentCreated": "Agent を作成しました", "agentCreateFailed": "Agent の作成に失敗しました: {{error}}", "agentDeleted": "Agent を削除しました", + "agentDeleteFailed": "Agent の削除に失敗しました: {{error}}", "agentUpdated": "Agent を更新しました", "agentUpdateFailed": "Agent の更新に失敗しました: {{error}}", "channelAssigned": "{{channel}} を Agent に割り当てました", diff --git a/src/i18n/locales/zh/agents.json b/src/i18n/locales/zh/agents.json index e8785ec9c..b1cd6ef6d 100644 --- a/src/i18n/locales/zh/agents.json +++ b/src/i18n/locales/zh/agents.json @@ -41,6 +41,7 @@ "agentCreated": "Agent 已创建", "agentCreateFailed": "创建 Agent 失败:{{error}}", "agentDeleted": "Agent 已删除", + "agentDeleteFailed": "删除 Agent 失败:{{error}}", "agentUpdated": "Agent 已更新", "agentUpdateFailed": "更新 Agent 失败:{{error}}", "channelAssigned": "{{channel}} 已分配给 Agent", diff --git a/src/pages/Agents/index.tsx b/src/pages/Agents/index.tsx index 0839ec757..90728f579 100644 --- a/src/pages/Agents/index.tsx +++ b/src/pages/Agents/index.tsx @@ -153,12 +153,17 @@ export function Agents() { variant="destructive" onConfirm={async () => { if (!agentToDelete) return; - await deleteAgent(agentToDelete.id); - setAgentToDelete(null); - if (activeAgentId === agentToDelete.id) { - setActiveAgentId(null); + try { + await deleteAgent(agentToDelete.id); + const deletedId = agentToDelete.id; + setAgentToDelete(null); + if (activeAgentId === deletedId) { + setActiveAgentId(null); + } + toast.success(t('toast.agentDeleted')); + } catch (error) { + toast.error(t('toast.agentDeleteFailed', { error: String(error) })); } - toast.success(t('toast.agentDeleted')); }} onCancel={() => setAgentToDelete(null)} />