feat: update localStorage keys for user progress and last module ID, enhance editor layout with validation indicator

This commit is contained in:
Michael Czechowski
2025-05-18 23:22:17 +02:00
parent 10094e36dc
commit 1ce1f6cd2f
4 changed files with 58 additions and 87 deletions

View File

@@ -43,7 +43,7 @@ const lessonEngine = new LessonEngine();
// Load user progress from localStorage // Load user progress from localStorage
function loadUserProgress() { function loadUserProgress() {
const savedProgress = localStorage.getItem("codeCrispiesProgress"); const savedProgress = localStorage.getItem("codeCrispies.Progress");
if (savedProgress) { if (savedProgress) {
state.userProgress = JSON.parse(savedProgress); state.userProgress = JSON.parse(savedProgress);
} }
@@ -51,7 +51,7 @@ function loadUserProgress() {
// Save user progress to localStorage // Save user progress to localStorage
function saveUserProgress() { function saveUserProgress() {
localStorage.setItem("codeCrispiesProgress", JSON.stringify(state.userProgress)); localStorage.setItem("codeCrispies.Progress", JSON.stringify(state.userProgress));
} }
// Initialize the module list // Initialize the module list
@@ -61,7 +61,7 @@ async function initializeModules() {
renderModuleList(elements.moduleList, state.modules, selectModule); renderModuleList(elements.moduleList, state.modules, selectModule);
// Select the first module or the last one user was on // Select the first module or the last one user was on
const lastModuleId = localStorage.getItem("lastModuleId"); const lastModuleId = localStorage.getItem("codeCrispies.lastModuleId");
if (lastModuleId && state.modules.find((m) => m.id === lastModuleId)) { if (lastModuleId && state.modules.find((m) => m.id === lastModuleId)) {
selectModule(lastModuleId); selectModule(lastModuleId);
} else if (state.modules.length > 0) { } else if (state.modules.length > 0) {
@@ -145,7 +145,7 @@ function selectModule(moduleId) {
loadCurrentLesson(); loadCurrentLesson();
// Save the last selected module // Save the last selected module
localStorage.setItem("lastModuleId", moduleId); localStorage.setItem("codeCrispies.lastModuleId", moduleId);
// Reset any success indicators // Reset any success indicators
resetSuccessIndicators(); resetSuccessIndicators();
@@ -161,29 +161,10 @@ function resetSuccessIndicators() {
// Configure editor layout based on display type // Configure editor layout based on display type
function configureEditorLayout(lesson) { function configureEditorLayout(lesson) {
// Default to block display if not specified // Remove validation indicator if exists
const displayType = lesson.editorDisplayType || "block"; const existingIndicator = elements.codeEditor.querySelector(".validation-success-indicator");
if (existingIndicator) {
// Reset classes existingIndicator.remove();
elements.codeEditor.classList.remove("inline-editor", "block-editor");
elements.editorContent.classList.remove("inline-mode", "block-mode");
// Apply appropriate layout class
if (displayType === "inline") {
elements.codeEditor.classList.add("inline-editor");
elements.editorContent.classList.add("inline-mode");
// Add special styling for inline mode
elements.codeInput.style.display = "inline-block";
elements.codeInput.style.width = lesson.inlineInputWidth || "auto";
} else {
// Default block mode
elements.codeEditor.classList.add("block-editor");
elements.editorContent.classList.add("block-mode");
// Reset styles for block mode
elements.codeInput.style.display = "block";
elements.codeInput.style.width = "100%";
} }
} }
@@ -248,10 +229,10 @@ function loadCurrentLesson() {
let previewTimer = null; let previewTimer = null;
function setupLivePreview() { function setupLivePreview() {
// Clear previous event listener if any // Clear previous event listener if any
elements.codeInput.removeEventListener('input', handleUserInput); elements.codeInput.removeEventListener("input", handleUserInput);
// Add new event listener // Add new event listener
elements.codeInput.addEventListener('input', handleUserInput); elements.codeInput.addEventListener("input", handleUserInput);
} }
// Handle user input with debounced preview updates // Handle user input with debounced preview updates
@@ -327,6 +308,12 @@ function runCode() {
updateModuleSelectorButtonProgress(); updateModuleSelectorButtonProgress();
} }
// Add validation indicator to editor
const validationIndicator = document.createElement("div");
validationIndicator.className = "validation-success-indicator";
validationIndicator.innerHTML = "✓";
elements.codeEditor.appendChild(validationIndicator);
// 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.");
@@ -457,8 +444,8 @@ function resetProgress() {
document.getElementById("cancel-reset").addEventListener("click", closeModal); document.getElementById("cancel-reset").addEventListener("click", closeModal);
document.getElementById("confirm-reset").addEventListener("click", () => { document.getElementById("confirm-reset").addEventListener("click", () => {
localStorage.removeItem("codeCrispiesProgress"); localStorage.removeItem("codeCrispies.Progress");
localStorage.removeItem("lastModuleId"); localStorage.removeItem("codeCrispies.lastModuleId");
state.userProgress = {}; state.userProgress = {};
closeModal(); closeModal();

View File

@@ -16,16 +16,16 @@ import responsiveConfig from "../../lessons/08-responsive.json";
// Module store // Module store
const moduleStore = [ const moduleStore = [
basicSelectorsConfig, basicSelectorsConfig
basicsConfig, // basicsConfig,
boxModelConfig, // boxModelConfig,
selectorsConfig, // selectorsConfig,
colorsConfig, // colorsConfig,
typographyConfig, // typographyConfig,
unitVariablesConfig, // unitVariablesConfig,
transitionsAnimationsConfig, // transitionsAnimationsConfig,
layoutConfig, // layoutConfig,
responsiveConfig // responsiveConfig
]; ];
/** /**
@@ -83,15 +83,6 @@ function validateModuleConfig(config) {
config.lessons.forEach((lesson, index) => { config.lessons.forEach((lesson, index) => {
if (!lesson.title) throw new Error(`Lesson ${index} missing "title"`); if (!lesson.title) throw new Error(`Lesson ${index} missing "title"`);
if (!lesson.previewHTML) throw new Error(`Lesson ${index} missing "previewHTML"`); if (!lesson.previewHTML) throw new Error(`Lesson ${index} missing "previewHTML"`);
// Apply defaults for new properties if they don't exist
if (lesson.editorDisplayType === undefined) {
lesson.editorDisplayType = "block"; // Default to block display
}
if (lesson.editorDisplayType === "inline" && lesson.inlineInputWidth === undefined) {
lesson.inlineInputWidth = "auto"; // Default width for inline input
}
}); });
} }
@@ -120,36 +111,3 @@ export function addCustomModule(moduleConfig) {
return false; return false;
} }
} }
/**
* Convert a module to include the enhanced schema with editorDisplayType
* @param {Object} moduleConfig - The module configuration to convert
* @returns {Object} The enhanced module configuration
*/
export function enhanceModuleSchema(moduleConfig) {
if (!moduleConfig || !moduleConfig.lessons) return moduleConfig;
const enhancedModule = {...moduleConfig};
enhancedModule.lessons = moduleConfig.lessons.map(lesson => {
const enhancedLesson = {...lesson};
// Apply defaults for new properties if they don't exist
if (enhancedLesson.editorDisplayType === undefined) {
enhancedLesson.editorDisplayType = "block"; // Default to block display
}
if (enhancedLesson.editorDisplayType === "inline" && enhancedLesson.inlineInputWidth === undefined) {
enhancedLesson.inlineInputWidth = "auto"; // Default width for inline input
}
return enhancedLesson;
});
return enhancedModule;
}
// Enhance all modules on load to ensure they have the new schema properties
moduleStore.forEach((module, index) => {
moduleStore[index] = enhanceModuleSchema(module);
});

View File

@@ -331,17 +331,27 @@ code {
color: #d4d4d4; color: #d4d4d4;
border: none; border: none;
width: 100%; width: 100%;
min-height: 100px; min-height: 100px; /* Ensure minimum height */
font-family: var(--font-code); font-family: var(--font-code);
font-size: 14px; font-size: 14px;
line-height: 1.5; line-height: 1.5;
padding: var(--spacing-xs) 0; padding: var(--spacing-xs) 0;
outline: none; outline: none;
resize: vertical; overflow: auto; /* Ensure scrolling works */
resize: none; /* Disable textarea resize */
caret-color: var(--primary-light); caret-color: var(--primary-light);
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
} }
.code-input.inline-input {
display: inline-block;
min-height: 30px;
height: auto;
padding: 2px 4px;
margin: 0 4px;
border-bottom: 1px dashed #666;
}
/* Controls */ /* Controls */
.controls { .controls {
display: flex; display: flex;
@@ -414,6 +424,22 @@ code {
border: 1px solid var(--success-color); border: 1px solid var(--success-color);
} }
.validation-success-indicator {
position: absolute;
top: 1rem;
right: 7rem;
background-color: var(--success-color);
color: white;
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
z-index: 5;
}
/* Module selector button with progress */ /* Module selector button with progress */
#module-selector-btn { #module-selector-btn {
overflow: hidden; overflow: hidden;