feat(web): UI-Overhaul — Mobile-Nav, Sprachauswahl, 5 Sprachen, Stats-Karten
Mobile-Nav scrollt horizontal und ist auf der Login-Seite ausgeblendet. Nav-Innere Container entfernt (PillTabGroup → flache Buttons). Sprachauswahl von der Nav auf die Account-Page verschoben (eigene Karte mit Vollnamen, vertikales Layout). 5 Locales: DE, EN, FR, IT, ES mit vollständigen Übersetzungen. Account-Karte erlaubt Namensbearbeitung. Stats-Page komplett auf Card-Aesthetic umgebaut (ChartBar, Fire, Brain, CalendarDots, Target, CalendarCheck — keine Emojis). Zwei neue Stats-Karten: Retention-Rate (lapses/reps) und Fälligkeitsvorschau (nächste 7 Tage). API um retention_rate, retention_reps, retention_lapses, due_forecast erweitert. 84-Tage-Activity-Grid hinzugefügt. TS-Fehler aus Locale-Erweiterung behoben (ClozeCardForm number[], decks/new + NewDeckCard Locale-Typ). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
578a0a41f7
commit
3a4523da3e
20 changed files with 1778 additions and 273 deletions
|
|
@ -8,7 +8,7 @@
|
|||
}: {
|
||||
text: string;
|
||||
extra: string;
|
||||
clusterIds: string[];
|
||||
clusterIds: number[];
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,9 @@
|
|||
import { page } from '$app/state';
|
||||
import { goto } from '$app/navigation';
|
||||
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
|
||||
import { i18n, t } from '$lib/i18n/index.svelte.ts';
|
||||
import { PillTabGroup } from '@mana/shared-ui-2';
|
||||
import { t } from '$lib/i18n/index.svelte.ts';
|
||||
|
||||
const langOptions = [
|
||||
{ id: 'de', label: 'DE', title: 'Deutsch' },
|
||||
{ id: 'en', label: 'EN', title: 'English' },
|
||||
];
|
||||
|
||||
const navOptions = $derived([
|
||||
const navItems = $derived([
|
||||
{ id: 'decks', label: t('nav.decks') },
|
||||
{ id: 'explore', label: t('nav.explore') },
|
||||
{ id: 'import', label: t('nav.import') },
|
||||
|
|
@ -36,10 +30,6 @@
|
|||
devUser.user?.email?.charAt(0).toUpperCase() ??
|
||||
'?'
|
||||
);
|
||||
|
||||
function navTo(id: string) {
|
||||
goto('/' + id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="bottom-bar" role="navigation" aria-label={t('common.main_nav')}>
|
||||
|
|
@ -49,17 +39,16 @@
|
|||
<div class="divider" aria-hidden="true"></div>
|
||||
|
||||
<!-- Hauptnavigation -->
|
||||
<PillTabGroup options={navOptions} value={activeNav} onChange={navTo} />
|
||||
|
||||
<div class="divider" aria-hidden="true"></div>
|
||||
|
||||
<!-- Sprache -->
|
||||
<PillTabGroup
|
||||
options={langOptions}
|
||||
value={i18n.current}
|
||||
onChange={(id: string) => i18n.set(id as 'de' | 'en')}
|
||||
sectionLabel={t('common.language_switcher')}
|
||||
/>
|
||||
{#each navItems as item (item.id)}
|
||||
<button
|
||||
class="nav-item"
|
||||
class:active={activeNav === item.id}
|
||||
onclick={() => goto('/' + item.id)}
|
||||
aria-current={activeNav === item.id ? 'page' : undefined}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
<!-- Account -->
|
||||
{#if devUser.id}
|
||||
|
|
@ -98,6 +87,22 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.bottom-bar {
|
||||
left: 0.75rem;
|
||||
right: 0.75rem;
|
||||
bottom: 0.75rem;
|
||||
transform: none;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.bottom-bar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 1.25rem;
|
||||
|
|
@ -106,6 +111,37 @@
|
|||
margin: 0 0.125rem;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0.625rem;
|
||||
border-radius: 9999px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
font-family: inherit;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: color 0.15s ease, background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
color: hsl(var(--color-foreground));
|
||||
background: hsl(var(--color-surface-hover));
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
color: hsl(var(--color-primary));
|
||||
background: hsl(var(--color-primary) / 0.1);
|
||||
}
|
||||
|
||||
.nav-item:focus-visible {
|
||||
outline: 2px solid hsl(var(--color-primary));
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.logo-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import { createDeck, generateDeck, generateDeckFromImage } from '$lib/api/decks.ts';
|
||||
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
|
||||
import { toasts } from '$lib/stores/toasts.svelte.ts';
|
||||
import { i18n, t } from '$lib/i18n/index.svelte.ts';
|
||||
import { i18n, t, type Locale } from '$lib/i18n/index.svelte.ts';
|
||||
import CardSurface from './CardSurface.svelte';
|
||||
import DeckCategoryIcon from './DeckCategoryIcon.svelte';
|
||||
import { apiErrorMessage } from '$lib/api/error.ts';
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
let color = $state('#0088ff');
|
||||
let category = $state<DeckCategoryId | undefined>(undefined);
|
||||
let count = $state(15);
|
||||
let language = $state<'de' | 'en'>(i18n.current);
|
||||
let language = $state<Locale>(i18n.current);
|
||||
let saving = $state(false);
|
||||
let generating = $state(false);
|
||||
let aiError = $state<string | null>(null);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue