JIT
Parent student score page
create another page that parent can see the score of the the specific student on this code: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Student Dashboard</title> <link rel="stylesheet" href="student.css"> </head> <body> <!-- Navbar Removed --> <div class="container"> <h1>Student Dashboard</h1> <!-- Quiz Completion Status Message --> <div id="quiz-completion-status" class="completion-notice hidden"> <p>Please complete all available quizzes to view your performance analytics.</p> </div> <!-- Display Created Lessons --> <section id="lessons-section"> <h2>Available Lessons</h2> <!-- Container for lessons where event listener will be attached --> <div id="createdLessons"> <p>Loading lessons...</p> </div> </section> <!-- Display Created Quizzes --> <section id="quizzes-section"> <h2>Available Quizzes</h2> <!-- Container for quizzes where event listener will be attached --> <div id="createdQuizzes"> <p>Loading quizzes...</p> </div> </section> </div> <!-- Performance Analytics Button --> <button id="analyticsBtn" class="analytics-btn" disabled>View Performance & Analytics</button> <!-- Performance Analytics Modal --> <div id="analyticsModal" class="modal"> <div class="modal-content"> <!-- Close Button Removed --> <h2>Performance & Analytics</h2> <div class="analytics-section"> <h3>Activity</h3> <p><strong>Last Accessed:</strong> <span id="lastAccessed">N/A</span></p> </div> <div class="analytics-section"> <h3>Lesson Progress</h3> <ul id="lessonProgressList"> <li>Loading lesson status...</li> </ul> </div> <div class="analytics-section"> <h3>Quiz Scores</h3> <ul id="quizScoresList"> <li>No quizzes taken yet.</li> </ul> </div> <div class="analytics-section"> <h3>Overall Grade</h3> <p><strong id="overallGrade">N/A</strong></p> </div> <!-- Navigation Buttons Inside Modal --> <div class="modal-navigation"> <button id="modalDashboardBtn" class="modal-nav-btn dashboard">Back to Dashboard</button> <button id="modalHomeBtn" class="modal-nav-btn home">Homepage</button> <button id="modalLogoutBtn" class="modal-nav-btn logout">Logout</button> </div> </div> </div> <script src="login.js"></script> <!-- Include for getUsers/saveUsers --> <script src="student.js"></script> </body> </html> /* Navbar Styles Removed */ /* Existing styles below... */ body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 20px 20px 80px 20px; /* Re-added top padding, kept side/bottom */ line-height: 1.6; } /* Prevent body scroll when modal is open */ body.modal-open { overflow: hidden; } .container { max-width: 900px; margin: 20px auto; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } h1, h2, h3, h4 { color: #333; } h1 { text-align: center; color: #4CAF50; margin-bottom: 30px; } h2 { border-bottom: 2px solid #4CAF50; padding-bottom: 5px; margin-top: 30px; } section { margin-bottom: 30px; } /* Quiz Completion Notice */ .completion-notice { background-color: #fff3cd; color: #856404; border: 1px solid #ffeeba; padding: 15px; margin-bottom: 20px; border-radius: 5px; text-align: center; } .completion-notice p { margin: 0; font-weight: bold; } .lesson, .quiz { border: 1px solid #ddd; background-color: #f9f9f9; padding: 15px; margin-bottom: 15px; border-radius: 5px; position: relative; /* Needed for absolute positioning of button if desired */ } .lesson h4, .quiz h4 { margin-top: 0; color: #555; } /* Lesson Read Button */ .mark-read-btn { padding: 6px 12px; background-color: #17a2b8; /* Teal */ color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; margin-top: 10px; transition: background-color 0.3s ease, opacity 0.3s ease; display: inline-block; /* Or block if needed */ } .mark-read-btn:hover:not(:disabled) { background-color: #138496; } .mark-read-btn:disabled { background-color: #a6d8de; /* Lighter teal */ cursor: not-allowed; opacity: 0.7; } .btn { padding: 10px 20px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; margin-top: 10px; transition: background-color 0.3s ease; } .btn:hover:not(:disabled) { /* Ensure hover only applies when not disabled */ background-color: #45a049; } .btn:disabled { background-color: #ccc; cursor: not-allowed; opacity: 0.6; /* Slightly adjust opacity */ } .submit-quiz-btn { background-color: #007bff; } .submit-quiz-btn:hover:not(:disabled) { background-color: #0056b3; } .quiz-question { margin-top: 20px; padding-top: 15px; border-top: 1px solid #eee; } .quiz-question p strong { display: inline-block; /* Allow span to sit next to it */ margin-bottom: 10px; } /* Answer Input Styling */ .quiz-question input[type="text"] { width: calc(100% - 22px); padding: 10px; margin-top: 5px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } .quiz-question .choices label { display: block; margin-bottom: 8px; cursor: pointer; padding: 8px; border-radius: 4px; background-color: #e9e9e9; transition: background-color 0.2s ease; } .quiz-question .choices label:hover { background-color: #dcdcdc; } .quiz-question .choices input[type="radio"] { margin-right: 10px; vertical-align: middle; } /* Score Display */ .quiz-score-area { margin-top: 20px; padding: 15px; background-color: #e3f2fd; border: 1px solid #bbdefb; border-radius: 5px; text-align: center; } .quiz-score-area h3 { margin: 0; color: #0d47a1; } /* Hide elements */ .hidden { display: none; } /* --- Feedback Styles --- */ .feedback-mark { /* Container for the mark */ display: inline-block; /* Allows margin */ margin-left: 8px; font-size: 1.2em; font-weight: bold; vertical-align: middle; /* Align with text */ } .correct-mark { color: green; } .incorrect-mark { color: red; } .correct-answer-highlight { background-color: #c8e6c9 !important; border: 1px solid #a5d6a7; font-weight: bold; } .correct-answer-text { display: block; margin-top: 5px; color: #2e7d32; font-style: italic; font-size: 0.9em; } .incorrect-answer-input { border-color: #e57373 !important; background-color: #ffebee; } .incorrect-choice-selected { background-color: #ffcdd2 !important; border: 1px solid #ef9a9a; text-decoration: line-through; } .quiz-question .choices label.correct-answer-highlight, .quiz-question .choices label.incorrect-choice-selected { opacity: 1; color: #333; } /* --- Analytics Button Style --- */ .analytics-btn { position: fixed; bottom: 20px; right: 20px; padding: 12px 25px; background-color: #6f42c1; /* Purple color */ color: white; border: none; border-radius: 25px; /* Pill shape */ cursor: pointer; font-size: 15px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); z-index: 1000; /* Ensure it's above other content */ transition: background-color 0.3s ease, transform 0.2s ease, opacity 0.3s ease; } .analytics-btn:hover:not(:disabled) { background-color: #5a379b; transform: translateY(-2px); /* Slight lift on hover */ } .analytics-btn:disabled { background-color: #b3a1d1; /* Lighter purple when disabled */ cursor: not-allowed; opacity: 0.7; transform: none; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } /* --- Fullscreen Modal Styles --- */ .modal { display: none; /* Hidden by default */ position: fixed; z-index: 1001; /* Above the button */ left: 0; top: 0; width: 100%; /* Full width */ height: 100%; /* Full height */ overflow: hidden; /* Prevent body scroll when modal is open */ background-color: #fefefe; /* Use solid background instead of overlay */ } .modal-content { background-color: transparent; /* Make content background transparent */ margin: 0; /* Remove margin */ padding: 30px; /* Adjust padding for fullscreen */ border: none; /* Remove border */ width: 100%; /* Full width */ height: 100%; /* Full height */ max-width: none; /* Remove max-width */ border-radius: 0; /* Remove border-radius */ box-shadow: none; /* Remove box-shadow */ position: relative; overflow-y: auto; /* Allow content scrolling */ box-sizing: border-box; /* Include padding in height/width */ } /* Close Button Styles Removed */ /* .close-btn { ... } */ .modal h2 { text-align: center; color: #6f42c1; margin-top: 10px; /* Add some top margin */ margin-bottom: 30px; /* Increase bottom margin */ font-size: 1.8em; /* Larger title */ } .analytics-section { margin-bottom: 25px; /* Increase spacing */ padding-bottom: 20px; border-bottom: 1px solid #eee; max-width: 800px; /* Limit width of content sections */ margin-left: auto; /* Center content sections */ margin-right: auto; } .analytics-section:last-of-type { /* Remove border from last section before nav */ border-bottom: none; } .analytics-section h3 { color: #444; margin-bottom: 15px; font-size: 1.3em; /* Larger section titles */ } #quizScoresList, #lessonProgressList { /* Apply to both lists */ list-style: none; padding: 0; max-height: 30vh; /* Adjust max height relative to viewport */ overflow-y: auto; } #quizScoresList li, #lessonProgressList li { /* Apply to both lists */ background-color: #f9f9f9; padding: 10px 15px; /* Increase padding */ margin-bottom: 8px; /* Increase spacing */ border-radius: 4px; border: 1px solid #eee; font-size: 1em; /* Slightly larger font */ display: flex; /* Use flex for alignment */ justify-content: space-between; /* Space out title and status/score */ align-items: center; } #quizScoresList li strong, #lessonProgressList li strong { /* Apply to both lists */ color: #333; margin-right: 10px; /* Add space between title and value */ } /* Lesson Progress Specific Styles in Modal */ #lessonProgressList .status-completed, #lessonProgressList .status-read { color: green; font-weight: bold; } #lessonProgressList .status-not-started { color: grey; } #overallGrade { font-size: 1.4em; /* Larger grade font */ font-weight: bold; color: #28a745; } /* Styles for Navigation Buttons inside Modal */ .modal-navigation { margin-top: 30px; /* Increase space */ padding-top: 25px; border-top: 1px solid #ccc; /* Slightly darker border */ text-align: center; display: flex; justify-content: center; gap: 20px; /* Increase gap */ max-width: 800px; /* Limit width */ margin-left: auto; margin-right: auto; } .modal-nav-btn { padding: 12px 25px; /* Larger buttons */ border: none; border-radius: 5px; cursor: pointer; font-size: 1em; /* Larger font */ font-weight: bold; transition: background-color 0.3s ease, transform 0.2s ease; } .modal-nav-btn.dashboard { /* Style for the new dashboard button */ background-color: #ffc107; /* Yellow/Orange color */ color: #333; } .modal-nav-btn.dashboard:hover { background-color: #e0a800; transform: translateY(-1px); } .modal-nav-btn.home { background-color: #17a2b8; color: white; } .modal-nav-btn.home:hover { background-color: #138496; transform: translateY(-1px); } .modal-nav-btn.logout { background-color: #dc3545; color: white; } .modal-nav-btn.logout:hover { background-color: #c82333; transform: translateY(-1px); } if (!loggedInUsername || !loggedInRole || loggedInRole !== 'student') { alert('Access denied. Please log in as a student.'); // Ensure login.js is loaded if redirecting to index.html which might be the login page window.location.href = 'index.html'; // Redirect if not a student return; } // --- End Login Check --- // --- DOM Elements --- const createdLessonsDiv = document.getElementById('createdLessons'); const createdQuizzesDiv = document.getElementById('createdQuizzes'); const quizCompletionStatusDiv = document.getElementById('quiz-completion-status'); // Analytics Elements const analyticsBtn = document.getElementById('analyticsBtn'); const analyticsModal = document.getElementById('analyticsModal'); const lastAccessedSpan = document.getElementById('lastAccessed'); const lessonProgressListUl = document.getElementById('lessonProgressList'); // Added for modal const quizScoresListUl = document.getElementById('quizScoresList'); const overallGradeSpan = document.getElementById('overallGrade'); // Modal Navigation Elements const modalDashboardBtn = document.getElementById('modalDashboardBtn'); const modalHomeBtn = document.getElementById('modalHomeBtn'); const modalLogoutBtn = document.getElementById('modalLogoutBtn'); // --- Storage Keys --- const storageKeys = { users: 'users', // Key for the main user object containing all users lessons: 'createdLessons', // Global lessons created by teacher quizzes: 'createdQuizzes', // Global quizzes created by teacher lastAccessed: `studentLastAccessed_${loggedInUsername}` // Student-specific last access }; // --- Utility Functions (Ensure login.js is included for getUsers/saveUsers) --- // getUsers() and saveUsers() are assumed to be available from login.js if (typeof getUsers !== 'function' || typeof saveUsers !== 'function') { console.error("Error: getUsers or saveUsers function not found. Make sure login.js is included before student.js"); alert("A critical error occurred. Please try refreshing the page or logging in again."); // Optionally redirect or disable functionality return; } // --- Get Current Student Data --- // Helper to safely get the current student's data object from the main users store function getCurrentStudentData() { const users = getUsers(); const studentData = users[loggedInUsername]; if (studentData && studentData.role === 'student') { // Initialize progress arrays if they don't exist (robustness) if (!Array.isArray(studentData.quizScores)) { studentData.quizScores = []; } if (!Array.isArray(studentData.lessonProgress)) { studentData.lessonProgress = []; } return studentData; } console.error("Could not find valid student data for username:", loggedInUsername); // Maybe redirect or show error if student data is corrupted/missing alert("Error: Could not load your data. Please try logging in again."); logout(); // Log out if data is missing return null; // Return null to indicate failure } // --- Track Last Accessed --- function recordLastAccessed() { const now = new Date(); localStorage.setItem(storageKeys.lastAccessed, now.toISOString()); } // --- Lesson Display & Tracking --- function updateCreatedLessons() { const globalLessons = JSON.parse(localStorage.getItem(storageKeys.lessons) || '[]'); const studentData = getCurrentStudentData(); if (!studentData) return; // Stop if student data failed to load createdLessonsDiv.innerHTML = ''; // Clear existing lessons if (globalLessons.length === 0) { createdLessonsDiv.innerHTML = '<p>No lessons available.</p>'; return; } globalLessons.forEach((lesson, index) => { // Basic validation const title = lesson.title || 'Untitled Lesson'; const content = lesson.content || 'No content.'; const lessonId = `lesson-${index}`; // Simple ID based on index // Check student's progress for this lesson const progressEntry = studentData.lessonProgress.find(p => p.lessonName === title); const isRead = progressEntry && (progressEntry.status === 'read' || progressEntry.status === 'completed'); // Consider both 'read' and 'completed' as read const lessonDiv = document.createElement('div'); lessonDiv.classList.add('lesson'); lessonDiv.innerHTML = ` <h4>${title}</h4> <p>${content}</p> <button class="mark-read-btn" data-lesson-title="${title}" ${isRead ? 'disabled' : ''}> ${isRead ? 'Read' : 'Mark as Read'} </button> `; createdLessonsDiv.appendChild(lessonDiv); }); } // --- Event Delegation for Lesson Buttons --- createdLessonsDiv.addEventListener('click', (event) => { const target = event.target; if (target.classList.contains('mark-read-btn') && !target.disabled) { const lessonTitle = target.dataset.lessonTitle; markLessonAsRead(lessonTitle, target); } }); function markLessonAsRead(lessonTitle, buttonElement) { const users = getUsers(); const studentData = users[loggedInUsername]; // Get current data again if (!studentData) { alert("Error updating lesson status."); return; } // Ensure lessonProgress array exists if (!Array.isArray(studentData.lessonProgress)) { studentData.lessonProgress = []; } // Find the progress entry or create it let progressEntry = studentData.lessonProgress.find(p => p.lessonName === lessonTitle); if (progressEntry) { progressEntry.status = 'read'; // Update status to 'read' } else { // Add new entry if it doesn't exist studentData.lessonProgress.push({ lessonName: lessonTitle, status: 'read' }); } // Save the updated users object saveUsers(users); // Update the button state buttonElement.disabled = true; buttonElement.textContent = 'Read'; console.log(`Lesson "${lessonTitle}" marked as read for ${loggedInUsername}`); // Optionally, update the analytics modal if it's open if (analyticsModal.style.display === 'block') { populateAnalyticsData(); } } // --- Quiz Display and Taking Logic --- function updateCreatedQuizzes() { console.log("Updating created quizzes display for student:", loggedInUsername); const globalQuizzes = JSON.parse(localStorage.getItem(storageKeys.quizzes) || '[]'); const studentData = getCurrentStudentData(); if (!studentData) return; // Stop if student data failed to load createdQuizzesDiv.innerHTML = ''; // Clear previous content if (globalQuizzes.length === 0) { createdQuizzesDiv.innerHTML = '<p>No quizzes available.</p>'; checkAndEnforceQuizCompletion(); // Check even if no quizzes return; } globalQuizzes.forEach((quiz, index) => { // Basic validation of quiz structure if (!quiz || typeof quiz.quizTitle === 'undefined' || !Array.isArray(quiz.questions)) { console.warn(`Skipping invalid quiz data at index ${index}:`, quiz); return; // Skip rendering this invalid quiz } const quizTitle = quiz.quizTitle || 'Untitled Quiz'; // Check if this student has taken this quiz by looking in their main data const resultData = studentData.quizScores.find(score => score.quizName === quizTitle); const hasTaken = !!resultData; // True if resultData is found console.log(`Quiz Index: ${index}, Title: ${quizTitle}, Has Taken: ${hasTaken}`); const quizDiv = document.createElement('div'); quizDiv.classList.add('quiz'); quizDiv.id = `quiz-${index}`; quizDiv.innerHTML = ` <h4>${quizTitle}</h4> <p>Type: ${quiz.quizType || 'N/A'}</p> <p>Number of questions: ${quiz.questions.length}</p> <button class="btn take-quiz-btn" data-quiz-index="${index}" ${hasTaken ? 'disabled' : ''}>${hasTaken ? 'Quiz Taken' : 'Take Quiz'}</button> <div class="quiz-questions-area" id="quiz-area-${index}" style="display: none;"></div> <div class="quiz-score-area" id="score-area-${index}" style="display: ${hasTaken ? 'block' : 'none'};"> ${hasTaken && resultData ? `<h3>Your Score: ${resultData.score}%</h3>` : ''} </div> `; createdQuizzesDiv.appendChild(quizDiv); }); checkAndEnforceQuizCompletion(); // Check completion after rendering quizzes } // --- Event Delegation for Quiz Buttons --- createdQuizzesDiv.addEventListener('click', (event) => { const target = event.target; // The element that was actually clicked // Check if the clicked element is a "take-quiz-btn" or inside one const takeButton = target.closest('.take-quiz-btn'); if (takeButton && !takeButton.disabled) { console.log("Take Quiz button clicked via delegation for index:", takeButton.dataset.quizIndex); startQuiz(takeButton); // Pass the button element itself return; // Stop further processing for this click } // Check if the clicked element is a "submit-quiz-btn" or inside one const submitButton = target.closest('.submit-quiz-btn'); if (submitButton && !submitButton.disabled) { console.log("Submit Quiz button clicked via delegation for index:", submitButton.dataset.quizIndex); submitQuiz(submitButton); // Pass the button element itself return; // Stop further processing for this click } }); function startQuiz(buttonElement) { // Accepts the button element directly const quizIndex = buttonElement.dataset.quizIndex; console.log('startQuiz called for index:', quizIndex); const globalQuizzes = JSON.parse(localStorage.getItem(storageKeys.quizzes) || '[]'); // Validate quiz index and data if (quizIndex === undefined || !globalQuizzes[quizIndex] || !Array.isArray(globalQuizzes[quizIndex].questions)) { console.error(`Invalid quiz index or data for index ${quizIndex}`); alert("Error: Could not load quiz data."); return; } const quiz = globalQuizzes[quizIndex]; const questionsArea = document.getElementById(`quiz-area-${quizIndex}`); const scoreArea = document.getElementById(`score-area-${quizIndex}`); const takeQuizButton = buttonElement; // The button that was clicked if (!questionsArea || !scoreArea) { console.error(`Could not find questionsArea or scoreArea for quiz index ${quizIndex}`); alert("Error: Could not display quiz."); return; } // Hide other open quiz forms if any, and show their "Take Quiz" buttons document.querySelectorAll('.quiz-questions-area').forEach(area => { if (area.id !== `quiz-area-${quizIndex}`) { area.style.display = 'none'; const otherQuizIndex = area.id.split('-')[2]; const otherTakeButton = document.querySelector(`.take-quiz-btn[data-quiz-index="${otherQuizIndex}"]`); // Check if the other quiz has been taken using the main student data const studentData = getCurrentStudentData(); const otherQuizTitle = globalQuizzes[otherQuizIndex]?.quizTitle; const otherQuizTaken = studentData?.quizScores.some(s => s.quizName === otherQuizTitle); if (otherTakeButton && !otherQuizTaken) { // Only show if not already taken otherTakeButton.style.display = 'inline-block'; } } }); questionsArea.innerHTML = ''; // Clear previous content if any scoreArea.innerHTML = ''; // Clear score area scoreArea.style.display = 'none'; // Hide score area questionsArea.style.display = 'block'; // Show questions area takeQuizButton.style.display = 'none'; // Hide the clicked "Take Quiz" button quiz.questions.forEach((question, qIndex) => { // Basic validation of question structure if (!question || typeof question.questionText === 'undefined') { console.warn(`Skipping invalid question data at qIndex ${qIndex} for quiz ${quizIndex}:`, question); return; // Skip rendering invalid question } const questionDiv = document.createElement('div'); questionDiv.classList.add('quiz-question'); let answerHtml = ''; if (quiz.quizType === 'identification') { answerHtml = `<label for="answer-${quizIndex}-${qIndex}">Your Answer:</label><input type="text" id="answer-${quizIndex}-${qIndex}" name="answer-${quizIndex}-${qIndex}" required>`; } else if (quiz.quizType === 'multiple-choice') { answerHtml = '<div class="choices">'; const choices = Array.isArray(question.choices) ? question.choices : []; if (choices.length === 0) { console.warn(`No choices found for multiple-choice question qIndex ${qIndex} in quiz ${quizIndex}`); } // Shuffle choices before displaying (optional, but good practice) const shuffledChoices = [...choices].sort(() => Math.random() - 0.5); shuffledChoices.forEach((choice, cIndex) => { // Ensure choice text is displayed correctly, even if null/undefined const choiceText = (choice !== null && choice !== undefined) ? choice : `Choice ${cIndex + 1}`; // Use the original choice value for the radio button's value attribute const choiceValue = (choice !== null && choice !== undefined) ? choice : `__choice_${cIndex}__`; // Handle potential null/undefined values answerHtml += `<label><input type="radio" name="answer-${quizIndex}-${qIndex}" value="${choiceValue}" id="choice-${quizIndex}-${qIndex}-${cIndex}" required> ${choiceText}</label>`; }); answerHtml += '</div>'; } else { console.warn(`Unsupported quiz type "${quiz.quizType}" for question qIndex ${qIndex} in quiz ${quizIndex}`); answerHtml = '<p><em>Unsupported question type.</em></p>'; } questionDiv.innerHTML = `<p><strong>${qIndex + 1}. ${question.questionText}</strong></p>${answerHtml}`; questionsArea.appendChild(questionDiv); }); // Add submit button only if valid questions were rendered if (questionsArea.children.length > 0 && !questionsArea.querySelector('.submit-quiz-btn')) { const submitButton = document.createElement('button'); submitButton.textContent = 'Submit Quiz'; submitButton.classList.add('btn', 'submit-quiz-btn'); submitButton.dataset.quizIndex = quizIndex; // Listener is handled by delegation, no need to add here questionsArea.appendChild(submitButton); } else if (questionsArea.children.length === 0) { // Handle case where a quiz has no valid questions questionsArea.innerHTML = '<p><em>No valid questions found for this quiz. Cannot submit.</em></p>'; // Optionally re-display the "Take Quiz" button or show a message takeQuizButton.style.display = 'inline-block'; // Show button again takeQuizButton.disabled = true; // Disable it as it's problematic takeQuizButton.textContent = 'Quiz Error'; } } function submitQuiz(buttonElement) { // Accepts the button element directly const quizIndex = buttonElement.dataset.quizIndex; console.log('submitQuiz called for index:', quizIndex); const globalQuizzes = JSON.parse(localStorage.getItem(storageKeys.quizzes) || '[]'); // Validate quiz index and data if (quizIndex === undefined || !globalQuizzes[quizIndex] || !Array.isArray(globalQuizzes[quizIndex].questions)) { console.error(`Invalid quiz index or data for submission: ${quizIndex}`); alert("Error: Could not submit quiz data."); return; } const quiz = globalQuizzes[quizIndex]; const questionsArea = buttonElement.closest('.quiz-questions-area'); // Find parent question area const scoreArea = document.getElementById(`score-area-${quizIndex}`); if (!questionsArea || !scoreArea) { console.error("Could not find questions or score area for quiz index:", quizIndex); alert("Error submitting quiz. Please try again."); return; } let score = 0; let allAnswered = true; // Filter out potentially invalid questions from the quiz data before counting/iterating const validQuestions = quiz.questions.filter(q => q && typeof q.questionText !== 'undefined'); const totalValidQuestions = validQuestions.length; // Clear previous feedback before evaluating questionsArea.querySelectorAll('.feedback-mark, .correct-answer-text').forEach(el => el.remove()); questionsArea.querySelectorAll('.correct-answer-highlight, .incorrect-choice-selected, .incorrect-answer-input').forEach(el => { el.classList.remove('correct-answer-highlight', 'incorrect-choice-selected', 'incorrect-answer-input'); if (el.tagName === 'LABEL') { el.style.textDecoration = 'none'; } }); // Re-enable inputs temporarily for validation, disable later questionsArea.querySelectorAll('input[type="text"], input[type="radio"]').forEach(input => input.disabled = false); // Iterate through each *valid* question defined in the quiz data validQuestions.forEach((question, qIndex) => { let studentAnswer = null; const correctAnswer = question.correctAnswer; // Find the corresponding question element in the DOM based on its position among valid questions const questionElement = questionsArea.querySelector(`.quiz-question:nth-child(${qIndex + 1})`); // Assumes DOM order matches validQuestions order const questionNumberElement = questionElement ? questionElement.querySelector('p strong') : null; if (!questionElement || !questionNumberElement) { console.warn(`Could not find question element in DOM for valid qIndex ${qIndex} during submission in quiz ${quizIndex}`); allAnswered = false; // Consider it unanswered if the element is missing return; // Skip this question } // --- Get Student Answer and Validate --- if (quiz.quizType === 'identification') { const inputElement = questionElement.querySelector(`input[type="text"]`); if (!inputElement) { console.warn(`Missing input element for identification qIndex ${qIndex}`); allAnswered = false; return; } studentAnswer = inputElement.value.trim(); if (studentAnswer === "") allAnswered = false; else if (correctAnswer !== undefined && correctAnswer !== null && studentAnswer.toLowerCase() === String(correctAnswer).toLowerCase()) score++; } else if (quiz.quizType === 'multiple-choice') { const checkedRadio = questionElement.querySelector(`input[type="radio"]:checked`); if (!checkedRadio) allAnswered = false; else { studentAnswer = checkedRadio.value; if (studentAnswer === String(correctAnswer)) score++; } } }); // End of validQuestions.forEach for validation // --- Check if all questions were answered --- if (!allAnswered) { alert('Please answer all questions before submitting.'); questionsArea.querySelectorAll('input[type="text"], input[type="radio"]').forEach(input => input.disabled = false); // Re-enable return; // Stop submission } // --- If all answered, proceed to show feedback and save --- // Disable all inputs now that submission is final questionsArea.querySelectorAll('input[type="text"], input[type="radio"]').forEach(input => input.disabled = true); // Iterate again through valid questions to apply feedback styles validQuestions.forEach((question, qIndex) => { const questionElement = questionsArea.querySelector(`.quiz-question:nth-child(${qIndex + 1})`); if (!questionElement) return; const questionNumberElement = questionElement.querySelector('p strong'); const correctAnswer = question.correctAnswer; let studentAnswer = null; let isCorrect = false; // Re-evaluate for feedback if (quiz.quizType === 'identification') { const inputElement = questionElement.querySelector(`input[type="text"]`); studentAnswer = inputElement ? inputElement.value.trim() : ''; isCorrect = (correctAnswer !== undefined && correctAnswer !== null && studentAnswer.toLowerCase() === String(correctAnswer).toLowerCase()); if (!isCorrect && inputElement) { inputElement.classList.add('incorrect-answer-input'); if (correctAnswer !== undefined && correctAnswer !== null) { const correctAnswerSpan = document.createElement('span'); correctAnswerSpan.classList.add('correct-answer-text'); correctAnswerSpan.textContent = `Correct answer: ${correctAnswer}`; inputElement.parentNode.insertBefore(correctAnswerSpan, inputElement.nextSibling); } } } else if (quiz.quizType === 'multiple-choice') { const radioButtons = questionElement.querySelectorAll(`input[type="radio"]`); const checkedRadio = questionElement.querySelector(`input[type="radio"]:checked`); studentAnswer = checkedRadio ? checkedRadio.value : null; isCorrect = (studentAnswer === String(correctAnswer)); radioButtons.forEach(radio => { const choiceLabel = radio.closest('label'); if (!choiceLabel) return; // Highlight the correct answer's label if (radio.value === String(correctAnswer)) { choiceLabel.classList.add('correct-answer-highlight'); } // If this radio was checked and it's incorrect, style it if (radio.checked && !isCorrect) { choiceLabel.classList.add('incorrect-choice-selected'); choiceLabel.style.textDecoration = 'line-through'; // Add line-through } }); } // Add checkmark or cross if (questionNumberElement) { const feedbackMark = document.createElement('span'); feedbackMark.classList.add('feedback-mark'); feedbackMark.textContent = isCorrect ? '✓' : '✗'; feedbackMark.classList.add(isCorrect ? 'correct-mark' : 'incorrect-mark'); // Ensure no duplicate marks const existingMark = questionNumberElement.querySelector('.feedback-mark'); if (existingMark) existingMark.remove(); questionNumberElement.appendChild(feedbackMark); } }); // End of feedback loop // --- Display Final Score and Save --- const percentageScore = totalValidQuestions > 0 ? Math.round((score / totalValidQuestions) * 100) : 0; scoreArea.innerHTML = `<h3>Your Score: ${percentageScore}%</h3>`; // Show percentage scoreArea.style.display = 'block'; buttonElement.disabled = true; // Disable the submit button permanently buttonElement.textContent = 'Submitted'; // Find the original "Take Quiz" button again and ensure it's disabled and text updated const originalTakeButton = document.querySelector(`.take-quiz-btn[data-quiz-index="${quizIndex}"]`); if (originalTakeButton) { originalTakeButton.disabled = true; originalTakeButton.textContent = 'Quiz Taken'; originalTakeButton.style.display = 'inline-block'; // Ensure it's visible again } // Save the result (percentage score) to the main student data saveQuizResult(quiz.quizTitle || 'Untitled Quiz', percentageScore); checkAndEnforceQuizCompletion(); // Update analytics button status } function saveQuizResult(quizTitle, percentageScore) { const users = getUsers(); const studentData = users[loggedInUsername]; if (!studentData) { console.error("Cannot save quiz result: Student data not found."); return; } // Ensure quizScores array exists if (!Array.isArray(studentData.quizScores)) { studentData.quizScores = []; } // Find if a result for this quiz already exists const existingResultIndex = studentData.quizScores.findIndex(q => q.quizName === quizTitle); const newResult = { quizName: quizTitle, score: percentageScore, // Store the percentage score timestamp: new Date().toISOString() }; if (existingResultIndex > -1) { // Update existing result studentData.quizScores[existingResultIndex] = newResult; console.log("Quiz result updated for:", quizTitle, "for student:", loggedInUsername); } else { // Add new result studentData.quizScores.push(newResult); console.log("Quiz result saved for:", quizTitle, "for student:", loggedInUsername); } // Save the entire updated users object back to localStorage saveUsers(users); } // --- Quiz Completion Enforcement --- function checkAndEnforceQuizCompletion() { const globalQuizzes = JSON.parse(localStorage.getItem(storageKeys.quizzes) || '[]'); const studentData = getCurrentStudentData(); if (!studentData) return; // Can't check if data is missing let allValidQuizzesTaken = true; // Filter global quizzes to only include valid ones const validGlobalQuizzes = globalQuizzes.filter(quiz => quiz && typeof quiz.quizTitle !== 'undefined' && Array.isArray(quiz.questions)); if (validGlobalQuizzes.length === 0) { allValidQuizzesTaken = true; // No valid quizzes to take } else { for (const quiz of validGlobalQuizzes) { const quizTitle = quiz.quizTitle; // Check if THIS student has taken this VALID quiz by looking in their data const hasTaken = studentData.quizScores.some(score => score.quizName === quizTitle); if (!hasTaken) { allValidQuizzesTaken = false; break; // Found an untaken, valid quiz } } } console.log("All valid quizzes taken by", loggedInUsername, ":", allValidQuizzesTaken); if (allValidQuizzesTaken) { quizCompletionStatusDiv.classList.add('hidden'); analyticsBtn.disabled = false; // Enable button console.log("Analytics button enabled for", loggedInUsername); } else { quizCompletionStatusDiv.classList.remove('hidden'); analyticsBtn.disabled = true; // Disable button console.log("Analytics button disabled for", loggedInUsername); } } // --- Analytics Modal Logic --- function openAnalyticsModal() { checkAndEnforceQuizCompletion(); // Re-check before opening if (analyticsBtn.disabled) { alert("Please complete all available quizzes before viewing analytics."); return; } populateAnalyticsData(); analyticsModal.style.display = 'block'; document.body.classList.add('modal-open'); // Prevent body scroll } function closeAnalyticsModal() { analyticsModal.style.display = 'none'; document.body.classList.remove('modal-open'); // Allow body scroll again } function populateAnalyticsData() { const studentData = getCurrentStudentData(); if (!studentData) return; // Stop if data is missing // Get last accessed time for this student const lastAccessedISO = localStorage.getItem(storageKeys.lastAccessed); lastAccessedSpan.textContent = lastAccessedISO ? new Date(lastAccessedISO).toLocaleString() : 'Not recorded yet.'; // --- Populate Lesson Progress --- lessonProgressListUl.innerHTML = ''; // Clear previous list const globalLessons = JSON.parse(localStorage.getItem(storageKeys.lessons) || '[]'); if (globalLessons.length > 0) { // Ensure studentData.lessonProgress exists const lessonProgress = Array.isArray(studentData.lessonProgress) ? studentData.lessonProgress : []; globalLessons.forEach(lesson => { const title = lesson.title || 'Untitled Lesson'; const progressEntry = lessonProgress.find(p => p.lessonName === title); const status = progressEntry ? progressEntry.status : 'not started'; const statusClass = `status-${status.toLowerCase().replace(' ', '-')}`; const listItem = document.createElement('li'); listItem.innerHTML = `<strong>${title}:</strong> <span class="${statusClass}">${status}</span>`; lessonProgressListUl.appendChild(listItem); }); } else { lessonProgressListUl.innerHTML = '<li>No lessons available.</li>'; } // --- Populate Quiz Scores --- quizScoresListUl.innerHTML = ''; // Clear previous list const quizScores = studentData.quizScores; // Get scores directly from student data if (quizScores.length === 0) { quizScoresListUl.innerHTML = '<li>No quizzes taken yet.</li>'; overallGradeSpan.textContent = 'N/A'; return; // Exit early if no scores } let totalPercentage = 0; let numberOfQuizzesTaken = 0; // Sort scores alphabetically by quiz name for consistent display quizScores.sort((a, b) => a.quizName.localeCompare(b.quizName)); quizScores.forEach(result => { // Ensure result has expected properties before calculating if (result && typeof result.quizName !== 'undefined' && typeof result.score === 'number') { const listItem = document.createElement('li'); listItem.innerHTML = `<strong>${result.quizName}:</strong> ${result.score}%`; quizScoresListUl.appendChild(listItem); totalPercentage += result.score; numberOfQuizzesTaken++; } else { console.warn(`Invalid score data found in student results:`, result); } }); // Calculate overall grade as the average of the percentage scores of quizzes taken overallGradeSpan.textContent = numberOfQuizzesTaken > 0 ? `${(totalPercentage / numberOfQuizzesTaken).toFixed(1)}%` : 'N/A'; } // --- Navigation Logic --- function goToHomepage() { // Determine homepage based on role if necessary, or assume a general one window.location.href = 'homepage.html'; // Assuming homepage.html exists } function logout() { sessionStorage.removeItem('loggedInUsername'); sessionStorage.removeItem('loggedInRole'); // Also clear student-specific items if desired localStorage.removeItem(storageKeys.lastAccessed); alert('You have been logged out.'); window.location.href = 'index.html'; // Redirect to login page (index.html) } // --- Event Listeners --- if (analyticsBtn) analyticsBtn.addEventListener('click', openAnalyticsModal); if (modalDashboardBtn) modalDashboardBtn.addEventListener('click', closeAnalyticsModal); if (modalHomeBtn) modalHomeBtn.addEventListener('click', goToHomepage); if (modalLogoutBtn) modalLogoutBtn.addEventListener('click', logout); // --- Initial Load --- recordLastAccessed(); // Record access time on load updateCreatedLessons(); updateCreatedQuizzes(); // Initial population and completion check for this student (parent) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Parent Access - Find Student</title> <link rel="stylesheet" href="parent.css"> <!-- Use new CSS --> </head> <body> <div class="access-container"> <h2>Find Student Record</h2> <p>Please enter the student's details to view their progress.</p> <input type="text" id="studentFullName" placeholder="Enter Student's Full Name"> <input type="text" id="studentIdNumber" placeholder="Enter Student's ID Number"> <button onclick="verifyStudent()">View Dashboard</button> <p id="accessErrorMessage" class="error-message"></p> <p><a href="login.html">Back to Login</a></p> </div> <script src="parent.js"></script> <!-- Use new JS --> <script src="login.js"></script> <!-- Include login.js for getUsers function --> </body> </html> /* Reusing and adapting styles from login.css */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #e2a2a2, #f4f4f4); /* Slightly different gradient for parent section */ display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 20px; } .access-container, .dashboard-container { background: #fff; width: 100%; max-width: 600px; /* Wider for dashboard */ padding: 30px; border-radius: 12px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); text-align: left; /* Align text left for dashboard content */ } .access-container { max-width: 450px; /* Keep access container similar to login */ text-align: center; /* Center align access form */ } h2, h3 { color: #333; margin-bottom: 20px; font-weight: bold; } h2 { font-size: 24px; text-align: center; /* Center main headings */ } h3 { font-size: 20px; margin-top: 25px; /* Add space above subheadings */ border-bottom: 1px solid #eee; /* Separator line */ padding-bottom: 10px; } input, button { /* Styles for access page */ width: 100%; padding: 15px; margin: 10px 0; border-radius: 8px; border: 1px solid #ddd; font-size: 16px; outline: none; transition: all 0.3s ease; } input:focus, button:focus { border-color: #c82333; /* Parent theme color */ box-shadow: 0 0 8px rgba(200, 35, 51, 0.3); } button { background: #c82333; /* Parent theme color */ color: white; border: none; cursor: pointer; font-weight: bold; transition: background 0.3s ease; } button:hover { background: #a81d2a; } .error-message { color: red; font-size: 14px; margin-top: 10px; min-height: 1.2em; /* Reserve space */ text-align: center; } p { font-size: 14px; color: #666; margin-top: 15px; line-height: 1.6; } .access-container p { text-align: center; /* Center paragraph text in access container */ } p a { color: #c82333; /* Parent theme color */ text-decoration: none; } p a:hover { text-decoration: underline; } /* Dashboard specific styles */ .dashboard-container div { margin-bottom: 20px; } .dashboard-container ul { list-style: none; padding-left: 0; } .dashboard-container li { background-color: #f9f9f9; border: 1px solid #eee; padding: 10px 15px; margin-bottom: 8px; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; } .dashboard-container li span { font-weight: bold; } .lesson-progress .status-completed { color: green; } .lesson-progress .status-read { color: orange; } .lesson-progress .status-not-started { color: grey; } .student-info p { margin-top: 5px; } // ========== Parent Access Page (parent-access.html) ========== function verifyStudent() { const studentFullNameInput = document.getElementById("studentFullName"); const studentIdNumberInput = document.getElementById("studentIdNumber"); const errorMessage = document.getElementById("accessErrorMessage"); const studentFullName = studentFullNameInput.value.trim(); const studentIdNumber = studentIdNumberInput.value.trim(); errorMessage.textContent = ''; // Clear previous errors if (!studentFullName || !studentIdNumber) { errorMessage.textContent = "Please enter both the student's full name and ID number."; return; } const users = getUsers(); // Assumes getUsers() is available (from login.js) let foundStudentUsername = null; // Iterate through all users to find a matching student for (const username in users) { const userData = users[username]; if (userData.role === 'student' && userData.fullName.toLowerCase() === studentFullName.toLowerCase() && // Case-insensitive name check userData.studentId === studentIdNumber) { foundStudentUsername = username; break; // Stop searching once found } } if (foundStudentUsername) { // Student found, store the username (key) for the dashboard sessionStorage.setItem('viewingStudentUsername', foundStudentUsername); // Redirect to the dashboard window.location.href = "parent-dashboard.html"; } else { // No matching student found errorMessage.textContent = "No student record found matching the provided name and ID. Please check the details and try again."; studentFullNameInput.value = ''; // Clear fields on error studentIdNumberInput.value = ''; } } // ========== Parent Dashboard Page (parent-dashboard.html) ========== function populateDashboard() { const viewingStudentUsername = sessionStorage.getItem('viewingStudentUsername'); const dashboardTitle = document.getElementById('dashboardTitle'); // Security check: If no student username is in session storage, redirect back if (!viewingStudentUsername) { alert("No student selected. Redirecting to access page."); window.location.href = "parent-access.html"; return; // Stop execution } const users = getUsers(); // Assumes getUsers() is available const studentData = users[viewingStudentUsername]; if (!studentData || studentData.role !== 'student') { alert("Error retrieving student data. Redirecting."); sessionStorage.removeItem('viewingStudentUsername'); // Clear invalid session item window.location.href = "parent-access.html"; return; // Stop execution } // Populate Student Info document.getElementById('studentName').textContent = studentData.fullName || 'N/A'; document.getElementById('studentId').textContent = studentData.studentId || 'N/A'; dashboardTitle.textContent = `${studentData.fullName}'s Progress Dashboard`; // Populate Quiz Scores const quizList = document.getElementById('quizList'); quizList.innerHTML = ''; // Clear loading message if (studentData.quizScores && studentData.quizScores.length > 0) { studentData.quizScores.forEach(quiz => { const listItem = document.createElement('li'); listItem.innerHTML = `${quiz.quizName} <span>${quiz.score}%</span>`; quizList.appendChild(listItem); }); } else { quizList.innerHTML = '<li>No quiz scores available.</li>'; } // Populate Lesson Progress const lessonList = document.getElementById('lessonList'); lessonList.innerHTML = ''; // Clear loading message if (studentData.lessonProgress && studentData.lessonProgress.length > 0) { studentData.lessonProgress.forEach(lesson => { const listItem = document.createElement('li'); // Add class for styling based on status const statusClass = `status-${lesson.status.toLowerCase().replace(' ', '-')}`; listItem.innerHTML = `${lesson.lessonName} <span class="${statusClass}">${lesson.status}</span>`; lessonList.appendChild(listItem); }); } else { lessonList.innerHTML = '<li>No lesson progress available.</li>'; } } function logoutParent() { // Clear specific session storage items related to parent view sessionStorage.removeItem('viewingStudentUsername'); // Optionally clear parent login info if you stored it, e.g., sessionStorage.removeItem('loggedInUsername'); // Redirect to main login page window.location.href = 'index.html'; } // Run dashboard population logic only if we are on the dashboard page // Check for an element unique to the dashboard if (document.getElementById('dashboardTitle')) { document.addEventListener('DOMContentLoaded', populateDashboard); }

Okay, let's create the parent dashboard page and the necessary CSS and JavaScript logic.

This will involve:

  1. Creating parent-dashboard.html to display the student's progress.
  2. Creating parent-dashboard.css for styling.
  3. Updating parent.js to fetch and display the data on the dashboard page.
<jitProject id="parent-student-dashboard" title="Parent Student Dashboard"> <jitFile type="file" filePath="parent-dashboard.html"> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Student Progress Dashboard</title> <link rel="stylesheet" href="parent-dashboard.css"> <!-- Use new CSS --> </head> <body> <div class="dashboard-container"> <h2 id="dashboardTitle">Student Progress Dashboard</h2>
        <div class="student-info">
            <h3>Student Information</h3>
            <p><strong>Name:</strong> <span id="studentName">Loading...</span></p>
            <p><strong>ID Number:</strong> <span id="studentId">Loading...</span></p>
        </div>

        <div class="quiz-scores">
            <h3>Quiz Scores</h3>
            <ul id="quizList">
                <li>Loading scores...</li>
            </ul>
        </div>

        <div class="lesson-progress">
            <h3>Lesson Progress</h3>
            <ul id="lessonList">
                <li>Loading progress...</li>
            </ul>
        </div>

        <div class="navigation-buttons">
             <button onclick="window.location.href='parent-access.html'">Find Another Student</button>
             <button onclick="logoutParent()">Logout</button>
        </div>
    </div>

    <script src="login.js"></script> <!-- Include for getUsers -->
    <script src="parent.js"></script> <!-- Include for populateDashboard and logoutParent -->
</body>
</html>
</jitFile> <jitFile type="file" filePath="parent-dashboard.css"> /* Reusing and adapting styles from parent.css */ * { margin: 0; padding: 0; box-sizing: border-box; }
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background: linear-gradient(135deg, #a2c4e2, #f4f4f4); /* Slightly different gradient */
    display: flex;
    justify-content: center;
    align-items: flex-start; /* Align top */
    min-height: 100vh;
    padding: 30px 20px; /* Add more padding */
}

.dashboard-container {
    background: #fff;
    width: 100%;
    max-width: 700px; /* Wider for dashboard content */
    padding: 30px 40px; /* More padding inside */
    border-radius: 12px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
    text-align: left; /* Align text left for dashboard content */
}

h2, h3 {
    color: #333;
    margin-bottom: 20px;
    font-weight: bold;
}

h2 {
    font-size: 26px; /* Larger title */
    text-align: center; /* Center main heading */
    color: #2a6a9e; /* Theme color */
    margin-bottom: 30px;
}

h3 {
    font-size: 20px;
    margin-top: 30px; /* Add space above subheadings */
    border-bottom: 2px solid #2a6a9e; /* Separator line */
    padding-bottom: 10px;
    color: #444;
}

.student-info p {
    font-size: 16px;
    color: #555;
    margin-top: 10px;
    line-height: 1.6;
}
.student-info p strong {
    display: inline-block;
    width: 100px; /* Align labels */
}
.student-info span {
    font-weight: normal;
    color: #333;
}


.dashboard-container div[class$="-info"],
.dashboard-container div[class$="-scores"],
.dashboard-container div[class$="-progress"] {
    margin-bottom: 30px; /* Space between sections */
}

.dashboard-container ul {
    list-style: none;
    padding-left: 0;
    margin-top: 15px;
    max-height: 300px; /* Limit height and allow scroll */
    overflow-y: auto; /* Add scroll if needed */
    border: 1px solid #eee; /* Optional border around list */
    border-radius: 5px;
    padding: 10px; /* Padding inside the list area */
}

.dashboard-container li {
    background-color: #f9f9f9;
    border: 1px solid #eee;
    padding: 12px 18px; /* More padding in list items */
    margin-bottom: 10px; /* Space between items */
    border-radius: 4px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 15px;
}
.dashboard-container li:last-child {
    margin-bottom: 0;
}

.dashboard-container li span {
    font-weight: bold;
    padding: 3px 8px; /* Padding around status/score */
    border-radius: 4px; /* Rounded corners for status/score */
    font-size: 0.9em;
}

/* Lesson Progress Status Styling */
.lesson-progress .status-completed,
.lesson-progress .status-read {
    color: #1e7e34; /* Darker green */
    background-color: #d4edda; /* Light green background */
    border: 1px solid #c3e6cb;
}
.lesson-progress .status-not-started {
    color: #6c757d; /* Grey */
    background-color: #e2e3e5; /* Light grey background */
    border: 1px solid #d6d8db;
}

/* Quiz Score Styling */
.quiz-scores li span {
    color: #004085; /* Dark blue */
    background-color: #cce5ff; /* Light blue background */
    border: 1px solid #b8daff;
}

/* Empty list message */
.dashboard-container ul li:only-child { /* Style if only the 'loading' or 'no items' message is present */
    background-color: transparent;
    border: none;
    font-style: italic;
    color: #888;
    justify-content: center;
}


/* Navigation Buttons */
.navigation-buttons {
    margin-top: 40px;
    padding-top: 20px;
    border-top: 1px solid #ddd;
    text-align: center;
    display: flex;
    justify-content: center;
    gap: 20px;
}

.navigation-buttons button {
    padding: 12px 25px;
    border-radius: 8px;
    border: none;
    font-size: 16px;
    cursor: pointer;
    font-weight: bold;
    transition: background 0.3s ease, transform 0.2s ease;
}

.navigation-buttons button:first-child { /* Find Another Student */
    background: #ffc107; /* Yellow */
    color: #333;
}
.navigation-buttons button:first-child:hover {
    background: #e0a800;
    transform: translateY(-1px);
}

.navigation-buttons button:last-child { /* Logout */
    background: #dc3545; /* Red */
    color: white;
}
.navigation-buttons button:last-child:hover {
    background: #c82333;
    transform: translateY(-1px);
}
</jitFile> <jitFile type="file" filePath="parent.js"> // ========== Parent Access Page (parent-access.html) ==========
function verifyStudent() {
    const studentFullNameInput = document.getElementById("studentFullName");
    const studentIdNumberInput = document.getElementById("studentIdNumber");
    const errorMessage = document.getElementById("accessErrorMessage");

    const studentFullName = studentFullNameInput.value.trim();
    const studentIdNumber = studentIdNumberInput.value.trim();
    errorMessage.textContent = ''; // Clear previous errors

    if (!studentFullName || !studentIdNumber) {
        errorMessage.textContent = "Please enter both the student's full name and ID number.";
        return;
    }

    // Ensure getUsers is available
    if (typeof getUsers !== 'function') {
        errorMessage.textContent = "Error: Cannot retrieve user data. Please refresh.";
        console.error("getUsers function not found. Is login.js included?");
        return*
Updated files
v1
give the full code

Okay, here is the complete code for parent.js, including the dashboard population and logout logic.

This provides the complete parent.js file along with the necessary HTML and CSS for the parent access and dashboard pages. Remember to have login.js available in the same directory as it's required for the getUsers() function.

Updated files
v2