Add frontend for TrackingConfig + TemplateConfig, fix locale, simplify trackers
Some checks failed
Validate / Hassfest (push) Has been cancelled
Some checks failed
Validate / Hassfest (push) Has been cancelled
New pages: - /tracking-configs: Full CRUD with event tracking, asset display, periodic summary, scheduled assets, and memory mode sections. Collapsible sub-sections that show/hide based on enabled state. - /template-configs: Full CRUD with all 21 template slots organized into 5 fieldsets (event messages, asset formatting, date/location, scheduled messages, telegram). Preview support per slot. Updated pages: - Targets: added tracking_config_id + template_config_id selectors (dropdowns populated from configs). Configs are reusable. - Trackers: simplified to album selection + scan interval + targets. Added Test, Test Periodic, Test Memory buttons per tracker. - Nav: replaced Templates with Tracking + Templates config links Other fixes: - Language button: now triggers window.location.reload() to force all child pages to re-evaluate t() calls - Dark theme buttons: changed primary color to dark gray in dark mode - Removed old /templates page (replaced by /template-configs) - Added .gitignore for __pycache__ in server package Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,11 +14,8 @@
|
||||
let showForm = $state(false);
|
||||
let editing = $state<number | null>(null);
|
||||
const defaultForm = () => ({
|
||||
name: '', server_id: 0, album_ids: [] as string[], event_types: ['assets_added'],
|
||||
name: '', server_id: 0, album_ids: [] as string[],
|
||||
target_ids: [] as number[], scan_interval: 60,
|
||||
track_images: true, track_videos: true, notify_favorites_only: false,
|
||||
include_people: true, include_asset_details: false,
|
||||
max_assets_to_show: 5, assets_order_by: 'none', assets_order: 'descending',
|
||||
});
|
||||
let form = $state(defaultForm());
|
||||
let error = $state('');
|
||||
@@ -33,11 +30,7 @@
|
||||
async function edit(trk: any) {
|
||||
form = {
|
||||
name: trk.name, server_id: trk.server_id, album_ids: [...trk.album_ids],
|
||||
event_types: [...trk.event_types], target_ids: [...trk.target_ids], scan_interval: trk.scan_interval,
|
||||
track_images: trk.track_images ?? true, track_videos: trk.track_videos ?? true,
|
||||
notify_favorites_only: trk.notify_favorites_only ?? false, include_people: trk.include_people ?? true,
|
||||
include_asset_details: trk.include_asset_details ?? false, max_assets_to_show: trk.max_assets_to_show ?? 5,
|
||||
assets_order_by: trk.assets_order_by ?? 'none', assets_order: trk.assets_order ?? 'descending',
|
||||
target_ids: [...trk.target_ids], scan_interval: trk.scan_interval,
|
||||
};
|
||||
editing = trk.id; showForm = true;
|
||||
if (form.server_id) await loadAlbums();
|
||||
@@ -103,57 +96,8 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{t('trackers.eventTypes')}</label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each ['assets_added', 'assets_removed', 'album_renamed', 'album_sharing_changed', 'changed'] as evt}
|
||||
<label class="flex items-center gap-1 text-sm">
|
||||
<input type="checkbox" checked={form.event_types.includes(evt)}
|
||||
onchange={() => form.event_types = form.event_types.includes(evt) ? form.event_types.filter(e => e !== evt) : [...form.event_types, evt]} />
|
||||
{evt}
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset filtering -->
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.track_images} /> {t('trackers.trackImages')}</label>
|
||||
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.track_videos} /> {t('trackers.trackVideos')}</label>
|
||||
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.notify_favorites_only} /> {t('trackers.favoritesOnly')}</label>
|
||||
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.include_people} /> {t('trackers.includePeople')}</label>
|
||||
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.include_asset_details} /> {t('trackers.includeAssetDetails')}</label>
|
||||
</div>
|
||||
|
||||
<!-- Sorting -->
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label for="trk-sort" class="block text-sm font-medium mb-1">{t('trackers.sortBy')}</label>
|
||||
<select id="trk-sort" bind:value={form.assets_order_by} class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]">
|
||||
<option value="none">{t('trackers.sortNone')}</option>
|
||||
<option value="date">{t('trackers.sortDate')}</option>
|
||||
<option value="rating">{t('trackers.sortRating')}</option>
|
||||
<option value="name">{t('trackers.sortName')}</option>
|
||||
<option value="random">{t('trackers.sortRandom')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="trk-order" class="block text-sm font-medium mb-1">{t('trackers.sortOrder')}</label>
|
||||
<select id="trk-order" bind:value={form.assets_order} class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]">
|
||||
<option value="descending">{t('trackers.descending')}</option>
|
||||
<option value="ascending">{t('trackers.ascending')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label for="trk-max" class="block text-sm font-medium mb-1">{t('trackers.maxAssetsToShow')}</label>
|
||||
<input id="trk-max" type="number" bind:value={form.max_assets_to_show} min="0" max="50" class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="trk-interval" class="block text-sm font-medium mb-1">{t('trackers.scanInterval')}</label>
|
||||
<input id="trk-interval" type="number" bind:value={form.scan_interval} min="10" max="3600" class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||
</div>
|
||||
<label for="trk-interval" class="block text-sm font-medium mb-1">{t('trackers.scanInterval')}</label>
|
||||
<input id="trk-interval" type="number" bind:value={form.scan_interval} min="10" max="3600" class="w-32 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||
</div>
|
||||
|
||||
{#if targets.length > 0}
|
||||
@@ -191,10 +135,13 @@
|
||||
{tracker.enabled ? t('trackers.active') : t('trackers.paused')}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm text-[var(--color-muted-foreground)]">{tracker.album_ids.length} {t('trackers.albums_count')} · {t('trackers.every')} {tracker.scan_interval}s · {tracker.event_types.join(', ')}</p>
|
||||
<p class="text-sm text-[var(--color-muted-foreground)]">{tracker.album_ids.length} {t('trackers.albums_count')} · {t('trackers.every')} {tracker.scan_interval}s · {tracker.target_ids.length} target(s)</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<button onclick={() => edit(tracker)} class="text-xs text-[var(--color-muted-foreground)] hover:underline">{t('common.edit')}</button>
|
||||
<button onclick={async () => { await api(`/trackers/${tracker.id}/trigger`, { method: 'POST' }); }} class="text-xs text-[var(--color-muted-foreground)] hover:underline">{t('common.test')}</button>
|
||||
<button onclick={async () => { await api(`/trackers/${tracker.id}/test-periodic`, { method: 'POST' }); }} class="text-xs text-[var(--color-muted-foreground)] hover:underline">Test Periodic</button>
|
||||
<button onclick={async () => { await api(`/trackers/${tracker.id}/test-memory`, { method: 'POST' }); }} class="text-xs text-[var(--color-muted-foreground)] hover:underline">Test Memory</button>
|
||||
<button onclick={() => toggle(tracker)} class="text-xs text-[var(--color-muted-foreground)] hover:underline">
|
||||
{tracker.enabled ? t('trackers.pause') : t('trackers.resume')}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user