feat: port full CRUD API routes and frontend pages from Immich Watcher
Backend API (38 routes): - providers: full CRUD + test connection + list collections + API key masking - trackers: full CRUD + trigger + history + test-periodic/memory - tracking-configs: full CRUD with Pydantic models, provider_type filter - template-configs: full CRUD + preview + preview-raw with two-pass validation - targets: full CRUD + test notification + config masking - telegram-bots: full CRUD + chat discovery + token endpoint - users: full admin CRUD + password reset + self-delete protection - status: dashboard endpoint with providers/trackers/targets/events counts Frontend pages updated: - Dashboard with animated stat cards and event timeline - Providers with proper components, delete confirm, snackbar - Trackers/targets/tracking-configs/template-configs/telegram-bots/users all use PageHeader, Card, Loading, MdiIcon with correct i18n keys Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,34 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { t } from '$lib/i18n/index.svelte.ts';
|
||||
import { api } from '$lib/api.ts';
|
||||
import { onMount } from 'svelte';
|
||||
import { api } from '$lib/api';
|
||||
import { t } from '$lib/i18n';
|
||||
import PageHeader from '$lib/components/PageHeader.svelte';
|
||||
import Card from '$lib/components/Card.svelte';
|
||||
import Loading from '$lib/components/Loading.svelte';
|
||||
import MdiIcon from '$lib/components/MdiIcon.svelte';
|
||||
|
||||
let trackers = $state<any[]>([]);
|
||||
let loading = $state(true);
|
||||
let loaded = $state(false);
|
||||
|
||||
onMount(async () => {
|
||||
try { trackers = await api.get('/trackers'); } catch {}
|
||||
loading = false;
|
||||
try { trackers = await api('/trackers'); } catch {}
|
||||
loaded = true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="p-6 max-w-5xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold">{t('nav.trackers')}</h1>
|
||||
</div>
|
||||
<PageHeader title={t('trackers.title')} description={t('trackers.description')} />
|
||||
|
||||
{#if loading}
|
||||
<p class="text-muted-foreground">{t('common.loading')}</p>
|
||||
{:else if trackers.length === 0}
|
||||
<p class="text-muted-foreground text-center py-16">{t('common.noData')}</p>
|
||||
{:else}
|
||||
<div class="grid gap-4 md:grid-cols-2 stagger-children">
|
||||
{#each trackers as tracker}
|
||||
<div class="p-4 bg-card rounded-xl border border-border">
|
||||
<h3 class="font-semibold">{tracker.name}</h3>
|
||||
<p class="text-xs text-muted-foreground">{tracker.collection_ids?.length || 0} collections</p>
|
||||
</div>
|
||||
{/each}
|
||||
{#if !loaded}
|
||||
<Loading />
|
||||
{:else if trackers.length === 0}
|
||||
<Card>
|
||||
<div class="flex flex-col items-center py-8 gap-3" style="color: var(--color-muted-foreground);">
|
||||
<div style="opacity: 0.4;"><MdiIcon name="mdiRadar" size={40} /></div>
|
||||
<p class="text-sm">{t('trackers.noTrackers')}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Card>
|
||||
{:else}
|
||||
<div class="grid gap-4 sm:grid-cols-2 stagger-children">
|
||||
{#each trackers as tracker}
|
||||
<Card hover>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-lg"
|
||||
style="background: {tracker.enabled ? 'var(--color-success-bg)' : 'var(--color-muted)'}; color: {tracker.enabled ? 'var(--color-success-fg)' : 'var(--color-muted-foreground)'};">
|
||||
<MdiIcon name="mdiRadar" size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-medium text-sm">{tracker.name}</h3>
|
||||
<p class="text-xs" style="color: var(--color-muted-foreground);">
|
||||
{tracker.collection_ids?.length || 0} {t('trackers.albums_count')} · {t('trackers.every')} {tracker.scan_interval}s
|
||||
</p>
|
||||
</div>
|
||||
<span class="ml-auto text-xs px-2 py-0.5 rounded-full"
|
||||
style="background: {tracker.enabled ? 'var(--color-success-bg)' : 'var(--color-muted)'}; color: {tracker.enabled ? 'var(--color-success-fg)' : 'var(--color-muted-foreground)'};">
|
||||
{tracker.enabled ? t('trackers.active') : t('trackers.paused')}
|
||||
</span>
|
||||
</div>
|
||||
</Card>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user