Add i18n (RU/EN), dark/light themes, enhanced tracker/target forms (Phase 7a)
Some checks failed
Validate / Hassfest (push) Has been cancelled
Some checks failed
Validate / Hassfest (push) Has been cancelled
Frontend enhancements: - i18n: Full Russian and English translations (~170 keys each), language switcher in sidebar and login page, auto-detect from browser, persists to localStorage - Themes: Light/dark mode with CSS custom properties, system preference detection, toggle in sidebar header, smooth transitions - Dark theme: Full color palette (background, card, muted, border, success, warning, error variants) Enhanced forms: - Tracker creation: asset type filtering (images/videos), favorites only, include people/details toggles, sort by/order selects, max assets to show - Target creation: Telegram media settings (collapsible) with max media, group size, chunk delay, max asset size, URL preview disable, large photos as documents - Template creation: event_type selector (all/added/removed/renamed/deleted) All pages use t() for translations, var(--color-*) for theme-safe colors, and proper label-for-input associations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,13 +3,18 @@
|
||||
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');
|
||||
@@ -33,40 +38,37 @@
|
||||
<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">
|
||||
<h1 class="text-xl font-semibold text-center mb-1">Immich Watcher</h1>
|
||||
<p class="text-sm text-[var(--color-muted-foreground)] text-center mb-6">Sign in to your account</p>
|
||||
<div class="flex justify-end gap-1 mb-4">
|
||||
<button onclick={() => setLocale(getLocale() === 'en' ? 'ru' : 'en')}
|
||||
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-red-50 text-red-700 text-sm rounded-md p-3 mb-4">{error}</div>
|
||||
<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">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)]"
|
||||
/>
|
||||
<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">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)]"
|
||||
/>
|
||||
<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 ? 'Signing in...' : 'Sign in'}
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user