lernschiff/resources/views/livewire/settings/index.blade.php

211 lines
14 KiB
PHP

@php
$user = auth()->user();
$name = $user?->name ?? '—';
$email = $user?->email ?? '—';
$sections = [
['key' => 'profile', 'label' => 'Profil', 'icon' => 'user-circle'],
['key' => 'language', 'label' => 'Sprache & Region', 'icon' => 'language'],
['key' => 'security', 'label' => 'Sicherheit', 'icon' => 'shield-check'],
['key' => 'notifications', 'label' => 'Benachrichtigungen','icon' => 'bell'],
['key' => 'danger', 'label' => 'Gefahrenzone', 'icon' => 'exclamation-triangle'],
];
@endphp
<div class="flex flex-col gap-6">
<header>
<div class="text-[11px] uppercase tracking-[0.14em] text-ink-3 font-semibold mb-1.5">Konto</div>
<h1 class="text-[28px] leading-tight font-extrabold tracking-tight text-ink-1">Einstellungen</h1>
<p class="text-sm text-ink-2 mt-2 max-w-xl leading-relaxed">Verwalte dein Profil, Sprache, Sicherheit und Benachrichtigungen.</p>
</header>
<div class="grid grid-cols-[200px_1fr] gap-6">
{{-- Section Sub-Nav (server-rendered active state, no Alpine flicker) --}}
<nav class="flex flex-col gap-1" wire:key="settings-subnav">
@foreach($sections as $s)
@php
$isActive = $section === $s['key'];
$cls = 'flex items-center gap-2.5 px-3 py-2 rounded-lg text-sm cursor-pointer transition-all border focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base text-left';
$cls .= $isActive
? ' bg-bg-soft text-ink-1 font-semibold shadow-sm border-line'
: ' text-ink-2 hover:bg-bg-soft border-transparent';
@endphp
<button
type="button"
wire:click="setSection('{{ $s['key'] }}')"
class="{{ $cls }}"
data-testid="settings-nav-{{ $s['key'] }}"
@if($isActive) aria-current="page" @endif
>
<x-dynamic-component :component="'heroicon-o-' . $s['icon']" class="w-4 h-4 shrink-0" />
<span>{{ $s['label'] }}</span>
</button>
@endforeach
</nav>
{{-- Sections (server-renders only the active one, no Alpine flicker) --}}
<div class="flex flex-col gap-5" wire:key="settings-section-{{ $section }}">
@if($section === 'profile')
<x-card class="shadow-sm" data-section="profile">
<h2 class="font-bold text-base text-ink-1 mb-4">Profil</h2>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-ink-2 mb-1.5">Name</label>
<input type="text" value="{{ $name }}" class="w-full px-3.5 py-2.5 bg-bg-soft border border-line rounded-lg text-sm text-ink-1 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
</div>
<div>
<label class="block text-xs font-semibold text-ink-2 mb-1.5">E-Mail-Adresse</label>
<input type="text" value="{{ $email }}" class="w-full px-3.5 py-2.5 bg-bg-soft border border-line rounded-lg text-sm text-ink-1 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
</div>
<div class="col-span-2">
<label class="block text-xs font-semibold text-ink-2 mb-1.5">Über mich</label>
<textarea rows="3" placeholder="Erzähl etwas über dich…" class="w-full px-3.5 py-2.5 bg-bg-soft border border-line rounded-lg text-sm text-ink-1 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"></textarea>
</div>
</div>
<div class="flex justify-end gap-2 mt-5 pt-4 border-t border-line">
<x-btn>Abbrechen</x-btn>
<x-btn variant="primary">Speichern</x-btn>
</div>
</x-card>
@endif
@if($section === 'language')
<x-card class="shadow-sm" data-section="language">
<h2 class="font-bold text-base text-ink-1 mb-4">Sprache & Region</h2>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-ink-2 mb-1.5">Sprache</label>
<select class="w-full px-3.5 py-2.5 bg-bg-soft border border-line rounded-lg text-sm text-ink-1 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<option>Deutsch</option>
<option>English</option>
</select>
</div>
<div>
<label class="block text-xs font-semibold text-ink-2 mb-1.5">Zeitzone</label>
<select class="w-full px-3.5 py-2.5 bg-bg-soft border border-line rounded-lg text-sm text-ink-1 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<option>Europa/Wien (UTC+1)</option>
<option>Europa/Berlin (UTC+1)</option>
<option>Europa/Zürich (UTC+1)</option>
</select>
</div>
</div>
<div class="flex justify-end gap-2 mt-5 pt-4 border-t border-line">
<x-btn variant="primary">Speichern</x-btn>
</div>
</x-card>
@endif
@if($section === 'security')
<x-card class="shadow-sm" data-section="security">
<h2 class="font-bold text-base text-ink-1 mb-4">Sicherheit</h2>
<div class="flex flex-col gap-4" x-data="{ showPw: false, showNewPw: false }">
<div>
<label class="block text-xs font-semibold text-ink-2 mb-1.5">Aktuelles Passwort</label>
<div class="relative">
<input type="password" x-bind:type="showPw ? 'text' : 'password'" class="w-full px-3.5 py-2.5 pr-10 bg-bg-soft border border-line rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<button type="button" @click="showPw = !showPw" class="absolute right-3 top-1/2 -translate-y-1/2 text-ink-3 hover:text-ink-1 transition-colors" aria-label="Passwort anzeigen">
<span x-show="!showPw"><x-heroicon-o-eye class="w-4 h-4" /></span>
<span x-show="showPw" x-cloak><x-heroicon-o-eye-slash class="w-4 h-4" /></span>
</button>
</div>
</div>
<div>
<label class="block text-xs font-semibold text-ink-2 mb-1.5">Neues Passwort</label>
<div class="relative">
<input type="password" x-bind:type="showNewPw ? 'text' : 'password'" class="w-full px-3.5 py-2.5 pr-10 bg-bg-soft border border-line rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<button type="button" @click="showNewPw = !showNewPw" class="absolute right-3 top-1/2 -translate-y-1/2 text-ink-3 hover:text-ink-1 transition-colors" aria-label="Passwort anzeigen">
<span x-show="!showNewPw"><x-heroicon-o-eye class="w-4 h-4" /></span>
<span x-show="showNewPw" x-cloak><x-heroicon-o-eye-slash class="w-4 h-4" /></span>
</button>
</div>
</div>
<div class="flex justify-end gap-2 pt-4 border-t border-line">
<x-btn variant="primary">Passwort ändern</x-btn>
</div>
</div>
<div class="mt-6 pt-5 border-t border-line">
<button
type="button"
wire:click="$dispatch('openModal', { component: 'settings.modals.logout-all' })"
class="inline-flex items-center gap-2 text-sm text-danger hover:underline font-semibold cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-danger focus-visible:ring-offset-2 rounded"
data-testid="logout-all-trigger"
>
<x-heroicon-o-arrow-left-on-rectangle class="w-4 h-4" />
Auf allen Geräten abmelden
</button>
</div>
</x-card>
@endif
@if($section === 'notifications')
<x-card class="shadow-sm" data-section="notifications">
<h2 class="font-bold text-base text-ink-1 mb-4">Benachrichtigungen</h2>
<div class="flex flex-col gap-4">
@php
$notifOpts = [
['key' => 'reminders', 'label' => 'Tägliche Erinnerungen', 'desc' => 'Erinnert dich bei offenen Aufgaben.', 'enabled' => true],
['key' => 'streak', 'label' => 'Streak-Warnungen', 'desc' => 'Bevor deine Lernserie verloren geht.', 'enabled' => true],
['key' => 'teacher', 'label' => 'Lehrer-Nachrichten', 'desc' => 'Direkte Nachrichten von Lehrkräften.', 'enabled' => true],
['key' => 'newsletter', 'label' => 'Lernschiff-Newsletter', 'desc' => 'Tipps, neue Features und Aktionen.', 'enabled' => false],
];
@endphp
@foreach($notifOpts as $opt)
<div class="flex items-start justify-between gap-4 py-2" x-data="{ enabled: {{ $opt['enabled'] ? 'true' : 'false' }} }">
<div>
<div class="font-semibold text-sm text-ink-1">{{ $opt['label'] }}</div>
<div class="text-xs text-ink-3 mt-0.5">{{ $opt['desc'] }}</div>
</div>
<button type="button" @click="enabled = !enabled" :aria-pressed="enabled"
:class="enabled ? 'bg-primary' : 'bg-line-strong'"
class="relative shrink-0 inline-flex h-5 w-9 items-center rounded-full transition-colors cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2">
<span :class="enabled ? 'translate-x-4' : 'translate-x-0.5'" class="inline-block h-4 w-4 transform rounded-full bg-bg-soft shadow transition-transform"></span>
</button>
</div>
@endforeach
</div>
<div class="flex justify-end gap-2 mt-5 pt-4 border-t border-line">
<x-btn variant="primary">Speichern</x-btn>
</div>
</x-card>
@endif
@if($section === 'danger')
<x-card class="shadow-sm border-danger/30" data-section="danger">
<h2 class="font-bold text-base text-danger mb-1">Gefahrenzone</h2>
<p class="text-xs text-ink-3 mb-5">Diese Aktionen können nicht rückgängig gemacht werden.</p>
<div class="flex justify-between items-center py-3 border-t border-line">
<div>
<div class="font-semibold text-sm text-ink-1">Fortschritt zurücksetzen</div>
<div class="text-xs text-ink-3 mt-0.5">Alle Punkte, Streaks und Lektionen werden gelöscht.</div>
</div>
<x-btn>Zurücksetzen</x-btn>
</div>
<div class="flex justify-between items-center py-3 border-t border-line">
<div>
<div class="font-semibold text-sm text-danger">Konto löschen</div>
<div class="text-xs text-ink-3 mt-0.5">Alle Daten unwiderruflich löschen.</div>
</div>
<button
type="button"
wire:click="$dispatch('openModal', { component: 'settings.modals.delete' })"
class="inline-flex items-center gap-2 px-3.5 py-2 rounded-lg bg-danger text-white text-sm font-semibold hover:opacity-90 cursor-pointer transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-danger focus-visible:ring-offset-2"
data-testid="delete-trigger"
>
<x-heroicon-o-trash class="w-4 h-4" />
Konto löschen
</button>
</div>
</x-card>
@endif
</div>
</div>
</div>