From b1b50187b38b2caaf2b231af378551a02033ddc5 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 29 Apr 2026 23:11:32 -0400 Subject: [PATCH] fix(web): integrations panel review fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Trim whitespace before checking the typed-confirm value (paste-with-spaces was silently keeping the button disabled). - Wrap onConfirmDisconnect in try/catch and surface disconnect_failed inline in the modal — operators on flaky networks now see why nothing happened. - Invalidate quality-profile + root-folder caches alongside config on both save and disconnect, so a base_url change refetches the lists immediately. --- .../routes/admin/integrations/+page.svelte | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/web/src/routes/admin/integrations/+page.svelte b/web/src/routes/admin/integrations/+page.svelte index 3e3a914c..8c83ab5e 100644 --- a/web/src/routes/admin/integrations/+page.svelte +++ b/web/src/routes/admin/integrations/+page.svelte @@ -66,7 +66,13 @@ default_root_folder_path: rootPath }; await putLidarrConfig(cfg); - await client.invalidateQueries({ queryKey: qk.lidarrConfig() }); + // Invalidate config + profile/folder lists. A new base_url means a + // different server, so the cached lists must refetch. + await Promise.all([ + client.invalidateQueries({ queryKey: qk.lidarrConfig() }), + client.invalidateQueries({ queryKey: qk.lidarrQualityProfiles() }), + client.invalidateQueries({ queryKey: qk.lidarrRootFolders() }) + ]); apiKeyInput = ''; } catch (e) { saveError = (e as { code?: string }).code ?? 'save_failed'; @@ -89,28 +95,40 @@ } // Disconnect typed-confirm modal. Requires the literal "DISCONNECT" string - // to avoid muscle-memory clearing of an integration the operator depends on. + // (whitespace-trimmed) to avoid muscle-memory clearing of an integration the + // operator depends on. let modalOpen = $state(false); let disconnectInput = $state(''); - const canDisconnect = $derived(disconnectInput === 'DISCONNECT'); + let disconnectError = $state(null); + const canDisconnect = $derived(disconnectInput.trim() === 'DISCONNECT'); async function onConfirmDisconnect() { if (!canDisconnect) return; - await putLidarrConfig({ - enabled: false, - base_url: '', - api_key: '', - default_quality_profile_id: 0, - default_root_folder_path: '' - }); - await client.invalidateQueries({ queryKey: qk.lidarrConfig() }); - modalOpen = false; - disconnectInput = ''; + disconnectError = null; + try { + await putLidarrConfig({ + enabled: false, + base_url: '', + api_key: '', + default_quality_profile_id: 0, + default_root_folder_path: '' + }); + await Promise.all([ + client.invalidateQueries({ queryKey: qk.lidarrConfig() }), + client.invalidateQueries({ queryKey: qk.lidarrQualityProfiles() }), + client.invalidateQueries({ queryKey: qk.lidarrRootFolders() }) + ]); + modalOpen = false; + disconnectInput = ''; + } catch (e) { + disconnectError = (e as { code?: string }).code ?? 'disconnect_failed'; + } } function cancelDisconnect() { modalOpen = false; disconnectInput = ''; + disconnectError = null; } @@ -289,6 +307,9 @@ aria-label="Type DISCONNECT to confirm" class="mt-3 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent" /> + {#if disconnectError} +

Disconnect failed — {disconnectError}

+ {/if}