mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-02 04:27:39 +00:00
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:
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user