import { LessonEngine } from './LessonEngine'; import { renderLesson, renderModuleList, renderLevelIndicator, showFeedback } from './renderer'; import { validateUserCode } from './validator'; import { loadModules } from '../lessons/lesson-config'; // Main Application state const state = { currentModule: null, currentLessonIndex: 0, modules: [], userProgress: {}, // Format: { moduleId: { completed: [0, 2, 3], current: 4 } } }; // DOM elements const elements = { moduleList: document.querySelector('.module-list'), lessonTitle: document.getElementById('lesson-title'), lessonDescription: document.getElementById('lesson-description'), taskInstruction: document.getElementById('task-instruction'), previewArea: document.getElementById('preview-area'), editorPrefix: document.getElementById('editor-prefix'), codeInput: document.getElementById('code-input'), editorSuffix: document.getElementById('editor-suffix'), prevBtn: document.getElementById('prev-btn'), nextBtn: document.getElementById('next-btn'), runBtn: document.getElementById('run-btn'), levelIndicator: document.getElementById('level-indicator'), modalContainer: document.getElementById('modal-container'), modalTitle: document.getElementById('modal-title'), modalContent: document.getElementById('modal-content'), modalClose: document.getElementById('modal-close'), moduleSelectorBtn: document.getElementById('module-selector-btn'), resetBtn: document.getElementById('reset-btn'), helpBtn: document.getElementById('help-btn'), }; // Initialize the lesson engine const lessonEngine = new LessonEngine(); // Load user progress from localStorage function loadUserProgress() { const savedProgress = localStorage.getItem('codeCrispiesProgress'); if (savedProgress) { state.userProgress = JSON.parse(savedProgress); } } // Save user progress to localStorage function saveUserProgress() { localStorage.setItem('codeCrispiesProgress', JSON.stringify(state.userProgress)); } // Initialize the module list async function initializeModules() { try { state.modules = await loadModules(); renderModuleList(elements.moduleList, state.modules, selectModule); // Select the first module or the last one user was on const lastModuleId = localStorage.getItem('lastModuleId'); if (lastModuleId && state.modules.find(m => m.id === lastModuleId)) { selectModule(lastModuleId); } else if (state.modules.length > 0) { selectModule(state.modules[0].id); } } catch (error) { console.error('Failed to load modules:', error); elements.lessonDescription.textContent = 'Failed to load modules. Please refresh the page.'; } } // Select a module function selectModule(moduleId) { const selectedModule = state.modules.find(module => module.id === moduleId); if (!selectedModule) return; state.currentModule = selectedModule; // Update module list UI const moduleItems = elements.moduleList.querySelectorAll('.module-list-item'); moduleItems.forEach(item => { item.classList.remove('active'); if (item.dataset.moduleId === moduleId) { item.classList.add('active'); } }); // Load user progress for this module if (!state.userProgress[moduleId]) { state.userProgress[moduleId] = { completed: [], current: 0 }; } state.currentLessonIndex = state.userProgress[moduleId].current || 0; loadCurrentLesson(); // Save the last selected module localStorage.setItem('lastModuleId', moduleId); } // Load the current lesson function loadCurrentLesson() { if (!state.currentModule || !state.currentModule.lessons) { return; } // Make sure lesson index is in bounds if (state.currentLessonIndex >= state.currentModule.lessons.length) { state.currentLessonIndex = state.currentModule.lessons.length - 1; } else if (state.currentLessonIndex < 0) { state.currentLessonIndex = 0; } const lesson = state.currentModule.lessons[state.currentLessonIndex]; lessonEngine.setLesson(lesson); // Update UI renderLesson( elements.lessonTitle, elements.lessonDescription, elements.taskInstruction, elements.previewArea, elements.editorPrefix, elements.codeInput, elements.editorSuffix, lesson ); // Update level indicator renderLevelIndicator( elements.levelIndicator, state.currentLessonIndex + 1, state.currentModule.lessons.length ); // Update navigation buttons updateNavigationButtons(); // Save current progress state.userProgress[state.currentModule.id].current = state.currentLessonIndex; saveUserProgress(); } // Update navigation buttons state function updateNavigationButtons() { elements.prevBtn.disabled = state.currentLessonIndex === 0; elements.nextBtn.disabled = !state.currentModule || state.currentLessonIndex === state.currentModule.lessons.length - 1; // Style changes for disabled buttons if (elements.prevBtn.disabled) { elements.prevBtn.classList.add('btn-disabled'); } else { elements.prevBtn.classList.remove('btn-disabled'); } if (elements.nextBtn.disabled) { elements.nextBtn.classList.add('btn-disabled'); } else { elements.nextBtn.classList.remove('btn-disabled'); } } // Go to the next lesson function nextLesson() { if (!state.currentModule) return; if (state.currentLessonIndex < state.currentModule.lessons.length - 1) { state.currentLessonIndex++; loadCurrentLesson(); } } // Go to the previous lesson function prevLesson() { if (state.currentLessonIndex > 0) { state.currentLessonIndex--; loadCurrentLesson(); } } // Run the user code function runCode() { const userCode = elements.codeInput.value; const lesson = state.currentModule.lessons[state.currentLessonIndex]; const validationResult = validateUserCode(userCode, lesson); if (validationResult.isValid) { // Mark lesson as completed const moduleProgress = state.userProgress[state.currentModule.id]; if (!moduleProgress.completed.includes(state.currentLessonIndex)) { moduleProgress.completed.push(state.currentLessonIndex); saveUserProgress(); } // Show success feedback showFeedback(true, validationResult.message || 'Great job! Your code works correctly.'); // Apply the code to see the result lessonEngine.applyUserCode(userCode); // Enable the next button if not already on the last lesson if (state.currentLessonIndex < state.currentModule.lessons.length - 1) { elements.nextBtn.disabled = false; elements.nextBtn.classList.remove('btn-disabled'); } } else { // Show error feedback showFeedback(false, validationResult.message || 'Your code doesn\'t seem to be correct. Try again!'); } } // Show the module selector modal function showModuleSelector() { elements.modalTitle.textContent = 'Select a Module'; // Create module buttons const moduleButtons = state.modules.map(module => { const button = document.createElement('button'); button.classList.add('btn', 'module-button'); button.style.display = 'block'; button.style.width = '100%'; button.style.marginBottom = '10px'; button.style.padding = '15px'; button.style.textAlign = 'left'; // Add completion status const progress = state.userProgress[module.id]; const completedCount = progress ? progress.completed.length : 0; const totalLessons = module.lessons.length; const percentComplete = Math.round((completedCount / totalLessons) * 100); button.innerHTML = ` ${module.title}
Code Crispies is an interactive platform for learning CSS through practical exercises.
Select a module from the sidebar to start learning. Each module contains a series of lessons focused on specific CSS concepts.
For each lesson:
Are you sure you want to reset all your progress? This cannot be undone.