fix(settings): inline MCP delete errors and stabilize save-close timer

Replace blocking alert() calls in MCP delete handlers with non-blocking deleteError state updates and setSaveStatus('error') on failure.

Thread deleteError through Settings -> Agents settings views and render inline error feedback in Claude/Codex MCP sections near delete actions.

Prevent orphaned save-close timers by clearing and nulling closeTimerRef before scheduling a new timeout, and by nulling the ref during unmount cleanup.
This commit is contained in:
Haileyesus
2026-02-24 14:05:30 +03:00
parent f59f40d0f9
commit e20ee2828d
6 changed files with 36 additions and 3 deletions

View File

@@ -191,6 +191,7 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }:
const [activeTab, setActiveTab] = useState<SettingsMainTab>(() => normalizeMainTab(initialTab));
const [isSaving, setIsSaving] = useState(false);
const [saveStatus, setSaveStatus] = useState<'success' | 'error' | null>(null);
const [deleteError, setDeleteError] = useState<string | null>(null);
const [projectSortOrder, setProjectSortOrder] = useState<ProjectSortOrder>('name');
const [codeEditorSettings, setCodeEditorSettings] = useState<CodeEditorSettingsState>(() => (
readCodeEditorSettings()
@@ -456,12 +457,14 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }:
return;
}
setDeleteError(null);
try {
await deleteMcpServer(serverId, scope);
await fetchMcpServers();
setDeleteError(null);
setSaveStatus('success');
} catch (error) {
alert(`Error: ${getErrorMessage(error)}`);
setDeleteError(getErrorMessage(error));
setSaveStatus('error');
}
},
@@ -609,12 +612,14 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }:
return;
}
setDeleteError(null);
try {
await deleteCodexMcpServer(serverName);
await fetchCodexMcpServers();
setDeleteError(null);
setSaveStatus('success');
} catch (error) {
alert(`Error: ${getErrorMessage(error)}`);
setDeleteError(getErrorMessage(error));
setSaveStatus('error');
}
},
@@ -706,6 +711,10 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }:
}));
setSaveStatus('success');
if (closeTimerRef.current !== null) {
window.clearTimeout(closeTimerRef.current);
closeTimerRef.current = null;
}
closeTimerRef.current = window.setTimeout(() => onClose(), 1000);
} catch (error) {
console.error('Error saving settings:', error);
@@ -776,6 +785,7 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }:
useEffect(() => () => {
if (closeTimerRef.current !== null) {
window.clearTimeout(closeTimerRef.current);
closeTimerRef.current = null;
}
}, []);
@@ -786,6 +796,7 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }:
toggleDarkMode,
isSaving,
saveStatus,
deleteError,
projectSortOrder,
setProjectSortOrder,
codeEditorSettings,

View File

@@ -31,6 +31,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }: Set
setActiveTab,
isSaving,
saveStatus,
deleteError,
projectSortOrder,
setProjectSortOrder,
codeEditorSettings,
@@ -153,6 +154,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }: Set
onDiscoverMcpTools={handleMcpToolsDiscovery}
onOpenCodexMcpForm={openCodexMcpForm}
onDeleteCodexMcpServer={handleCodexMcpDelete}
deleteError={deleteError}
/>
)}

View File

@@ -24,6 +24,7 @@ export default function AgentsSettingsTab({
mcpTestResults,
mcpServerTools,
mcpToolsLoading,
deleteError,
onOpenMcpForm,
onDeleteMcpServer,
onTestMcpServer,
@@ -86,6 +87,7 @@ export default function AgentsSettingsTab({
mcpTestResults={mcpTestResults}
mcpServerTools={mcpServerTools}
mcpToolsLoading={mcpToolsLoading}
deleteError={deleteError}
onOpenMcpForm={onOpenMcpForm}
onDeleteMcpServer={onDeleteMcpServer}
onTestMcpServer={onTestMcpServer}

View File

@@ -19,6 +19,7 @@ export default function AgentCategoryContentSection({
mcpTestResults,
mcpServerTools,
mcpToolsLoading,
deleteError,
onOpenMcpForm,
onDeleteMcpServer,
onTestMcpServer,
@@ -95,6 +96,7 @@ export default function AgentCategoryContentSection({
testResults={mcpTestResults}
serverTools={mcpServerTools}
toolsLoading={mcpToolsLoading}
deleteError={deleteError}
/>
)}
@@ -115,6 +117,7 @@ export default function AgentCategoryContentSection({
onAdd={() => onOpenCodexMcpForm()}
onEdit={(server) => onOpenCodexMcpForm(server)}
onDelete={(serverId) => onDeleteCodexMcpServer(serverId)}
deleteError={deleteError}
/>
)}
</div>

View File

@@ -31,6 +31,7 @@ type ClaudeMcpServersProps = {
testResults: Record<string, McpTestResult>;
serverTools: Record<string, McpToolsResult>;
toolsLoading: Record<string, boolean>;
deleteError?: string | null;
};
function ClaudeMcpServers({
@@ -40,6 +41,7 @@ function ClaudeMcpServers({
onDelete,
testResults,
serverTools,
deleteError,
}: Omit<ClaudeMcpServersProps, 'agent' | 'onTest' | 'onDiscoverTools' | 'toolsLoading'>) {
const { t } = useTranslation('settings');
@@ -57,6 +59,11 @@ function ClaudeMcpServers({
{t('mcpServers.addButton')}
</Button>
</div>
{deleteError && (
<div className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-800/60 dark:bg-red-900/20 dark:text-red-200">
{deleteError}
</div>
)}
<div className="space-y-2">
{servers.map((server) => {
@@ -254,9 +261,10 @@ type CodexMcpServersProps = {
onAdd: () => void;
onEdit: (server: McpServer) => void;
onDelete: (serverId: string) => void;
deleteError?: string | null;
};
function CodexMcpServers({ servers, onAdd, onEdit, onDelete }: Omit<CodexMcpServersProps, 'agent'>) {
function CodexMcpServers({ servers, onAdd, onEdit, onDelete, deleteError }: Omit<CodexMcpServersProps, 'agent'>) {
const { t } = useTranslation('settings');
return (
@@ -273,6 +281,11 @@ function CodexMcpServers({ servers, onAdd, onEdit, onDelete }: Omit<CodexMcpServ
{t('mcpServers.addButton')}
</Button>
</div>
{deleteError && (
<div className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-800/60 dark:bg-red-900/20 dark:text-red-200">
{deleteError}
</div>
)}
<div className="space-y-2">
{servers.map((server) => (

View File

@@ -36,6 +36,7 @@ export type AgentsSettingsTabProps = {
mcpTestResults: Record<string, McpTestResult>;
mcpServerTools: Record<string, McpToolsResult>;
mcpToolsLoading: Record<string, boolean>;
deleteError: string | null;
onOpenMcpForm: (server?: McpServer) => void;
onDeleteMcpServer: (serverId: string, scope?: string) => void;
onTestMcpServer: (serverId: string, scope?: string) => void;
@@ -71,6 +72,7 @@ export type AgentCategoryContentSectionProps = {
mcpTestResults: Record<string, McpTestResult>;
mcpServerTools: Record<string, McpToolsResult>;
mcpToolsLoading: Record<string, boolean>;
deleteError: string | null;
onOpenMcpForm: (server?: McpServer) => void;
onDeleteMcpServer: (serverId: string, scope?: string) => void;
onTestMcpServer: (serverId: string, scope?: string) => void;