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>
77 lines
3.1 KiB
Svelte
77 lines
3.1 KiB
Svelte
<script lang="ts">
|
|
import { goto } from '$app/navigation';
|
|
import { onMount } from 'svelte';
|
|
import { api } from '$lib/api';
|
|
import { login } from '$lib/auth.svelte';
|
|
import { t, initLocale, getLocale, setLocale } from '$lib/i18n';
|
|
import { initTheme, getTheme, setTheme, type Theme } from '$lib/theme.svelte';
|
|
|
|
const theme = getTheme();
|
|
let username = $state('');
|
|
let password = $state('');
|
|
let error = $state('');
|
|
let submitting = $state(false);
|
|
|
|
onMount(async () => {
|
|
initLocale();
|
|
initTheme();
|
|
try {
|
|
const res = await api<{ needs_setup: boolean }>('/auth/needs-setup');
|
|
if (res.needs_setup) goto('/setup');
|
|
} catch { /* ignore */ }
|
|
});
|
|
|
|
async function handleSubmit(e: SubmitEvent) {
|
|
e.preventDefault();
|
|
error = '';
|
|
submitting = true;
|
|
try {
|
|
await login(username, password);
|
|
goto('/');
|
|
} catch (err: any) {
|
|
error = err.message || 'Login failed';
|
|
}
|
|
submitting = false;
|
|
}
|
|
</script>
|
|
|
|
<div class="min-h-screen flex items-center justify-center bg-[var(--color-background)]">
|
|
<div class="w-full max-w-sm">
|
|
<div class="bg-[var(--color-card)] border border-[var(--color-border)] rounded-lg p-6 shadow-sm">
|
|
<div class="flex justify-end gap-1 mb-4">
|
|
<button onclick={() => { setLocale(getLocale() === 'en' ? 'ru' : 'en'); window.location.reload(); }}
|
|
class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">
|
|
{getLocale().toUpperCase()}
|
|
</button>
|
|
<button onclick={() => { const o: Theme[] = ['light','dark','system']; setTheme(o[(o.indexOf(theme.current)+1)%3]); }}
|
|
class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">
|
|
{theme.resolved === 'dark' ? '🌙' : '☀️'}
|
|
</button>
|
|
</div>
|
|
<h1 class="text-xl font-semibold text-center mb-1">{t('app.name')}</h1>
|
|
<p class="text-sm text-[var(--color-muted-foreground)] text-center mb-6">{t('auth.signInTitle')}</p>
|
|
|
|
{#if error}
|
|
<div class="bg-[var(--color-error-bg)] text-[var(--color-error-fg)] text-sm rounded-md p-3 mb-4">{error}</div>
|
|
{/if}
|
|
|
|
<form onsubmit={handleSubmit} class="space-y-4">
|
|
<div>
|
|
<label for="username" class="block text-sm font-medium mb-1.5">{t('auth.username')}</label>
|
|
<input id="username" type="text" bind:value={username} required
|
|
class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--color-background)]" />
|
|
</div>
|
|
<div>
|
|
<label for="password" class="block text-sm font-medium mb-1.5">{t('auth.password')}</label>
|
|
<input id="password" type="password" bind:value={password} required
|
|
class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--color-background)]" />
|
|
</div>
|
|
<button type="submit" disabled={submitting}
|
|
class="w-full py-2 px-4 bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-md text-sm font-medium hover:opacity-90 transition-opacity disabled:opacity-50">
|
|
{submitting ? t('auth.signingIn') : t('auth.signIn')}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|