refactor: comprehensive consistency review — UI/UX, code quality, functional parity

Fix 19 issues across 3 priority tiers found during full-codebase review:

CRITICAL:
- Fix undefined --color-secondary CSS variable causing invisible UI elements
- Fix Google Photos command templates using nonexistent asset.originalFileName
- Fix scheduler template variable docs (tracker_name → schedule_name)
- Add missing admin guard on notification template update endpoint

HIGH:
- Fix 5 hardcoded English strings missing i18n (MultiEntitySelect, actions,
  settings, TelegramBotTab, users)
- Replace 17 raw <button> elements with shared <Button> component
- Replace 5 raw error divs with shared <ErrorBanner> component
- Refactor webhook handler duplication into shared _dispatch_webhook_event()
- Add 30+ provider-specific fields to TrackingConfig TypeScript type
- Add default TrackingConfig seeds for immich and google_photos
- Add provider-specific command variable docs (Gitea, Planka, NUT, GP, Webhook)

MEDIUM:
- Replace hardcoded hex colors and Tailwind classes with CSS variable tokens
- Remove dead code (unused imports, orphaned check_notification_tracker)
- Fix Svelte 5 patterns ($state for _prevProviderId, remove unnecessary as any)
- Fix inconsistent POST response shape (targets now returns full response)
- Fix Google Photos template dead asset.year branches, clarify album_url docs
This commit is contained in:
2026-03-31 23:27:35 +03:00
parent 6113a0039c
commit 6e51164f8e
29 changed files with 501 additions and 309 deletions
@@ -20,7 +20,8 @@
loading = true;
try {
logs = await api(`/providers/${providerId}/webhook-logs`);
} catch {
} catch (e) {
console.error('Failed to load webhook logs', e);
logs = [];
}
loading = false;
@@ -30,7 +31,7 @@
try {
await api(`/providers/${providerId}/webhook-logs`, { method: 'DELETE' });
logs = [];
} catch { /* ignore */ }
} catch (e) { console.error('Failed to clear webhook logs', e); }
showClearConfirm = false;
}
@@ -39,9 +40,9 @@
}
function statusColor(status: string): string {
if (status === 'matched') return '#059669';
if (status === 'unmatched') return '#f59e0b';
return '#ef4444';
if (status === 'matched') return 'var(--color-success-fg)';
if (status === 'unmatched') return 'var(--color-warning-fg)';
return 'var(--color-error-fg)';
}
function statusIcon(status: string): string {
@@ -71,7 +72,7 @@
<div class="flex items-center gap-2">
<h3 class="text-sm font-semibold">{t('webhookLogs.title')}</h3>
{#if logs.length > 0}
<span class="text-xs px-1.5 py-0.5 rounded-full bg-[var(--color-secondary)] text-[var(--color-secondary-foreground)]">{logs.length}</span>
<span class="text-xs px-1.5 py-0.5 rounded-full bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{logs.length}</span>
{/if}
</div>
{#if logs.length > 0}
@@ -99,7 +100,7 @@
<div class="flex items-center gap-2">
<MdiIcon name={expandedId === log.id ? 'mdiChevronDown' : 'mdiChevronRight'} size={14} />
<span class="text-xs text-[var(--color-muted-foreground)] tabular-nums">{formatTime(log.created_at)}</span>
<span class="text-xs font-mono px-1 py-0.5 rounded bg-[var(--color-secondary)]">{log.method}</span>
<span class="text-xs font-mono px-1 py-0.5 rounded bg-[var(--color-muted)]">{log.method}</span>
<span class="flex items-center gap-1" style="color: {statusColor(log.status)};">
<MdiIcon name={statusIcon(log.status)} size={14} />
<span class="text-xs font-medium">{statusLabel(log.status)}</span>