From 2c8174d1a00bee05ba345d42ec9863fe766b769a Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Mon, 19 May 2025 20:21:53 +0200 Subject: [PATCH] feat: implement better progress tracking and user feedback, run code after some idle time --- src/app.js | 40 ++++++++++++++++++++++++++++++++++++++-- src/helpers/renderer.js | 23 ++++++++++++++++++----- src/index.html | 10 +++++----- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/app.js b/src/app.js index aba1a50..93eb211 100644 --- a/src/app.js +++ b/src/app.js @@ -158,6 +158,7 @@ function resetSuccessIndicators() { elements.lessonTitle.classList.remove("success-text"); elements.nextBtn.classList.remove("success"); elements.taskInstruction.classList.remove("success-instruction"); + elements.runBtn.classList.remove("re-run"); } // Configure editor layout based on display type @@ -199,6 +200,28 @@ function loadCurrentLesson() { // Configure editor layout based on lesson settings resetEditorLayout(lesson); + // Update Run button text based on completion status + const moduleProgress = state.userProgress[state.currentModule.id]; + if (moduleProgress && moduleProgress.completed.includes(state.currentLessonIndex)) { + elements.runBtn.innerHTML = 'Re-run'; + + // Add completion badge next to title if not already present + if (!document.querySelector(".completion-badge")) { + const badge = document.createElement("span"); + badge.className = "completion-badge"; + badge.textContent = "Completed"; + elements.lessonTitle.appendChild(badge); + } + } else { + elements.runBtn.innerHTML = 'Run'; + + // Remove completion badge if exists + const badge = document.querySelector(".completion-badge"); + if (badge) { + badge.remove(); + } + } + // Update level indicator renderLevelIndicator(elements.levelIndicator, state.currentLessonIndex + 1, state.currentModule.lessons.length); @@ -242,8 +265,9 @@ function handleUserInput() { // Set a new timer for preview update after user stops typing previewTimer = setTimeout(() => { // Apply the code for preview without validation - lessonEngine.applyUserCode(elements.codeInput.value); - }, 500); // Update preview 500ms after user stops typing + // lessonEngine.applyUserCode(elements.codeInput.value); + runCode(); + }, 800); // Update preview 500ms after user stops typing // Store current code state state.userCodeBeforeValidation = elements.codeInput.value; @@ -320,6 +344,18 @@ function runCode() { // Show success feedback with visual indicators showFeedback(true, validationResult.message || "Great job! Your code works correctly."); + // Add this block to update the Run button to Re-run + elements.runBtn.innerHTML = 'Re-run'; + elements.runBtn.classList.add("re-run"); + + // Add completion badge if not present + if (!document.querySelector(".completion-badge")) { + const badge = document.createElement("span"); + badge.className = "completion-badge"; + badge.textContent = "Completed"; + elements.lessonTitle.appendChild(badge); + } + // Add success visual indicators elements.codeEditor.classList.add("success-highlight"); elements.lessonTitle.classList.add("success-text"); diff --git a/src/helpers/renderer.js b/src/helpers/renderer.js index 68afb4d..e13e973 100644 --- a/src/helpers/renderer.js +++ b/src/helpers/renderer.js @@ -9,7 +9,7 @@ let feedbackElement = null; * Render the module list in the sidebar * @param {HTMLElement} container - The container element for the module list * @param {Array} modules - The list of modules - * @param { Function} onSelectModule - Callback when a module is selected + * @param {Function} onSelectModule - Callback when a module is selected */ export function renderModuleList(container, modules, onSelectModule) { // Clear the container @@ -22,6 +22,19 @@ export function renderModuleList(container, modules, onSelectModule) { moduleItem.dataset.moduleId = module.id; moduleItem.textContent = module.title; + // Get user progress from localStorage to mark completed lessons + const progressData = localStorage.getItem("codeCrispies.Progress"); + if (progressData) { + try { + const progress = JSON.parse(progressData); + if (progress[module.id] && progress[module.id].completed.length === module.lessons.length) { + moduleItem.classList.add("completed"); + } + } catch (e) { + console.error("Error parsing progress data:", e); + } + } + moduleItem.addEventListener("click", () => { onSelectModule(module.id); }); @@ -50,9 +63,9 @@ export function renderLesson(titleEl, descriptionEl, taskEl, previewEl, prefixEl taskEl.innerHTML = lesson.task || ""; // Set code editor contents - prefixEl.textContent = lesson.codePrefix || ""; + // prefixEl.textContent = lesson.codePrefix || ""; inputEl.value = lesson.initialCode || ""; - suffixEl.textContent = lesson.codeSuffix || ""; + // suffixEl.textContent = lesson.codeSuffix || ""; // Clear any existing feedback clearFeedback(); @@ -83,7 +96,7 @@ export function showFeedback(isSuccess, message) { // Create feedback element feedbackElement = document.createElement("div"); feedbackElement.classList.add(isSuccess ? "feedback-success" : "feedback-error"); - feedbackElement.textContent = message; + feedbackElement.innerHTML = message; // Find where to insert the feedback const insertAfter = document.querySelector(".editor-content"); @@ -97,7 +110,7 @@ export function showFeedback(isSuccess, message) { feedbackElement.parentNode.removeChild(feedbackElement); } feedbackElement = null; - }, 3_000); // Remove feedback after 3 seconds + }, 5_000); // Remove feedback after 3 seconds } } diff --git a/src/index.html b/src/index.html index 5bbc7c4..0e4e2b3 100644 --- a/src/index.html +++ b/src/index.html @@ -11,7 +11,7 @@