diff --git a/apps/mana/apps/web/src/lib/stores/onboarding-flow.svelte.ts b/apps/mana/apps/web/src/lib/stores/onboarding-flow.svelte.ts new file mode 100644 index 000000000..4648b0696 --- /dev/null +++ b/apps/mana/apps/web/src/lib/stores/onboarding-flow.svelte.ts @@ -0,0 +1,39 @@ +/** + * Ephemeral state for the three-screen onboarding flow — holds values + * a later screen needs from an earlier screen (the freshly-typed name + * for Screen 2's greeting, the multi-selected template ids for the + * Screen 3 finish handler). + * + * Deliberately module-local and non-persistent: + * - The canonical source of truth is `authStore.user` (name) and the + * scene Dexie table (apps). This store only bridges screens inside + * a single session. + * - We can't mutate `authStore.user.name` after PATCH /me/profile + * because `user` is minted from the JWT; the in-memory value only + * catches up on the next token refresh. This store lets Screen 2 + * greet the user immediately with what they just typed. + * - Reset on flow completion so a `markComplete → reset → revisit` + * from settings starts fresh. + */ + +let pendingName = $state(null); +let selectedTemplateIds = $state([]); + +export const onboardingFlow = { + get pendingName() { + return pendingName; + }, + get selectedTemplateIds() { + return selectedTemplateIds; + }, + setPendingName(value: string) { + pendingName = value.trim() || null; + }, + setSelectedTemplateIds(ids: string[]) { + selectedTemplateIds = ids; + }, + reset() { + pendingName = null; + selectedTemplateIds = []; + }, +}; diff --git a/apps/mana/apps/web/src/routes/(app)/onboarding/look/+page.svelte b/apps/mana/apps/web/src/routes/(app)/onboarding/look/+page.svelte new file mode 100644 index 000000000..c6d9020c8 --- /dev/null +++ b/apps/mana/apps/web/src/routes/(app)/onboarding/look/+page.svelte @@ -0,0 +1,334 @@ + + + +
+
+

Hi {displayName}, wähle deinen Look

+

+ Das Theme gilt sofort und für alle Module. Du kannst es jederzeit in Einstellungen wechseln. +

+
+ +
+ +
+ {#each modes as m (m.id)} + {@const isActive = theme.mode === m.id} + + {/each} +
+
+ +
+ +
+ {#each allVariants as variant (variant)} + {@const def = THEME_DEFINITIONS[variant]} + {@const isActive = theme.variant === variant} + + {/each} +
+
+ +
+ + +
+
+ + diff --git a/apps/mana/apps/web/src/routes/(app)/onboarding/name/+page.svelte b/apps/mana/apps/web/src/routes/(app)/onboarding/name/+page.svelte index 4626b4287..ba8bda267 100644 --- a/apps/mana/apps/web/src/routes/(app)/onboarding/name/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/onboarding/name/+page.svelte @@ -8,6 +8,7 @@ import { goto } from '$app/navigation'; import { browser } from '$app/environment'; import { authStore } from '$lib/stores/auth.svelte'; + import { onboardingFlow } from '$lib/stores/onboarding-flow.svelte'; import { ArrowRight } from '@mana/shared-icons'; function getAuthUrl(): string { @@ -19,9 +20,10 @@ return import.meta.env.DEV ? 'http://localhost:3001' : ''; } - // Prefill: existing name (returning user revisiting) → email local-part - // → empty. Trimmed so whitespace-only values don't count as "filled". - let name = $state((authStore.user?.name ?? '').trim()); + // Prefill: last value entered in this flow (back-navigation) → existing + // `user.name` (returning user revisiting) → empty. Trimmed so + // whitespace-only values don't count as "filled". + let name = $state((onboardingFlow.pendingName ?? authStore.user?.name ?? '').trim()); let saving = $state(false); let error = $state(null); @@ -48,6 +50,7 @@ error = null; try { await saveName(trimmed); + onboardingFlow.setPendingName(trimmed); await goto('/onboarding/look'); } catch (err) { console.error('[onboarding/name] save failed:', err); @@ -70,6 +73,7 @@ } finally { saving = false; } + onboardingFlow.setPendingName(fallback); await goto('/onboarding/look'); }