From 78d516c55de9b033667c4f72d0d549d9b4fee891 Mon Sep 17 00:00:00 2001 From: Markus Graf Date: Sun, 29 Mar 2026 11:30:55 +0200 Subject: [PATCH] fix(01): add lesson cancel button and handle level 6 completion - Back button in lesson screen to return to forest without completing - "Alle Stufen geschafft!" message when all 6 levels are done - Cap currentLevel at MAX_LEVEL to prevent out-of-bounds progression Co-Authored-By: Claude Opus 4.6 (1M context) --- index.html | 1 + src/app.ts | 11 ++++++++++- src/forest/scene.ts | 19 +++++++++++++++++-- src/styles/main.css | 32 ++++++++++++++++++++++++++++++++ src/ui/screens.ts | 9 +++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 90e8bd6..3d586c2 100644 --- a/index.html +++ b/index.html @@ -38,6 +38,7 @@
+
diff --git a/src/app.ts b/src/app.ts index c8cebb0..6681f52 100644 --- a/src/app.ts +++ b/src/app.ts @@ -20,6 +20,8 @@ export function getDB(): IDBDatabase { return db; } +const MAX_LEVEL = 6; + async function handleLessonComplete(completedLevel: number): Promise { // Update progress after lesson completion const progress = await getProgress(db); @@ -31,7 +33,8 @@ async function handleLessonComplete(completedLevel: number): Promise { } // Advance to next level if this was the current level (LEVL-02, LEVL-03) - if (completedLevel === progress.currentLevel) { + // Cap at MAX_LEVEL + 1 so we don't go beyond defined levels + if (completedLevel === progress.currentLevel && completedLevel < MAX_LEVEL) { progress.currentLevel = completedLevel + 1; } @@ -51,6 +54,11 @@ async function handleLessonComplete(completedLevel: number): Promise { showScreen("forest"); } +async function handleLessonCancel(): Promise { + await initForestScreen(db, startLessonFromForest); + showScreen("forest"); +} + function startLessonFromForest(level: number): void { // Clean up previous lesson if any if (lessonCleanup) { @@ -69,6 +77,7 @@ function startLessonFromForest(level: number): void { level, layout, handleLessonComplete, + handleLessonCancel, ); }); } diff --git a/src/forest/scene.ts b/src/forest/scene.ts index 97661e6..40e29b8 100644 --- a/src/forest/scene.ts +++ b/src/forest/scene.ts @@ -48,6 +48,8 @@ export function renderLevelMap( }); } +const MAX_LEVEL = 6; + export async function initForestScreen( db: IDBDatabase, onStartLesson: (level: number) => void, @@ -61,6 +63,19 @@ export async function initForestScreen( renderLevelMap(mapContainer, progress, onStartLesson); - // Continue button starts the current level - continueBtn.onclick = () => onStartLesson(progress.currentLevel); + const allComplete = levels.every((l) => + progress.completedLevels.includes(l.level), + ); + + if (allComplete) { + continueBtn.textContent = "Alle Stufen geschafft!"; + continueBtn.disabled = true; + continueBtn.classList.add("forest__continue-btn--done"); + } else { + continueBtn.textContent = "Weiter ueben"; + continueBtn.disabled = false; + continueBtn.classList.remove("forest__continue-btn--done"); + continueBtn.onclick = () => + onStartLesson(Math.min(progress.currentLevel, MAX_LEVEL)); + } } diff --git a/src/styles/main.css b/src/styles/main.css index 0a0a08c..c70c6b6 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -259,6 +259,15 @@ h3 { transform: scale(1.05); } +.forest__continue-btn--done { + background: var(--accent-gold); + cursor: default; +} + +.forest__continue-btn--done:hover { + transform: none; +} + /* Lesson screen */ .lesson { display: flex; @@ -269,6 +278,29 @@ h3 { padding: 2rem; min-height: 100vh; background: var(--bg-cream); + position: relative; +} + +.lesson__back-btn { + position: absolute; + top: 1rem; + left: 1rem; + font-family: "Quicksand", sans-serif; + font-size: 1rem; + font-weight: 600; + color: var(--text-warm); + background: var(--bg-forest); + border: 2px solid var(--accent-green); + border-radius: 12px; + padding: 0.5rem 1rem; + cursor: pointer; + transition: background 0.2s ease, transform 0.1s ease; +} + +.lesson__back-btn:hover { + background: var(--accent-green); + color: white; + transform: scale(1.05); } .lesson__letter-area { diff --git a/src/ui/screens.ts b/src/ui/screens.ts index 6db614c..efc249b 100644 --- a/src/ui/screens.ts +++ b/src/ui/screens.ts @@ -79,9 +79,11 @@ export function initLessonScreen( level: number, layout: KeyboardLayout, onComplete: (level: number) => void, + onCancel?: () => void, ): () => void { const letterArea = document.getElementById("letter-area")!; const keyboardContainer = document.getElementById("lesson-keyboard")!; + const backBtn = document.getElementById("lesson-back-btn") as HTMLButtonElement; const state = createExerciseState(level); // Render keyboard @@ -131,6 +133,13 @@ export function initLessonScreen( document.addEventListener("keydown", onKeyDown); + // Back button to cancel lesson + backBtn.onclick = () => { + document.removeEventListener("keydown", onKeyDown); + letterArea.innerHTML = ""; + if (onCancel) onCancel(); + }; + // Return cleanup function return () => { document.removeEventListener("keydown", onKeyDown);