Einstellungen per Zahnrad erreichbar, verwirrenden Sync-Punkt entfernt [apk]

- AppHeader: farbiger Sync-Ampel-Punkt raus (war nicht selbsterklaerend),
  stattdessen Zahnrad-Icon -> Einstellungen, von jeder Seite erreichbar
- Einstellungen neu gegliedert: App (Version + Update-Button) zuerst,
  dann Synchronisierung (Status in Klartext + manueller Sync), Konto, Abmelden

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-05-19 16:30:29 +02:00
parent 280a973476
commit fca59545cc
2 changed files with 70 additions and 49 deletions

View file

@ -1,7 +1,6 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { sync } from '$lib/sync.svelte';
import { ChevronLeft, RefreshCw } from 'lucide-svelte';
import { page } from '$app/stores';
import { ChevronLeft, Settings } from 'lucide-svelte';
let {
title,
@ -9,14 +8,8 @@
subtitle = '',
}: { title: string; back?: boolean; subtitle?: string } = $props();
// Sync-Ampel: grün=ok, gelb=läuft/offen, rot=Fehler
const dot = $derived(
sync.status === 'error'
? 'bg-red-500'
: sync.pendingCount > 0 || sync.status === 'syncing'
? 'bg-amber-400'
: 'bg-emerald-500',
);
// Zahnrad auf der Einstellungs-Seite selbst ausblenden
const onSettings = $derived($page.url.pathname.startsWith('/einstellungen'));
</script>
<header class="flex items-center gap-2 border-b border-zinc-800 bg-zinc-900 px-3 pb-3 safe-top">
@ -31,13 +24,13 @@
<p class="truncate text-xs text-zinc-400">{subtitle}</p>
{/if}
</div>
<button
class="flex items-center gap-1 rounded px-2 py-1 text-xs text-zinc-300 active:bg-zinc-800"
onclick={() => sync.syncManual()}
title="Synchronisieren"
>
<span class="h-2.5 w-2.5 rounded-full {dot}"></span>
{#if sync.pendingCount > 0}<span>{sync.pendingCount}</span>{/if}
<RefreshCw size={14} class={sync.status === 'syncing' ? 'animate-spin' : ''} />
</button>
{#if !onSettings}
<a
class="rounded p-1.5 text-zinc-300 active:bg-zinc-800"
href="/einstellungen/"
aria-label="Einstellungen"
>
<Settings size={22} />
</a>
{/if}
</header>

View file

@ -9,11 +9,18 @@
let checking = $state(false);
async function logout() {
await auth.logout();
sync.stop();
goto('/login/');
}
// Sync-Status in Klartext
const syncLabel = $derived(
sync.status === 'syncing'
? 'Wird synchronisiert …'
: sync.status === 'error'
? 'Letzter Sync fehlgeschlagen'
: sync.status === 'offline'
? 'Offline'
: sync.pendingCount > 0
? `${sync.pendingCount} Protokoll(e) noch nicht übertragen`
: 'Alles synchronisiert',
);
async function checkUpdate() {
checking = true;
@ -22,11 +29,52 @@
if (upd) openUpdate(upd);
else toast.show('App ist aktuell', 'success');
}
async function logout() {
await auth.logout();
sync.stop();
goto('/login/');
}
</script>
<AppHeader title="Einstellungen" back />
<div class="flex flex-1 flex-col gap-4 overflow-y-auto p-4">
<!-- App / Version / Update -->
<section class="rounded-lg bg-zinc-900 p-4">
<h2 class="mb-2 text-sm font-semibold text-zinc-300">App</h2>
<p class="text-sm">
Version <span class="font-mono text-zinc-400">{APP_VERSION}</span>
</p>
<p class="mt-0.5 text-xs text-zinc-500">
Der Zeitstempel zeigt, welcher Build installiert ist.
</p>
<button
class="mt-3 w-full rounded bg-zinc-800 px-3 py-2 text-sm active:bg-zinc-700 disabled:opacity-50"
onclick={checkUpdate}
disabled={checking}
>
{checking ? 'Prüfe …' : 'Auf Update prüfen'}
</button>
</section>
<!-- Synchronisierung -->
<section class="rounded-lg bg-zinc-900 p-4">
<h2 class="mb-2 text-sm font-semibold text-zinc-300">Synchronisierung</h2>
<p class="text-sm text-zinc-300">{syncLabel}</p>
<p class="mt-0.5 break-all text-xs text-zinc-500">
Server: {getServerUrl() || '(Browser-Proxy)'}
</p>
<button
class="mt-3 w-full rounded bg-zinc-800 px-3 py-2 text-sm active:bg-zinc-700 disabled:opacity-50"
onclick={() => sync.syncManual()}
disabled={sync.status === 'syncing'}
>
Jetzt synchronisieren
</button>
</section>
<!-- Konto -->
<section class="rounded-lg bg-zinc-900 p-4">
<h2 class="mb-2 text-sm font-semibold text-zinc-300">Konto</h2>
<p class="text-sm">{auth.user?.name ?? '—'}</p>
@ -36,30 +84,10 @@
</p>
</section>
<section class="rounded-lg bg-zinc-900 p-4">
<h2 class="mb-2 text-sm font-semibold text-zinc-300">Server</h2>
<p class="break-all text-sm text-zinc-400">{getServerUrl() || '(Browser-Proxy)'}</p>
<p class="mt-2 text-xs text-zinc-500">
Offene Protokolle: {sync.pendingCount} · Status: {sync.status}
</p>
<button class="mt-2 rounded bg-zinc-800 px-3 py-1.5 text-sm" onclick={() => sync.syncNow()}>
Jetzt synchronisieren
</button>
</section>
<section class="rounded-lg bg-zinc-900 p-4">
<h2 class="mb-2 text-sm font-semibold text-zinc-300">App</h2>
<p class="text-sm text-zinc-400">Version {APP_VERSION}</p>
<button
class="mt-2 rounded bg-zinc-800 px-3 py-1.5 text-sm"
onclick={checkUpdate}
disabled={checking}
>
{checking ? 'Prüfe …' : 'Auf Update prüfen'}
</button>
</section>
<button class="mt-2 rounded-lg bg-red-700 py-2.5 font-semibold text-white" onclick={logout}>
<button
class="mt-2 rounded-lg bg-red-700 py-2.5 font-semibold text-white active:bg-red-800"
onclick={logout}
>
Abmelden
</button>
</div>