diff --git a/apps/mana/apps/web/src/lib/components/layout/use-ai-tier-items.svelte.ts b/apps/mana/apps/web/src/lib/components/layout/use-ai-tier-items.svelte.ts index 036601ea2..cec892082 100644 --- a/apps/mana/apps/web/src/lib/components/layout/use-ai-tier-items.svelte.ts +++ b/apps/mana/apps/web/src/lib/components/layout/use-ai-tier-items.svelte.ts @@ -184,7 +184,7 @@ export function useAiTierItems() { id: 'ai-settings', label: 'KI-Einstellungen', icon: 'settings', - onClick: () => goto('/settings#ai-options'), + onClick: () => goto('/'), }, ]); diff --git a/apps/mana/apps/web/src/lib/components/onboarding/steps/CompleteStep.svelte b/apps/mana/apps/web/src/lib/components/onboarding/steps/CompleteStep.svelte index a029bfcee..04e446ba0 100644 --- a/apps/mana/apps/web/src/lib/components/onboarding/steps/CompleteStep.svelte +++ b/apps/mana/apps/web/src/lib/components/onboarding/steps/CompleteStep.svelte @@ -84,7 +84,7 @@
diff --git a/apps/mana/apps/web/src/lib/components/settings/SettingsSidebar.svelte b/apps/mana/apps/web/src/lib/components/settings/SettingsSidebar.svelte index 952846de2..33967fc88 100644 --- a/apps/mana/apps/web/src/lib/components/settings/SettingsSidebar.svelte +++ b/apps/mana/apps/web/src/lib/components/settings/SettingsSidebar.svelte @@ -1,7 +1,7 @@ - - - - - {#if profileSuccess} -
- Profil erfolgreich aktualisiert! -
- {/if} - {#if profileError} -
- {profileError} -
- {/if} - -
-
- - -

E-Mail kann nicht geändert werden

-
- -
-
- - -
-
- - -
-
- - -
-
- - - - -
-
-
-

Konto-Status

-

Dein aktueller Kontostatus

-
- - Aktiv - -
- -
-
-

Rolle

-

Deine Berechtigungsstufe

-
- - {authStore.user?.role || 'user'} - -
- -
-
-

Benutzer-ID

-

Deine eindeutige Kennung

-
- - {authStore.user?.id?.slice(0, 8) || '...'}... - -
-
-
diff --git a/apps/mana/apps/web/src/lib/data/seed-registry.ts b/apps/mana/apps/web/src/lib/data/seed-registry.ts index 2b30962c1..88101b892 100644 --- a/apps/mana/apps/web/src/lib/data/seed-registry.ts +++ b/apps/mana/apps/web/src/lib/data/seed-registry.ts @@ -34,6 +34,7 @@ import { STRETCH_GUEST_SEED } from '$lib/modules/stretch/collections'; import { MEDITATE_GUEST_SEED } from '$lib/modules/meditate/collections'; import { SLEEP_GUEST_SEED } from '$lib/modules/sleep/collections'; import { MOOD_GUEST_SEED } from '$lib/modules/mood/collections'; +import { QUIZ_GUEST_SEED } from '$lib/modules/quiz/collections'; /** * Flat list of { tableName, rows } entries. Only modules with non-empty @@ -72,6 +73,7 @@ register(STRETCH_GUEST_SEED); register(MEDITATE_GUEST_SEED); register(SLEEP_GUEST_SEED); register(MOOD_GUEST_SEED); +register(QUIZ_GUEST_SEED); /** * Seed all module guest data into empty tables. Idempotent: tables diff --git a/apps/mana/apps/web/src/lib/modules/quiz/EditView.svelte b/apps/mana/apps/web/src/lib/modules/quiz/EditView.svelte index c70580e5e..b1e74e94e 100644 --- a/apps/mana/apps/web/src/lib/modules/quiz/EditView.svelte +++ b/apps/mana/apps/web/src/lib/modules/quiz/EditView.svelte @@ -8,15 +8,17 @@ import { quizzesStore } from './stores/quizzes.svelte'; import { QUESTION_TYPE_LABELS } from './types'; import type { QuestionType, QuestionOption, QuizQuestion } from './types'; - import { ArrowLeft, Plus, Trash, Check, Play } from '@mana/shared-icons'; + import { ArrowLeft, Plus, Trash, Check, Play, PencilSimple, X } from '@mana/shared-icons'; interface Props { quizId: string; } let { quizId }: Props = $props(); + // svelte-ignore state_referenced_locally const quiz$ = useQuiz(quizId); const quiz = $derived(quiz$.value); + // svelte-ignore state_referenced_locally const questions$ = useQuestions(quizId); const questions = $derived(questions$.value); @@ -49,7 +51,8 @@ }); } - // ── New question form ─────────────────────────────── + // ── Question form (new OR edit) ───────────────────── + let editingId = $state(null); let newType = $state('single'); let newText = $state(''); let newExplanation = $state(''); @@ -59,30 +62,52 @@ ]); let newTextAnswer = $state(''); - function resetNewForm() { - newText = ''; - newExplanation = ''; - newTextAnswer = ''; - if (newType === 'truefalse') { - newOptions = [ + function defaultOptions(type: QuestionType): QuestionOption[] { + if (type === 'truefalse') { + return [ { id: 't', text: 'Wahr', isCorrect: true }, { id: 'f', text: 'Falsch', isCorrect: false }, ]; - } else if (newType === 'text') { + } + if (type === 'text') return []; + return [ + { id: crypto.randomUUID(), text: '', isCorrect: type === 'single' }, + { id: crypto.randomUUID(), text: '', isCorrect: false }, + ]; + } + + function resetNewForm() { + editingId = null; + newText = ''; + newExplanation = ''; + newTextAnswer = ''; + newOptions = defaultOptions(newType); + } + + function onTypeChange() { + // Manual onchange so we don't rebuild options on every unrelated rerender + // (an $effect on newType would wipe the buffer when loading a question for edit). + newOptions = defaultOptions(newType); + newTextAnswer = ''; + } + + function startEdit(q: QuizQuestion) { + editingId = q.id; + newType = q.type; + newText = q.questionText; + newExplanation = q.explanation ?? ''; + if (q.type === 'text') { + newTextAnswer = q.options[0]?.text ?? ''; newOptions = []; } else { - newOptions = [ - { id: crypto.randomUUID(), text: '', isCorrect: newType === 'single' }, - { id: crypto.randomUUID(), text: '', isCorrect: false }, - ]; + newTextAnswer = ''; + newOptions = q.options.map((o) => ({ ...o })); } } - $effect(() => { - // Rebuild option slots when question type changes. - newType; + function cancelEdit() { resetNewForm(); - }); + } function toggleNewCorrect(id: string) { if (newType === 'single' || newType === 'truefalse') { @@ -100,7 +125,7 @@ newOptions = newOptions.filter((o) => o.id !== id); } - async function submitNewQuestion() { + async function submitQuestion() { const text = newText.trim(); if (!text) return; @@ -116,12 +141,21 @@ options = valid.map((o) => ({ ...o, text: o.text.trim() })); } - await quizzesStore.addQuestion(quizId, { - type: newType, - questionText: text, - options, - explanation: newExplanation.trim() || null, - }); + if (editingId) { + await quizzesStore.updateQuestion(editingId, { + type: newType, + questionText: text, + options, + explanation: newExplanation.trim() || null, + }); + } else { + await quizzesStore.addQuestion(quizId, { + type: newType, + questionText: text, + options, + explanation: newExplanation.trim() || null, + }); + } resetNewForm(); } @@ -198,10 +232,18 @@ {:else}
    {#each questions as q, i (q.id)} -
  1. +
  2. {i + 1} {QUESTION_TYPE_LABELS[q.type]} + + {/if} +
    - {/if} @@ -417,6 +474,10 @@ border: 1px solid hsl(var(--color-border)); background: hsl(var(--color-surface)); } + .question-item.editing { + border-color: hsl(var(--color-primary)); + background: hsl(var(--color-primary) / 0.05); + } .q-header { display: flex; align-items: center; @@ -478,6 +539,35 @@ border-radius: 0.5rem; border: 1px dashed hsl(var(--color-border)); } + .new-section.is-editing { + border-style: solid; + border-color: hsl(var(--color-primary)); + background: hsl(var(--color-primary) / 0.03); + } + .new-header { + display: flex; + align-items: center; + justify-content: space-between; + } + .new-header h2 { + margin: 0; + } + .cancel-btn { + display: inline-flex; + align-items: center; + gap: 0.25rem; + background: transparent; + border: 1px solid hsl(var(--color-border)); + color: hsl(var(--color-muted-foreground)); + padding: 0.25rem 0.625rem; + border-radius: 9999px; + font-size: 0.75rem; + cursor: pointer; + } + .cancel-btn:hover { + color: hsl(var(--color-error)); + border-color: hsl(var(--color-error)); + } .field { display: flex; flex-direction: column; diff --git a/apps/mana/apps/web/src/lib/modules/quiz/PlayView.svelte b/apps/mana/apps/web/src/lib/modules/quiz/PlayView.svelte index 2bee8d075..add3aee9e 100644 --- a/apps/mana/apps/web/src/lib/modules/quiz/PlayView.svelte +++ b/apps/mana/apps/web/src/lib/modules/quiz/PlayView.svelte @@ -14,8 +14,10 @@ } let { quizId }: Props = $props(); + // svelte-ignore state_referenced_locally const quiz$ = useQuiz(quizId); const quiz = $derived(quiz$.value); + // svelte-ignore state_referenced_locally const questions$ = useQuestions(quizId); const questions = $derived(questions$.value); diff --git a/apps/mana/apps/web/src/lib/modules/settings/ListView.svelte b/apps/mana/apps/web/src/lib/modules/settings/ListView.svelte index a631369a9..9e28deb0e 100644 --- a/apps/mana/apps/web/src/lib/modules/settings/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/settings/ListView.svelte @@ -1,7 +1,6 @@ @@ -974,7 +974,7 @@ currentSyncLabel={syncStatus.label} {appItems} {userEmail} - settingsHref="/settings" + settingsHref="/" manaHref="/mana" profileHref="/profile" spiralHref="/spiral" diff --git a/apps/mana/apps/web/src/routes/(app)/settings/+page.svelte b/apps/mana/apps/web/src/routes/(app)/settings/+page.svelte deleted file mode 100644 index 04a532450..000000000 --- a/apps/mana/apps/web/src/routes/(app)/settings/+page.svelte +++ /dev/null @@ -1,74 +0,0 @@ - - -
    - - -
    - (activeCategory = id)} onJump={jumpTo} /> - -
    - {#if activeCategory === 'profile'} - - {:else if activeCategory === 'general'} - - {:else if activeCategory === 'ai'} - - {:else if activeCategory === 'security'} - - {:else if activeCategory === 'credits'} - - {:else if activeCategory === 'data'} - - {/if} -
    -
    - -

    v{APP_VERSION}

    -
    diff --git a/apps/mana/apps/web/src/routes/(app)/settings/my-data/+page.svelte b/apps/mana/apps/web/src/routes/(app)/settings/my-data/+page.svelte index 28b452c35..16630bf06 100644 --- a/apps/mana/apps/web/src/routes/(app)/settings/my-data/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/settings/my-data/+page.svelte @@ -186,7 +186,7 @@