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:
26
src/app.js
26
src/app.js
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
13
src/main.css
13
src/main.css
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user