feat: implement better progress tracking and user feedback, run code after some idle time
This commit is contained in:
40
src/app.js
40
src/app.js
@@ -158,6 +158,7 @@ function resetSuccessIndicators() {
|
|||||||
elements.lessonTitle.classList.remove("success-text");
|
elements.lessonTitle.classList.remove("success-text");
|
||||||
elements.nextBtn.classList.remove("success");
|
elements.nextBtn.classList.remove("success");
|
||||||
elements.taskInstruction.classList.remove("success-instruction");
|
elements.taskInstruction.classList.remove("success-instruction");
|
||||||
|
elements.runBtn.classList.remove("re-run");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure editor layout based on display type
|
// Configure editor layout based on display type
|
||||||
@@ -199,6 +200,28 @@ function loadCurrentLesson() {
|
|||||||
// Configure editor layout based on lesson settings
|
// Configure editor layout based on lesson settings
|
||||||
resetEditorLayout(lesson);
|
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 = '<img src="./gear.svg" />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 = '<img src="./gear.svg" />Run';
|
||||||
|
|
||||||
|
// Remove completion badge if exists
|
||||||
|
const badge = document.querySelector(".completion-badge");
|
||||||
|
if (badge) {
|
||||||
|
badge.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update level indicator
|
// Update level indicator
|
||||||
renderLevelIndicator(elements.levelIndicator, state.currentLessonIndex + 1, state.currentModule.lessons.length);
|
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
|
// Set a new timer for preview update after user stops typing
|
||||||
previewTimer = setTimeout(() => {
|
previewTimer = setTimeout(() => {
|
||||||
// Apply the code for preview without validation
|
// Apply the code for preview without validation
|
||||||
lessonEngine.applyUserCode(elements.codeInput.value);
|
// lessonEngine.applyUserCode(elements.codeInput.value);
|
||||||
}, 500); // Update preview 500ms after user stops typing
|
runCode();
|
||||||
|
}, 800); // Update preview 500ms after user stops typing
|
||||||
|
|
||||||
// Store current code state
|
// Store current code state
|
||||||
state.userCodeBeforeValidation = elements.codeInput.value;
|
state.userCodeBeforeValidation = elements.codeInput.value;
|
||||||
@@ -320,6 +344,18 @@ function runCode() {
|
|||||||
// Show success feedback with visual indicators
|
// Show success feedback with visual indicators
|
||||||
showFeedback(true, validationResult.message || "Great job! Your code works correctly.");
|
showFeedback(true, validationResult.message || "Great job! Your code works correctly.");
|
||||||
|
|
||||||
|
// Add this block to update the Run button to Re-run
|
||||||
|
elements.runBtn.innerHTML = '<img src="./gear.svg" />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
|
// Add success visual indicators
|
||||||
elements.codeEditor.classList.add("success-highlight");
|
elements.codeEditor.classList.add("success-highlight");
|
||||||
elements.lessonTitle.classList.add("success-text");
|
elements.lessonTitle.classList.add("success-text");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ let feedbackElement = null;
|
|||||||
* Render the module list in the sidebar
|
* Render the module list in the sidebar
|
||||||
* @param {HTMLElement} container - The container element for the module list
|
* @param {HTMLElement} container - The container element for the module list
|
||||||
* @param {Array} modules - The list of modules
|
* @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) {
|
export function renderModuleList(container, modules, onSelectModule) {
|
||||||
// Clear the container
|
// Clear the container
|
||||||
@@ -22,6 +22,19 @@ export function renderModuleList(container, modules, onSelectModule) {
|
|||||||
moduleItem.dataset.moduleId = module.id;
|
moduleItem.dataset.moduleId = module.id;
|
||||||
moduleItem.textContent = module.title;
|
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", () => {
|
moduleItem.addEventListener("click", () => {
|
||||||
onSelectModule(module.id);
|
onSelectModule(module.id);
|
||||||
});
|
});
|
||||||
@@ -50,9 +63,9 @@ export function renderLesson(titleEl, descriptionEl, taskEl, previewEl, prefixEl
|
|||||||
taskEl.innerHTML = lesson.task || "";
|
taskEl.innerHTML = lesson.task || "";
|
||||||
|
|
||||||
// Set code editor contents
|
// Set code editor contents
|
||||||
prefixEl.textContent = lesson.codePrefix || "";
|
// prefixEl.textContent = lesson.codePrefix || "";
|
||||||
inputEl.value = lesson.initialCode || "";
|
inputEl.value = lesson.initialCode || "";
|
||||||
suffixEl.textContent = lesson.codeSuffix || "";
|
// suffixEl.textContent = lesson.codeSuffix || "";
|
||||||
|
|
||||||
// Clear any existing feedback
|
// Clear any existing feedback
|
||||||
clearFeedback();
|
clearFeedback();
|
||||||
@@ -83,7 +96,7 @@ export function showFeedback(isSuccess, message) {
|
|||||||
// Create feedback element
|
// Create feedback element
|
||||||
feedbackElement = document.createElement("div");
|
feedbackElement = document.createElement("div");
|
||||||
feedbackElement.classList.add(isSuccess ? "feedback-success" : "feedback-error");
|
feedbackElement.classList.add(isSuccess ? "feedback-success" : "feedback-error");
|
||||||
feedbackElement.textContent = message;
|
feedbackElement.innerHTML = message;
|
||||||
|
|
||||||
// Find where to insert the feedback
|
// Find where to insert the feedback
|
||||||
const insertAfter = document.querySelector(".editor-content");
|
const insertAfter = document.querySelector(".editor-content");
|
||||||
@@ -97,7 +110,7 @@ export function showFeedback(isSuccess, message) {
|
|||||||
feedbackElement.parentNode.removeChild(feedbackElement);
|
feedbackElement.parentNode.removeChild(feedbackElement);
|
||||||
}
|
}
|
||||||
feedbackElement = null;
|
feedbackElement = null;
|
||||||
}, 3_000); // Remove feedback after 3 seconds
|
}, 5_000); // Remove feedback after 3 seconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="./bar_1680535.png" width="32" alt="CODE CRISPIES Logo" />
|
<img src="./bowl.png" width="32" alt="CODE CRISPIES Logo" />
|
||||||
<h1>CODE<br /><span>CRISPIES</span></h1>
|
<h1>CODE<br /><span>CRISPIES</span></h1>
|
||||||
</div>
|
</div>
|
||||||
<nav class="main-nav">
|
<nav class="main-nav">
|
||||||
@@ -50,14 +50,14 @@
|
|||||||
|
|
||||||
<div class="code-editor">
|
<div class="code-editor">
|
||||||
<div class="editor-header">
|
<div class="editor-header">
|
||||||
<span>CSS Editor</span>
|
<label for="code-input">CSS Editor</label>
|
||||||
<div class="validation-indicators-container"></div>
|
<div class="validation-indicators-container"></div>
|
||||||
<button id="run-btn" class="btn btn-secondary"><img src="./gear.svg" />Run</button>
|
<button id="run-btn" class="btn btn-secondary"><img src="./gear.svg" alt="" />Run</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-content">
|
<div class="editor-content">
|
||||||
<pre><code id="editor-prefix"></code></pre>
|
<!-- <pre><code id="editor-prefix"></code></pre>-->
|
||||||
<textarea id="code-input" class="code-input" spellcheck="false"></textarea>
|
<textarea id="code-input" class="code-input" spellcheck="false"></textarea>
|
||||||
<pre><code id="editor-suffix"></code></pre>
|
<!-- <pre><code id="editor-suffix"></code></pre>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user