feat(ui): v0.16 features + dark mode across every page
Wires up everything from the previous commits into actual UI surfaces, and applies Tailwind dark: variants throughout. All pages now support the 'system' / 'light' / 'dark' preference set in the onboarding step or in Mein Konto → Design. Layout & nav: - routes/+layout.svelte: initTheme(), global pin-reset SSE handler that filters by user_id and calls clearPin(), one-shot /me/context fetch on boot to hydrate privacyNote + quota. - components/BottomNav.svelte: dark variants on the frosted-glass bar. - components/UploadSheet.svelte: dark variants on backdrop, sheet, source buttons. - components/OnboardingGuide.svelte: new "Helles oder dunkles Design?" step (3-option custom-radio grid), reactive currentStep with proper type narrowing, dark variants throughout. Privacy-note nudge appears on the PIN step only when one is configured. Feed: - routes/feed/+page.svelte: diashow entry icon (tablet/desktop only), long-press → ContextSheet (Löschen for own posts, Original anzeigen for all), upload-deleted + feed-delta SSE handlers, dark variants on header, search, autocomplete, filter chips, empty states. - components/FeedListCard.svelte: long-press wireup, double-tap-to-like, data-mode-aware mediaSrc via pickMediaUrl, kebab fallback for desktop, isOwn prop, dark variants. - components/FeedGrid.svelte: long-press wireup, dark variants. - components/LightboxModal.svelte: data-mode-aware src, double-tap heart burst, dark variants on card / comments / input. - components/HashtagChips.svelte: dark variants. Account: - routes/account/+page.svelte: theme picker (3-button radio grid), data mode picker (with confirm sheet for Original), live quota widget, preformatted Datenschutzhinweis block, diashow tile (mobile only), pin now sourced from the $currentPin store so a global pin-reset clears it live, clearQueue() on explicit logout, dark variants across every card + both bottom sheets. Upload: - routes/upload/+page.svelte: per-user quota progress bar above the submit button, dark variants. Host & Admin: - routes/host/+page.svelte: PIN-reset confirm + one-time PIN modal, hosts may demote other hosts, canResetPinFor() helper, dark variants on all cards, modals, stats, toast. - routes/admin/+page.svelte: Config form rebuilt as CONFIG_GROUPS with per-field kind (number / bool / text), renders toggles for the rate-limit + quota switches and a textarea for the privacy_note; Nutzer tab gains PIN reset + hosts-may-demote-hosts wiring; same one-time PIN modal; dark variants everywhere. - routes/admin/login/+page.svelte: dark variants. Join / Recover / Export: - routes/join/+page.svelte: rename inline link to "Ich habe bereits einen Account", dark variants. - routes/recover/+page.svelte: dark variants. - routes/export/+page.svelte: dark variants on status cards + HTML guide modal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -86,20 +86,20 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex min-h-screen items-center justify-center bg-gray-50 px-4">
|
||||
<div class="flex min-h-screen items-center justify-center bg-gray-50 px-4 dark:bg-gray-950">
|
||||
<div class="w-full max-w-sm">
|
||||
|
||||
{#if nameTaken}
|
||||
<!-- Name-taken state: sign in with PIN or choose a different name -->
|
||||
<div class="mb-5 rounded-lg border border-amber-200 bg-amber-50 p-4">
|
||||
<p class="font-semibold text-amber-900">„{takenName}" ist bereits vergeben.</p>
|
||||
<p class="mt-1 text-sm text-amber-800">
|
||||
<div class="mb-5 rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-800/60 dark:bg-amber-950/30">
|
||||
<p class="font-semibold text-amber-900 dark:text-amber-200">„{takenName}" ist bereits vergeben.</p>
|
||||
<p class="mt-1 text-sm text-amber-800 dark:text-amber-300/90">
|
||||
Wähle einen anderen Namen, z. B. einen Spitznamen oder füge deinen Nachnamen hinzu
|
||||
(„{takenName} M." oder „{takenName} aus Berlin").
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="mb-3 text-sm font-medium text-gray-700">
|
||||
<p class="mb-3 text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Falls du das bist, melde dich mit deinem PIN an:
|
||||
</p>
|
||||
|
||||
@@ -111,17 +111,17 @@
|
||||
maxlength={4}
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]*"
|
||||
class="mb-3 w-full rounded-lg border border-gray-300 px-4 py-3 text-center text-2xl font-mono tracking-widest focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200"
|
||||
class="mb-3 w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-center text-2xl font-mono tracking-widest text-gray-900 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100"
|
||||
/>
|
||||
|
||||
{#if recoveryError}
|
||||
<p class="mb-3 text-sm text-red-600">{recoveryError}</p>
|
||||
<p class="mb-3 text-sm text-red-600 dark:text-red-400">{recoveryError}</p>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={recoveryLoading || recoveryPin.length < 4}
|
||||
class="mb-3 w-full rounded-lg bg-blue-600 px-4 py-3 font-medium text-white transition hover:bg-blue-700 disabled:opacity-50"
|
||||
class="mb-3 w-full rounded-lg bg-blue-600 px-4 py-3 font-medium text-white transition hover:bg-blue-700 disabled:opacity-50 dark:bg-blue-500 dark:hover:bg-blue-400"
|
||||
>
|
||||
{recoveryLoading ? 'Wird angemeldet...' : 'Anmelden'}
|
||||
</button>
|
||||
@@ -129,15 +129,15 @@
|
||||
|
||||
<button
|
||||
onclick={tryDifferentName}
|
||||
class="w-full rounded-lg border border-gray-300 px-4 py-3 font-medium text-gray-700 transition hover:bg-gray-50"
|
||||
class="w-full rounded-lg border border-gray-300 px-4 py-3 font-medium text-gray-700 transition hover:bg-gray-50 dark:border-gray-700 dark:text-gray-300 dark:hover:bg-gray-800"
|
||||
>
|
||||
Anderen Namen wählen
|
||||
</button>
|
||||
|
||||
{:else}
|
||||
<!-- Normal join form -->
|
||||
<h1 class="mb-2 text-center text-2xl font-bold text-gray-900">Willkommen!</h1>
|
||||
<p class="mb-6 text-center text-gray-600">Gib deinen Namen ein, um dem Event beizutreten.</p>
|
||||
<h1 class="mb-2 text-center text-2xl font-bold text-gray-900 dark:text-gray-100">Willkommen!</h1>
|
||||
<p class="mb-6 text-center text-gray-600 dark:text-gray-400">Gib deinen Namen ein, um dem Event beizutreten.</p>
|
||||
|
||||
<form onsubmit={(e) => { e.preventDefault(); handleJoin(); }}>
|
||||
<input
|
||||
@@ -145,25 +145,24 @@
|
||||
bind:value={displayName}
|
||||
placeholder="Dein Name"
|
||||
maxlength={50}
|
||||
class="mb-3 w-full rounded-lg border border-gray-300 px-4 py-3 text-lg focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200"
|
||||
class="mb-3 w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-lg text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
||||
/>
|
||||
|
||||
{#if error}
|
||||
<p class="mb-3 text-sm text-red-600">{error}</p>
|
||||
<p class="mb-3 text-sm text-red-600 dark:text-red-400">{error}</p>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !displayName.trim()}
|
||||
class="w-full rounded-lg bg-blue-600 px-4 py-3 text-lg font-medium text-white transition hover:bg-blue-700 disabled:opacity-50"
|
||||
class="w-full rounded-lg bg-blue-600 px-4 py-3 text-lg font-medium text-white transition hover:bg-blue-700 disabled:opacity-50 dark:bg-blue-500 dark:hover:bg-blue-400"
|
||||
>
|
||||
{loading ? 'Wird geladen...' : 'Beitreten'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p class="mt-4 text-center text-sm text-gray-500">
|
||||
Schon dabei?
|
||||
<a href="/recover" class="text-blue-600 hover:underline">Mit PIN wiederherstellen</a>
|
||||
<p class="mt-4 text-center text-sm">
|
||||
<a href="/recover" class="text-blue-600 hover:underline dark:text-blue-400">Ich habe bereits einen Account</a>
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
@@ -172,17 +171,17 @@
|
||||
|
||||
{#if showPinModal}
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4">
|
||||
<div class="w-full max-w-sm rounded-xl bg-white p-6 shadow-lg">
|
||||
<h2 class="mb-2 text-xl font-bold text-gray-900">Dein Wiederherstellungs-PIN</h2>
|
||||
<p class="mb-4 text-sm text-gray-600">
|
||||
<div class="w-full max-w-sm rounded-xl bg-white p-6 shadow-lg dark:bg-gray-900">
|
||||
<h2 class="mb-2 text-xl font-bold text-gray-900 dark:text-gray-100">Dein Wiederherstellungs-PIN</h2>
|
||||
<p class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||
Merke dir diesen PIN! Du brauchst ihn, um dein Konto auf einem anderen Gerät wiederherzustellen.
|
||||
</p>
|
||||
|
||||
<div class="mb-4 flex items-center justify-center gap-3 rounded-lg bg-gray-100 p-4">
|
||||
<span class="text-4xl font-mono font-bold tracking-widest text-gray-900">{pin}</span>
|
||||
<div class="mb-4 flex items-center justify-center gap-3 rounded-lg bg-gray-100 p-4 dark:bg-gray-800">
|
||||
<span class="text-4xl font-mono font-bold tracking-widest text-gray-900 dark:text-gray-100">{pin}</span>
|
||||
<button
|
||||
onclick={copyPin}
|
||||
class="rounded-md bg-gray-200 px-3 py-1 text-sm font-medium text-gray-700 hover:bg-gray-300"
|
||||
class="rounded-md bg-gray-200 px-3 py-1 text-sm font-medium text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600"
|
||||
>
|
||||
{copied ? 'Kopiert!' : 'Kopieren'}
|
||||
</button>
|
||||
@@ -190,7 +189,7 @@
|
||||
|
||||
<button
|
||||
onclick={goToFeed}
|
||||
class="w-full rounded-lg bg-blue-600 px-4 py-3 font-medium text-white transition hover:bg-blue-700"
|
||||
class="w-full rounded-lg bg-blue-600 px-4 py-3 font-medium text-white transition hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-400"
|
||||
>
|
||||
Weiter zur Galerie
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user