fix: dashboard provider card shows filtered count, fix provider update 400

- Dashboard providers card now shows count of providers matching the
  global provider type filter instead of special name/type display
- Fix provider update sending empty config when only name/icon changed,
  causing 400 validation error (api_key required)
This commit is contained in:
2026-03-23 21:30:25 +03:00
parent 4049efe186
commit 0702ec72af
2 changed files with 16 additions and 13 deletions
+8 -12
View File
@@ -177,12 +177,12 @@
let displayCommandTrackers = $state(0); let displayCommandTrackers = $state(0);
const filteredProvider = $derived(globalProviderFilter.id ? providers.find(p => p.id === globalProviderFilter.id) : null); const filteredProviderCount = $derived(globalProviderFilter.providerType
? providers.filter(p => p.type === globalProviderFilter.providerType).length
: displayProviders);
const statCards = $derived(status ? [ const statCards = $derived(status ? [
filteredProvider { icon: 'mdiServer', label: 'dashboard.providers', value: filteredProviderCount, color: '#0d9488' },
? { icon: providerDefaultIcon(filteredProvider), label: filteredProvider.name, value: filteredProvider.type, color: '#0d9488', isProvider: true }
: { icon: 'mdiServer', label: 'dashboard.providers', value: displayProviders, color: '#0d9488' },
{ icon: 'mdiRadar', label: 'dashboard.activeTrackers', value: displayActive, suffix: ` / ${displayTotal}`, color: '#6366f1' }, { icon: 'mdiRadar', label: 'dashboard.activeTrackers', value: displayActive, suffix: ` / ${displayTotal}`, color: '#6366f1' },
{ icon: 'mdiTarget', label: 'dashboard.targets', value: displayTargets, color: '#f59e0b' }, { icon: 'mdiTarget', label: 'dashboard.targets', value: displayTargets, color: '#f59e0b' },
...(status.command_trackers !== undefined ? [{ icon: 'mdiConsoleLine', label: 'nav.commandTrackers', value: displayCommandTrackers, color: '#8b5cf6' }] : []), ...(status.command_trackers !== undefined ? [{ icon: 'mdiConsoleLine', label: 'nav.commandTrackers', value: displayCommandTrackers, color: '#8b5cf6' }] : []),
@@ -238,14 +238,10 @@
<MdiIcon name={card.icon} size={22} /> <MdiIcon name={card.icon} size={22} />
</div> </div>
<div> <div>
<p class="text-sm" style="color: var(--color-muted-foreground);">{card.isProvider ? card.label : t(card.label)}</p> <p class="text-sm" style="color: var(--color-muted-foreground);">{t(card.label)}</p>
{#if card.isProvider} <p class="stat-value font-mono" style="animation-delay: {i * 80 + 200}ms;">
<p class="text-lg font-medium" style="color: {card.color};">{card.value}</p> {card.value}{#if card.suffix}<span class="stat-suffix">{card.suffix}</span>{/if}
{:else} </p>
<p class="stat-value font-mono" style="animation-delay: {i * 80 + 200}ms;">
{card.value}{#if card.suffix}<span class="stat-suffix">{card.suffix}</span>{/if}
</p>
{/if}
</div> </div>
</div> </div>
</div> </div>
+8 -1
View File
@@ -106,7 +106,14 @@
} }
} }
if (editing) { if (editing) {
await api(`/providers/${editing}`, { method: 'PUT', body: JSON.stringify({ name: form.name, icon: form.icon, config }) }); // Only send config if user changed a config field (secrets are blank on edit)
const hasConfigChange = form.url !== (providers.find(p => p.id === editing)?.config?.url || '') ||
(form.type === 'immich' && (form.api_key || form.external_domain !== (providers.find(p => p.id === editing)?.config?.external_domain || ''))) ||
(form.type === 'gitea' && (form.api_token || form.webhook_secret)) ||
(form.type === 'planka' && (form.api_key || form.webhook_secret));
const body: any = { name: form.name, icon: form.icon };
if (hasConfigChange) body.config = config;
await api(`/providers/${editing}`, { method: 'PUT', body: JSON.stringify(body) });
} else { } else {
await api('/providers', { method: 'POST', body: JSON.stringify({ type: form.type, name: form.name, icon: form.icon, config }) }); await api('/providers', { method: 'POST', body: JSON.stringify({ type: form.type, name: form.name, icon: form.icon, config }) });
} }