feat: add module pill indicator and cross-module navigation

- Add module title pill above lesson title showing current category
- Enable next/prev buttons to cross module boundaries
- Automatically advance to next module when completing last lesson
- Go to last lesson of previous module when navigating back
This commit is contained in:
2025-12-25 15:25:39 +01:00
parent c8a643582f
commit 5ec56e52fc
5 changed files with 83 additions and 6 deletions

View File

@@ -18,6 +18,7 @@ const elements = {
helpBtn: document.getElementById("help-btn"),
// Left panel
modulePill: document.getElementById("module-pill"),
lessonTitle: document.getElementById("lesson-title"),
lessonDescription: document.getElementById("lesson-description"),
taskInstruction: document.getElementById("task-instruction"),
@@ -266,6 +267,11 @@ function loadCurrentLesson() {
// Update UI based on mode
updateEditorForMode(mode);
// Update module pill with category name
if (elements.modulePill && engineState.module) {
elements.modulePill.textContent = engineState.module.title;
}
// Reset any success indicators
resetSuccessIndicators();
@@ -362,19 +368,39 @@ function updateNavigationButtons() {
}
function nextLesson() {
const prevModuleId = lessonEngine.getCurrentState().module?.id;
const success = lessonEngine.nextLesson();
if (success) {
const newModuleId = lessonEngine.getCurrentState().module?.id;
if (newModuleId !== prevModuleId) {
updateModuleHighlight(newModuleId);
}
loadCurrentLesson();
}
}
function prevLesson() {
const prevModuleId = lessonEngine.getCurrentState().module?.id;
const success = lessonEngine.previousLesson();
if (success) {
const newModuleId = lessonEngine.getCurrentState().module?.id;
if (newModuleId !== prevModuleId) {
updateModuleHighlight(newModuleId);
}
loadCurrentLesson();
}
}
function updateModuleHighlight(moduleId) {
const moduleItems = elements.moduleList.querySelectorAll(".module-header");
moduleItems.forEach((item) => {
item.classList.remove("active");
if (item.dataset.moduleId === moduleId) {
item.classList.add("active");
}
});
}
// ================= CODE EXECUTION =================
function resetCode() {

View File

@@ -101,19 +101,49 @@ export class LessonEngine {
}
/**
* Move to the next lesson
* Move to the next lesson (crosses module boundaries)
* @returns {boolean} Whether the operation was successful
*/
nextLesson() {
return this.setLessonByIndex(this.currentLessonIndex + 1);
// Try next lesson in current module
if (this.setLessonByIndex(this.currentLessonIndex + 1)) {
return true;
}
// At end of module, try next module
const currentModuleIndex = this.modules.findIndex((m) => m.id === this.currentModule?.id);
if (currentModuleIndex >= 0 && currentModuleIndex < this.modules.length - 1) {
const nextModule = this.modules[currentModuleIndex + 1];
this.setModule(nextModule);
this.setLessonByIndex(0); // Start at first lesson
return true;
}
return false;
}
/**
* Move to the previous lesson
* Move to the previous lesson (crosses module boundaries)
* @returns {boolean} Whether the operation was successful
*/
previousLesson() {
return this.setLessonByIndex(this.currentLessonIndex - 1);
// Try previous lesson in current module
if (this.setLessonByIndex(this.currentLessonIndex - 1)) {
return true;
}
// At start of module, try previous module
const currentModuleIndex = this.modules.findIndex((m) => m.id === this.currentModule?.id);
if (currentModuleIndex > 0) {
const prevModule = this.modules[currentModuleIndex - 1];
this.setModule(prevModule);
// Go to last lesson of previous module
const lastIndex = prevModule.lessons.length - 1;
this.setLessonByIndex(lastIndex);
return true;
}
return false;
}
/**
@@ -400,6 +430,12 @@ export class LessonEngine {
* @returns {Object} The current lesson state
*/
getCurrentState() {
const currentModuleIndex = this.modules.findIndex((m) => m.id === this.currentModule?.id);
const isLastLesson = this.currentLessonIndex >= (this.currentModule ? this.currentModule.lessons.length - 1 : 0);
const isFirstLesson = this.currentLessonIndex === 0;
const isLastModule = currentModuleIndex >= this.modules.length - 1;
const isFirstModule = currentModuleIndex <= 0;
return {
module: this.currentModule,
lesson: this.currentLesson,
@@ -407,8 +443,8 @@ export class LessonEngine {
userCode: this.userCode,
totalLessons: this.currentModule ? this.currentModule.lessons.length : 0,
isCompleted: this.isCurrentLessonCompleted(),
canGoNext: this.currentLessonIndex < (this.currentModule ? this.currentModule.lessons.length - 1 : 0),
canGoPrev: this.currentLessonIndex > 0
canGoNext: !isLastLesson || !isLastModule,
canGoPrev: !isFirstLesson || !isFirstModule
};
}

View File

@@ -31,6 +31,7 @@
<!-- Linke Spalte: Anleitung + Editor -->
<div class="left-panel">
<section class="instructions">
<span class="module-pill" id="module-pill">Laden...</span>
<h2 id="lesson-title">Laden...</h2>
<div class="lesson-description" id="lesson-description">
Bitte wähle eine Lektion aus, um zu beginnen.

View File

@@ -31,6 +31,7 @@
<!-- Left Panel: Instructions + Editor -->
<div class="left-panel">
<section class="instructions">
<span class="module-pill" id="module-pill">Loading...</span>
<h2 id="lesson-title">Loading...</h2>
<div class="lesson-description" id="lesson-description">
Please select a lesson to begin.

View File

@@ -208,6 +208,19 @@ code, kbd {
border-bottom: 1px solid var(--border-color);
}
.module-pill {
display: inline-block;
background: var(--primary-bg-medium);
color: var(--primary-color);
padding: 2px 10px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
margin-bottom: var(--spacing-xs);
text-transform: uppercase;
letter-spacing: 0.5px;
}
#lesson-title {
font-size: 1.25rem;
color: var(--primary-dark);