fix everything and make it proper and functional learning management system that is connected in every dashboard on this code:
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<link rel="stylesheet" href="login.css">
</head>
<body>
<!-- Login Form -->
<div class="login-container" id="login-container">
<h2>Login</h2>
<input type="text" id="username" placeholder="Enter Username">
<input type="password" id="password" placeholder="Enter Password">
<select id="role">
<option value="">Select Role</option>
<option value="teacher">Teacher</option>
<option value="student">Junior High School Student</option>
<option value="parent">Parent</option>
</select>
<!-- Container for Teacher ID -->
<div id="loginTeacherIdContainer" class="hidden">
<input type="text" id="loginTeacherId" placeholder="Enter Teacher ID Number">
</div>
<!-- Container for Student ID -->
<div id="loginStudentIdContainer" class="hidden">
<input type="text" id="loginStudentId" placeholder="Enter Student ID Number">
</div>
<button onclick="login()">Login</button>
<p id="errorMessage" class="error-message"></p>
<p>Don't have an account? <a href="#" onclick="showSignup()">Sign up</a></p>
</div>
<!-- Sign Up Form -->
<div class="signup-container hidden" id="signup-container">
<h2>Sign Up</h2>
<input type="text" id="newUsername" placeholder="Choose a Username">
<input type="password" id="newPassword" placeholder="Choose a Password">
<select id="newRole">
<option value="">Select Role</option>
<option value="teacher">Teacher</option>
<option value="student">Junior High School Student</option>
<option value="parent">Parent</option>
</select>
<!-- Container for Teacher ID -->
<div id="signupTeacherIdContainer" class="hidden">
<input type="text" id="signupTeacherId" placeholder="Enter Teacher ID Number">
</div>
<!-- Container for Student Info (Full Name and ID) -->
<div id="signupStudentInfoContainer" class="hidden">
<input type="text" id="fullName" placeholder="Enter Full Name">
<input type="text" id="signupStudentId" placeholder="Enter Student ID Number">
</div>
<button onclick="signup()">Sign Up</button>
<p id="signupMessage" class="signup-message"></p>
<p>Already have an account? <a href="#" onclick="showLogin()">Log in</a></p>
</div>
<script src="login.js"></script>
</body>
</html>
login.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #a2c2e2, #f4f4f4);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh; /* Use min-height for pages that might grow */
padding: 20px;
}
.container, /* Generic container for homepage/dashboards */
.login-container,
.signup-container {
background: #fff;
width: 100%;
max-width: 450px; /* Slightly wider for homepage/dashboards */
padding: 30px;
border-radius: 12px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
text-align: center;
}
h1, h2 { /* Style h1 for homepage/dashboards */
color: #333;
margin-bottom: 20px;
font-size: 24px;
font-weight: bold;
}
input, select, button {
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, select:focus, button:focus {
border-color: #28a745;
box-shadow: 0 0 8px rgba(40, 167, 69, 0.3);
}
button {
background: #28a745;
color: white;
border: none;
cursor: pointer;
font-weight: bold;
transition: background 0.3s ease;
}
button:hover {
background: #218838;
}
.hidden {
display: none;
}
/* Style for the specific ID containers to ensure consistent margin */
#loginTeacherIdContainer input,
#loginStudentIdContainer input,
#signupTeacherIdContainer input,
#signupStudentInfoContainer input {
margin-top: 0; /* Inputs inside these divs already have margin */
}
/* Add margin between inputs within the student info container */
#signupStudentInfoContainer input#fullName {
margin-bottom: 10px;
}
.error-message, .signup-message {
color: red;
font-size: 14px;
margin-top: 10px;
min-height: 1.2em; /* Reserve space */
}
p {
font-size: 14px;
color: #666;
margin-top: 15px;
}
p a {
color: #28a745;
text-decoration: none;
}
p a:hover {
text-decoration: underline;
}
/* Homepage/Dashboard specific styles */
.welcome-message {
font-size: 18px;
margin-bottom: 25px;
color: #555;
}
login.js
// Retrieve users from localStorage
function getUsers() {
return JSON.parse(localStorage.getItem("users")) || {};
}
// Save users to localStorage
function saveUsers(users) {
localStorage.setItem("users", JSON.stringify(users));
}
function login() {
const username = document.getElementById("username").value.trim();
const password = document.getElementById("password").value.trim();
const role = document.getElementById("role").value;
const errorMessage = document.getElementById("errorMessage");
errorMessage.textContent = ''; // Clear previous error
// Get potential ID values
const teacherId = document.getElementById("loginTeacherId")?.value.trim();
const studentId = document.getElementById("loginStudentId")?.value.trim();
if (!username || !password || !role) {
errorMessage.textContent = "Please fill in username, password, and select a role.";
return;
}
// Role-specific ID validation
if (role === 'teacher' && !teacherId) {
errorMessage.textContent = "Please enter your Teacher ID Number.";
return;
}
if (role === 'student' && !studentId) {
errorMessage.textContent = "Please enter your Student ID Number.";
return;
}
const users = getUsers();
const userData = users[username];
// Check if user exists and credentials match
let credentialsMatch = userData &&
userData.password === password &&
userData.role === role;
// Check ID if applicable
if (credentialsMatch && role === 'teacher') {
credentialsMatch = userData.teacherId === teacherId;
}
if (credentialsMatch && role === 'student') {
credentialsMatch = userData.studentId === studentId;
}
if (credentialsMatch) {
// Store user info in sessionStorage
sessionStorage.setItem('loggedInUsername', username);
sessionStorage.setItem('loggedInRole', role);
// Store full name if user is a student
if (role === 'student' && userData.fullName) {
sessionStorage.setItem('loggedInFullName', userData.fullName);
}
// Optionally store ID if needed later
if (role === 'teacher') sessionStorage.setItem('loggedInTeacherId', teacherId);
if (role === 'student') sessionStorage.setItem('loggedInStudentId', studentId);
// Redirect to the homepage (assuming homepage.html exists)
// Make sure you have a homepage.html file or change this destination
window.location.href = "homepage.html";
} else {
errorMessage.textContent = "Incorrect username, password, role, or ID. Please try again.";
}
}
function signup() {
const newUsername = document.getElementById("newUsername").value.trim();
const newPassword = document.getElementById("newPassword").value.trim();
const newRole = document.getElementById("newRole").value;
const signupMessage = document.getElementById("signupMessage");
signupMessage.textContent = ''; // Clear previous message
// Get potential ID and name values
const teacherId = document.getElementById("signupTeacherId")?.value.trim();
const studentId = document.getElementById("signupStudentId")?.value.trim();
const fullName = document.getElementById("fullName")?.value.trim();
if (!newUsername || !newPassword || !newRole) {
signupMessage.textContent = "Please fill in username, password, and select a role.";
return;
}
// Basic password validation
if (newPassword.length < 4) {
signupMessage.textContent = "Password must be at least 4 characters long.";
return;
}
// Role-specific field validation
if (newRole === 'teacher' && !teacherId) {
signupMessage.textContent = "Teacher ID Number is required for teachers.";
return;
}
if (newRole === 'student' && (!fullName || !studentId)) {
signupMessage.textContent = "Full Name and Student ID Number are required for students.";
return;
}
const users = getUsers();
if (users[newUsername]) {
signupMessage.textContent = "Username already exists. Please choose a different one.";
return;
}
// Store new user in localStorage
const newUser = { password: newPassword, role: newRole };
if (newRole === 'teacher') {
newUser.teacherId = teacherId;
}
if (newRole === 'student') {
newUser.fullName = fullName;
newUser.studentId = studentId;
}
users[newUsername] = newUser;
saveUsers(users);
alert(`Account created successfully for ${newUsername}! Please log in.`);
// Switch back to login form
showLogin();
}
function showSignup() {
document.getElementById("signup-container").classList.remove("hidden");
document.getElementById("login-container").classList.add("hidden");
document.getElementById("errorMessage").textContent = '';
document.getElementById("signupMessage").textContent = '';
// Reset signup form fields
document.getElementById("newUsername").value = '';
document.getElementById("newPassword").value = '';
document.getElementById("newRole").value = '';
document.getElementById("signupTeacherId").value = '';
document.getElementById("fullName").value = '';
document.getElementById("signupStudentId").value = '';
// Hide role-specific fields initially
document.getElementById("signupTeacherIdContainer").classList.add("hidden");
document.getElementById("signupStudentInfoContainer").classList.add("hidden");
}
function showLogin() {
document.getElementById("login-container").classList.remove("hidden");
document.getElementById("signup-container").classList.add("hidden");
document.getElementById("errorMessage").textContent = '';
document.getElementById("signupMessage").textContent = '';
// Reset login form fields
document.getElementById("username").value = '';
document.getElementById("password").value = '';
document.getElementById("role").value = '';
document.getElementById("loginTeacherId").value = '';
document.getElementById("loginStudentId").value = '';
// Hide role-specific fields initially
document.getElementById("loginTeacherIdContainer").classList.add("hidden");
document.getElementById("loginStudentIdContainer").classList.add("hidden");
}
// Event listener for role change in LOGIN form
document.getElementById('role')?.addEventListener('change', function() {
const teacherIdContainer = document.getElementById('loginTeacherIdContainer');
const studentIdContainer = document.getElementById('loginStudentIdContainer');
const teacherIdInput = document.getElementById('loginTeacherId');
const studentIdInput = document.getElementById('loginStudentId');
// Hide both first
teacherIdContainer?.classList.add('hidden');
studentIdContainer?.classList.add('hidden');
if(teacherIdInput) teacherIdInput.value = ''; // Clear value when hidden
if(studentIdInput) studentIdInput.value = ''; // Clear value when hidden
if (this.value === 'teacher') {
teacherIdContainer?.classList.remove('hidden');
} else if (this.value === 'student') {
studentIdContainer?.classList.remove('hidden');
}
});
// Event listener for role change in SIGNUP form
document.getElementById('newRole')?.addEventListener('change', function() {
const teacherIdContainer = document.getElementById('signupTeacherIdContainer');
const studentInfoContainer = document.getElementById('signupStudentInfoContainer');
const teacherIdInput = document.getElementById('signupTeacherId');
const studentIdInput = document.getElementById('signupStudentId');
const fullNameInput = document.getElementById('fullName');
// Hide all role-specific containers first
teacherIdContainer?.classList.add('hidden');
studentInfoContainer?.classList.add('hidden');
// Clear values when hidden
if(teacherIdInput) teacherIdInput.value = '';
if(studentIdInput) studentIdInput.value = '';
if(fullNameInput) fullNameInput.value = '';
if (this.value === 'teacher') {
teacherIdContainer?.classList.remove('hidden');
} else if (this.value === 'student') {
studentInfoContainer?.classList.remove('hidden');
}
});
// Clear session storage on login page load and ensure correct form is shown
document.addEventListener('DOMContentLoaded', () => {
// Only run login page specific logic if login container exists
if (document.getElementById('login-container')) {
sessionStorage.clear(); // Clear all session storage on login page load
showLogin(); // Ensure login form is shown by default and fields are reset/hidden
}
});
homepage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #e2f0d9, #f4f4f4); /* Light green gradient */
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
padding: 20px;
text-align: center;
color: #333;
margin: 0;
}
.welcome-container {
background: #fff;
padding: 40px;
border-radius: 12px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
max-width: 500px;
width: 100%;
}
h1 {
color: #28a745; /* Green color */
margin-bottom: 15px;
font-size: 28px;
}
p {
font-size: 18px;
margin-bottom: 30px;
line-height: 1.6;
}
.dashboard-button {
display: inline-block;
padding: 15px 30px;
background: #28a745;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 18px;
font-weight: bold;
text-decoration: none;
transition: background 0.3s ease;
}
.dashboard-button:hover {
background: #218838;
}
/* Removed .logout-button styles */
</style>
</head>
<body>
<div class="welcome-container">
<h1 id="welcome-message">Welcome!</h1>
<p>You have successfully logged in. Click the button below to proceed to your dashboard.</p>
<button id="dashboard-link" class="dashboard-button">Go to Dashboard</button>
<!-- Removed Logout Button -->
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const username = sessionStorage.getItem('loggedInUsername');
const role = sessionStorage.getItem('loggedInRole');
const welcomeMessage = document.getElementById('welcome-message');
const dashboardLink = document.getElementById('dashboard-link');
// Removed reference to logoutBtn
// If no user info found, redirect to login
if (!username || !role) {
alert('You need to log in first.');
window.location.href = 'login.html';
return;
}
// Personalize welcome message
welcomeMessage.textContent = `Welcome, ${username}!`;
// Determine dashboard URL
let dashboardUrl = '';
if (role === 'teacher') {
dashboardUrl = 'teacher.html';
} else if (role === 'student') {
dashboardUrl = 'student.html';
} else if (role === 'parent') {
dashboardUrl = 'parent.html';
} else {
// Fallback if role is unexpected (redirect to login)
alert('Invalid user role detected.');
sessionStorage.removeItem('loggedInUsername'); // Clear session
sessionStorage.removeItem('loggedInRole');
window.location.href = 'login.html';
return;
}
// Set button click action
dashboardLink.onclick = () => {
window.location.href = dashboardUrl;
};
// Removed logoutBtn.onclick assignment
});
// Removed logout function
</script>
</body>
</html>
teachr.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Teacher Dashboard</title>
<link rel="stylesheet" href="teacher.css">
</head>
<body>
<div class="navbar">
<h1>Teacher Dashboard</h1>
</div>
<div class="container">
<div class="button-container">
<button class="btn" id="createLessonBtn">Create Lesson</button>
<button class="btn" id="createQuizBtn">Make Quiz</button>
<button class="btn" id="manageScoresBtn">Manage Scores & Grades</button>
<button class="btn" id="deleteLessonBtn">Delete Lesson</button>
<button class="btn" id="deleteQuizBtn">Delete Quiz</button> <!-- Delete Quiz Button Re-added -->
</div>
<h3>Created Lessons</h3>
<div id="createdLessons"></div>
<h3>Created Quizzes</h3>
<div id="createdQuizzes"></div>
</div>
<!-- Lesson Modal (Create/Edit) -->
<div id="lessonModal" class="modal">
<div class="modal-content">
<span class="close" data-modal-id="lessonModal">×</span>
<h2 id="lessonModalTitle">Create Lesson</h2>
<form id="lessonForm">
<input type="hidden" id="editLessonIndex" value="-1">
<label for="lessonTitle">Lesson Title:</label>
<input type="text" id="lessonTitle" name="lessonTitle" required><br><br>
<label for="lessonContent">Lesson Content:</label>
<textarea id="lessonContent" name="lessonContent" rows="4" required></textarea><br><br>
<button type="submit" id="lessonSubmitBtn" class="btn">Create Lesson</button>
</form>
</div>
</div>
<!-- Quiz Modal (Create/Edit) -->
<div id="quizModal" class="modal">
<div class="modal-content large">
<span class="close" data-modal-id="quizModal">×</span>
<h2 id="quizModalTitle">Create Quiz</h2>
<form id="quizForm">
<input type="hidden" id="editQuizIndex" value="-1">
<label for="quizType">Quiz Type:</label>
<select id="quizType" name="quizType" required>
<option value="multiple-choice">Multiple Choice</option>
<option value="identification">Identification</option>
</select><br><br>
<label for="quizTitle">Quiz Title:</label>
<input type="text" id="quizTitle" name="quizTitle" required><br><br>
<div id="questionsContainer"></div>
<button type="button" class="btn" id="addQuestionBtn">Add Question</button><br><br>
<button type="submit" id="quizSubmitBtn" class="btn">Create Quiz</button>
</form>
</div>
</div>
<!-- Delete Modal (Handles both Lessons and Quizzes) -->
<div id="deleteModal" class="modal">
<div class="modal-content">
<span class="close" data-modal-id="deleteModal">×</span>
<h2>Delete Item</h2> <!-- Generic Title -->
<label for="deleteType">Item Type:</label> <!-- Generic Label -->
<select id="deleteType"> <!-- Type Select Re-added -->
<option value="lesson">Lesson</option>
<option value="quiz">Quiz</option>
</select><br><br>
<label for="deleteChoice">Select Item to Delete:</label> <!-- Generic Label -->
<select id="deleteChoice"></select><br><br>
<button id="deleteBtn" class="btn delete">Delete</button>
</div>
</div>
<!-- Manage Scores Modal -->
<div id="manageScoresModal" class="modal">
<div class="modal-content large">
<span class="close" data-modal-id="manageScoresModal">×</span>
<h2>Manage Scores and Grades</h2>
<div class="manage-scores-container">
<label for="quizSelectForScores">Select Quiz:</label>
<select id="quizSelectForScores">
<option value="">-- Select a Quiz --</option>
<!-- Options populated by JS -->
</select>
<div id="studentScoresDisplay" class="scores-display-area">
<p class="no-scores-message">Select a quiz to view student scores.</p>
<!-- Table will be inserted here by JS -->
</div>
</div>
</div>
</div>
<!-- Performance Analytics Button -->
<button id="analyticsBtn" class="analytics-btn">View Performance & Analytics</button>
<!-- Performance Analytics Fullscreen Modal -->
<div id="analyticsModal" class="modal fullscreen">
<div class="modal-content">
<h2>Teacher Performance & Analytics</h2>
<div class="analytics-section">
<h3>Overview</h3>
<p><strong>Lessons Created:</strong> <span id="lessonCount">0</span></p>
<p><strong>Quizzes Created:</strong> <span id="quizCount">0</span></p>
</div>
<div class="analytics-section">
<h3>Class Performance (Example)</h3>
<p>Average Quiz Score: <span id="averageQuizScore">N/A</span></p>
</div>
<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="teacher.js"></script>
</body>
</html>
teacher.css
body {
font-family: Arial, sans-serif;
background-color: #f2f2f2;
padding: 0 20px 80px 20px; /* Remove top padding, ensure bottom padding for fixed button */
margin: 0;
line-height: 1.6;
}
/* Prevent body scroll when fullscreen modal is open */
body.modal-open {
overflow: hidden;
}
.navbar {
background-color: #4CAF50;
color: white;
padding: 15px 20px; /* Slightly more padding */
margin-bottom: 25px; /* More space */
border-radius: 0 0 5px 5px; /* Rounded bottom corners */
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.navbar h1 {
margin: 0;
text-align: center;
font-size: 1.8em;
}
.container {
background-color: #fff;
padding: 25px;
border-radius: 8px; /* More rounded */
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
max-width: 1000px; /* Wider container */
margin: 0 auto 20px auto; /* Center container, reduce bottom margin */
}
.hidden {
display: none;
}
/* --- Standard Modal Styles --- */
.modal:not(.fullscreen) { /* Apply only to non-fullscreen modals */
display: none;
position: fixed;
z-index: 100;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5); /* Darker overlay */
padding-top: 50px;
}
.modal:not(.fullscreen) .modal-content {
background-color: #fff;
margin: 3% auto; /* Adjust vertical margin */
padding: 25px 30px; /* More padding */
border: 1px solid #ccc;
border-radius: 8px;
width: 90%;
max-width: 650px; /* Default max-width */
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
position: relative; /* For close button */
animation-name: animatetop;
animation-duration: 0.4s;
}
/* Larger modal variant */
.modal:not(.fullscreen) .modal-content.large {
max-width: 850px;
}
@keyframes animatetop {
from {top: -300px; opacity: 0}
to {top: 0; opacity: 1}
}
.close {
color: #aaa;
position: absolute; /* Position relative to modal-content */
top: 10px;
right: 15px;
font-size: 30px;
font-weight: bold;
line-height: 1;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
/* --- Fullscreen Modal Styles --- */
.modal.fullscreen {
display: none;
position: fixed;
z-index: 1001;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #f8f9fa; /* Light background */
}
.modal.fullscreen .modal-content {
background-color: transparent;
margin: 0;
padding: 40px; /* More padding */
border: none;
width: 100%;
height: 100%;
max-width: none;
border-radius: 0;
box-shadow: none;
position: relative;
overflow-y: auto;
box-sizing: border-box;
}
.modal.fullscreen h2 {
text-align: center;
color: #6f42c1; /* Keep purple for analytics title */
margin-top: 10px;
margin-bottom: 40px; /* More space */
font-size: 2em; /* Larger title */
border-bottom: none; /* Remove border */
}
.modal.fullscreen .analytics-section {
margin-bottom: 30px;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: #fff; /* White background for sections */
max-width: 900px;
margin-left: auto;
margin-right: auto;
}
.modal.fullscreen .analytics-section:last-of-type {
border-bottom: 1px solid #e0e0e0; /* Keep border */
}
.modal.fullscreen .analytics-section h3 {
color: #555;
margin-top: 0;
margin-bottom: 15px;
font-size: 1.4em;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.modal.fullscreen .analytics-section p {
font-size: 1.1em;
margin-bottom: 10px;
}
.modal.fullscreen .analytics-section span {
font-weight: normal;
color: #333;
}
/* Styles for Navigation Buttons inside Fullscreen Modal */
.modal.fullscreen .modal-navigation {
margin-top: 40px;
padding-top: 30px;
border-top: 1px solid #ccc;
text-align: center;
display: flex;
justify-content: center;
gap: 25px;
max-width: 900px;
margin-left: auto;
margin-right: auto;
}
.modal.fullscreen .modal-nav-btn {
padding: 12px 30px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 1.1em;
font-weight: bold;
transition: background-color 0.3s ease, transform 0.2s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.modal.fullscreen .modal-nav-btn.dashboard {
background-color: #ffc107;
color: #333;
}
.modal.fullscreen .modal-nav-btn.dashboard:hover {
background-color: #e0a800;
transform: translateY(-1px);
}
.modal.fullscreen .modal-nav-btn.home {
background-color: #17a2b8;
color: white;
}
.modal.fullscreen .modal-nav-btn.home:hover {
background-color: #138496;
transform: translateY(-1px);
}
.modal.fullscreen .modal-nav-btn.logout {
background-color: #dc3545;
color: white;
}
.modal.fullscreen .modal-nav-btn.logout:hover {
background-color: #c82333;
transform: translateY(-1px);
}
/* --- Other Styles --- */
.button-container {
margin-bottom: 30px;
text-align: center; /* Center buttons */
}
.btn {
padding: 12px 25px; /* Larger buttons */
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em; /* Adjust font size */
margin: 5px; /* Use margin for spacing */
transition: background-color 0.3s ease, box-shadow 0.2s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.btn:hover {
background-color: #45a049;
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.btn.delete { /* Consistent naming */
background-color: #f44336;
}
.btn.delete:hover {
background-color: #da190b;
}
.btn.edit { /* Style for edit buttons */
background-color: #ffc107;
color: #333;
padding: 5px 10px; /* Smaller edit buttons */
font-size: 0.8em;
margin-left: 10px;
vertical-align: middle;
}
.btn.edit:hover {
background-color: #e0a800;
}
label {
display: block;
margin-bottom: 8px; /* More space */
font-weight: bold;
color: #555;
}
input[type="text"],
textarea,
select {
width: 100%; /* Use 100% width */
padding: 12px; /* More padding */
margin-bottom: 18px; /* More space */
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
font-size: 1em;
}
textarea {
resize: vertical;
min-height: 100px; /* Minimum height */
}
select {
margin-bottom: 25px;
}
.question-container {
border: 1px solid #e0e0e0;
padding: 20px;
margin-bottom: 20px;
border-radius: 5px;
background-color: #f9f9f9;
}
.question-container h4 {
margin-top: 0;
margin-bottom: 15px;
color: #666;
font-size: 0.9em;
text-transform: uppercase;
}
.question-container input[type="text"] {
margin-bottom: 12px;
}
.multiple-choice-container {
margin-top: 15px;
padding-left: 15px;
border-left: 3px solid #4CAF50;
}
.multiple-choice-container label {
font-weight: normal;
margin-bottom: 8px;
}
.multiple-choice-container input[type="text"] {
width: calc(100% - 15px); /* Adjust width */
margin-left: 0; /* Remove indent */
}
/* Display Area Styling */
#createdLessons, #createdQuizzes {
margin-top: 15px;
}
#createdLessons div, #createdQuizzes div {
background-color: #fff; /* White background */
padding: 15px;
margin-bottom: 15px;
border-radius: 5px;
border: 1px solid #e0e0e0;
display: flex; /* Use flexbox for layout */
justify-content: space-between; /* Space out content and button */
align-items: center; /* Vertically align items */
}
#createdLessons div > div, #createdQuizzes div > div {
flex-grow: 1; /* Allow text content to take available space */
margin-right: 15px; /* Add space between text and button (if any) */
}
#createdLessons p, #createdQuizzes p {
margin: 0 0 5px 0; /* Adjust margins */
}
#createdLessons p strong, #createdQuizzes p strong {
font-size: 1.1em;
color: #333;
}
/* Manage Scores Modal Specific Styles */
.manage-scores-container {
padding: 10px;
}
.scores-display-area {
margin-top: 20px;
border: 1px solid #ccc;
padding: 15px;
min-height: 100px;
max-height: 400px; /* Limit height and allow scroll */
overflow-y: auto;
background-color: #f9f9f9;
}
.scores-display-area table {
width: 100%;
border-collapse: collapse;
margin-top: 10px; /* Add space above table */
}
.scores-display-area th, .scores-display-area td {
border: 1px solid #ddd;
padding: 10px 12px; /* Adjust padding */
text-align: left;
font-size: 0.95em; /* Slightly smaller font */
}
.scores-display-area th {
background-color: #e9e9e9;
font-weight: bold;
position: sticky; /* Make header sticky */
top: 0; /* Stick to the top of the scrollable area */
z-index: 1; /* Ensure header stays above table content */
}
.scores-display-area h4 { /* Style for the quiz title */
margin-bottom: 15px;
font-size: 1.2em;
color: #333;
}
.scores-display-area .no-scores-message {
font-style: italic;
color: #666;
text-align: center;
padding: 20px;
}
/* --- Analytics Button (Bottom Right, Red) --- */
.analytics-btn {
position: fixed; /* Fixed position */
bottom: 20px; /* Distance from bottom */
right: 20px; /* Distance from right */
padding: 12px 25px;
background-color: #dc3545; /* Red 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: 999; /* Ensure it's above most content but below modals */
transition: background-color 0.3s ease, transform 0.2s ease;
}
.analytics-btn:hover {
background-color: #c82333; /* Darker red on hover */
transform: translateY(-2px); /* Slight lift on hover */
}
teacher.js
document.addEventListener('DOMContentLoaded', () => {
// --- Login Check ---
const loggedInUsername = sessionStorage.getItem('loggedInUsername');
const loggedInRole = sessionStorage.getItem('loggedInRole');
if (!loggedInUsername || !loggedInRole || loggedInRole !== 'teacher') {
alert('Access denied. Please log in as a teacher.');
window.location.href = 'login.html'; // Redirect to login if not a teacher
return;
}
// --- End Login Check ---
// --- DOM Elements ---
const lessonModal = document.getElementById('lessonModal');
const quizModal = document.getElementById('quizModal');
const deleteModal = document.getElementById('deleteModal');
const manageScoresModal = document.getElementById('manageScoresModal');
const analyticsModal = document.getElementById('analyticsModal');
const createLessonBtn = document.getElementById('createLessonBtn');
const createQuizBtn = document.getElementById('createQuizBtn');
const deleteLessonBtn = document.getElementById('deleteLessonBtn');
const deleteQuizBtn = document.getElementById('deleteQuizBtn');
const manageScoresBtn = document.getElementById('manageScoresBtn');
const analyticsBtn = document.getElementById('analyticsBtn');
const closeButtons = document.querySelectorAll('.close');
// Lesson Form Elements
const lessonForm = document.getElementById('lessonForm');
const lessonModalTitle = document.getElementById('lessonModalTitle');
const editLessonIndexInput = document.getElementById('editLessonIndex');
const lessonTitleInput = document.getElementById('lessonTitle');
const lessonContentInput = document.getElementById('lessonContent');
const lessonSubmitBtn = document.getElementById('lessonSubmitBtn');
// Quiz Form Elements
const quizForm = document.getElementById('quizForm');
const quizModalTitle = document.getElementById('quizModalTitle');
const editQuizIndexInput = document.getElementById('editQuizIndex');
const quizTypeSelect = document.getElementById('quizType');
const quizTitleInput = document.getElementById('quizTitle');
const questionsContainer = document.getElementById('questionsContainer');
const addQuestionBtn = document.getElementById('addQuestionBtn');
const quizSubmitBtn = document.getElementById('quizSubmitBtn');
// Delete Modal Elements
const deleteTypeSelect = document.getElementById('deleteType');
const deleteChoiceSelect = document.getElementById('deleteChoice');
const deleteBtn = document.getElementById('deleteBtn');
// Manage Scores Modal Elements
const quizSelectForScores = document.getElementById('quizSelectForScores');
const studentScoresDisplay = document.getElementById('studentScoresDisplay');
// Analytics Modal Elements
const lessonCountSpan = document.getElementById('lessonCount');
const quizCountSpan = document.getElementById('quizCount');
const averageQuizScoreSpan = document.getElementById('averageQuizScore');
const modalDashboardBtn = document.getElementById('modalDashboardBtn');
const modalHomeBtn = document.getElementById('modalHomeBtn');
const modalLogoutBtn = document.getElementById('modalLogoutBtn');
// Display Areas
const createdLessonsDiv = document.getElementById('createdLessons');
const createdQuizzesDiv = document.getElementById('createdQuizzes');
let questionCount = 0;
const storageKeys = {
lessons: 'createdLessons',
quizzes: 'createdQuizzes',
studentResultPrefix: 'studentQuizResults_', // Prefix for student results keys
registeredUsers: 'registeredUsers' // Key for all registered users
};
// --- Utility Functions ---
const openModal = (modal) => {
modal.style.display = "block";
if (modal.classList.contains('fullscreen')) {
document.body.classList.add('modal-open');
}
};
const closeModal = (modal) => {
modal.style.display = "none";
if (modal.classList.contains('fullscreen')) {
document.body.classList.remove('modal-open');
}
// Reset forms/content on modal close
if (modal.id === 'lessonModal') resetLessonForm();
if (modal.id === 'quizModal') resetQuizForm();
if (modal.id === 'manageScoresModal') {
studentScoresDisplay.innerHTML = '<p class="no-scores-message">Select a quiz to view student scores.</p>';
quizSelectForScores.value = "";
}
if (modal.id === 'deleteModal') {
deleteTypeSelect.value = 'lesson';
deleteChoiceSelect.innerHTML = '<option value="">-- Select --</option>';
}
};
// Gets array data (lessons, quizzes)
const getData = (key) => {
const data = localStorage.getItem(key);
if (data && data.startsWith('[')) {
try {
return JSON.parse(data);
} catch (e) {
console.error("Error parsing JSON array from localStorage for key:", key, e);
return [];
}
}
return [];
};
// Gets object data (registered users, student results)
const getObjectData = (key) => {
const data = localStorage.getItem(key);
if (data && data.startsWith('{')) {
try {
return JSON.parse(data);
} catch (e) {
console.error("Error parsing JSON object from localStorage for key:", key, e);
return {};
}
}
return {};
};
const setData = (key, data) => {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (e) {
console.error("Error setting data to localStorage for key:", key, e);
}
};
// --- Lesson Functions ---
const resetLessonForm = () => {
lessonForm.reset();
editLessonIndexInput.value = "-1";
lessonModalTitle.textContent = "Create Lesson";
lessonSubmitBtn.textContent = "Create Lesson";
};
const updateCreatedLessons = () => {
const lessons = getData(storageKeys.lessons);
createdLessonsDiv.innerHTML = '';
lessons.forEach((lesson, index) => {
const lessonDiv = document.createElement('div');
const contentDiv = document.createElement('div');
contentDiv.innerHTML = `<p><strong>${lesson.title}</strong></p><p>${lesson.content.substring(0, 100)}${lesson.content.length > 100 ? '...' : ''}</p>`;
lessonDiv.appendChild(contentDiv);
const editBtn = document.createElement('button');
editBtn.textContent = 'Edit';
editBtn.classList.add('btn', 'edit');
editBtn.dataset.index = index;
editBtn.addEventListener('click', () => editLesson(index));
lessonDiv.appendChild(editBtn);
createdLessonsDiv.appendChild(lessonDiv);
});
updateDeleteChoices(); // Update delete dropdown after lessons update
};
const editLesson = (index) => {
const lessons = getData(storageKeys.lessons);
if (index >= 0 && index < lessons.length) {
const lesson = lessons[index];
resetLessonForm();
editLessonIndexInput.value = index;
lessonTitleInput.value = lesson.title;
lessonContentInput.value = lesson.content;
lessonModalTitle.textContent = "Edit Lesson";
lessonSubmitBtn.textContent = "Save Changes";
openModal(lessonModal);
}
};
const handleLessonFormSubmit = (event) => {
event.preventDefault();
const lessonData = {
title: lessonTitleInput.value,
content: lessonContentInput.value,
};
const editIndex = parseInt(editLessonIndexInput.value, 10);
let lessons = getData(storageKeys.lessons);
if (editIndex > -1 && editIndex < lessons.length) {
lessons[editIndex] = lessonData;
alert('Lesson Updated!');
} else {
lessons.push(lessonData);
alert('Lesson Created!');
}
setData(storageKeys.lessons, lessons);
closeModal(lessonModal);
updateCreatedLessons();
};
// --- Quiz Functions ---
const resetQuizForm = () => {
quizForm.reset();
editQuizIndexInput.value = "-1";
questionsContainer.innerHTML = '';
questionCount = 0;
quizModalTitle.textContent = "Create Quiz";
quizSubmitBtn.textContent = "Create Quiz";
quizTypeSelect.disabled = false; // Re-enable type select
toggleMultipleChoiceFields(); // Ensure fields match default type
};
const toggleMultipleChoiceFields = () => {
const selectedType = quizTypeSelect.value;
const allQuestionContainers = questionsContainer.querySelectorAll('.question-container');
allQuestionContainers.forEach(qContainer => {
const mcContainer = qContainer.querySelector('.multiple-choice-container');
const choiceInputs = mcContainer ? mcContainer.querySelectorAll('input[type="text"]') : [];
const answerInput = qContainer.querySelector('input[name$="-correct-answer"]');
const answerLabel = answerInput ? answerInput.previousElementSibling : null;
if (mcContainer) {
mcContainer.style.display = selectedType === 'multiple-choice' ? 'block' : 'none';
// Set required only if multiple choice is selected
choiceInputs.forEach(input => input.required = (selectedType === 'multiple-choice'));
}
if (answerLabel && answerLabel.tagName === 'LABEL') {
answerLabel.textContent = selectedType === 'identification' ? 'Correct Answer:' : 'Correct Answer (must match one choice):';
}
});
};
const addQuestion = (questionData = null) => {
const currentQIndex = questionData ? questionData.qIndex : ++questionCount;
const questionDiv = document.createElement('div');
questionDiv.classList.add('question-container');
questionDiv.dataset.qIndex = currentQIndex;
const qText = questionData ? questionData.questionText : '';
const qAnswer = questionData ? questionData.correctAnswer : '';
const qChoices = questionData ? questionData.choices : ['', '', '', '']; // Default empty choices
questionDiv.innerHTML = `
<h4>Question ${currentQIndex}</h4>
<label for="question-${currentQIndex}">Question Text:</label>
<input type="text" id="question-${currentQIndex}" name="question-${currentQIndex}" value="${qText}" required><br>
<div class="multiple-choice-container" style="display: ${quizTypeSelect.value === 'multiple-choice' ? 'block' : 'none'};">
<label>Choices:</label><br>
<input type="text" name="question-${currentQIndex}-choice-1" placeholder="Choice 1" value="${qChoices[0] || ''}" ${quizTypeSelect.value === 'multiple-choice' ? 'required' : ''}><br>
<input type="text" name="question-${currentQIndex}-choice-2" placeholder="Choice 2" value="${qChoices[1] || ''}" ${quizTypeSelect.value === 'multiple-choice' ? 'required' : ''}><br>
<input type="text" name="question-${currentQIndex}-choice-3" placeholder="Choice 3" value="${qChoices[2] || ''}" ${quizTypeSelect.value === 'multiple-choice' ? 'required' : ''}><br>
<input type="text" name="question-${currentQIndex}-choice-4" placeholder="Choice 4" value="${qChoices[3] || ''}" ${quizTypeSelect.value === 'multiple-choice' ? 'required' : ''}><br>
</div>
<label for="question-${currentQIndex}-correct-answer">${quizTypeSelect.value === 'identification' ? 'Correct Answer:' : 'Correct Answer (must match one choice):'}</label>
<input type="text" name="question-${currentQIndex}-correct-answer" value="${qAnswer}" required><br>
`;
questionsContainer.appendChild(questionDiv);
if (!questionData) questionCount = currentQIndex; // Update global count only for new questions
// No need to call toggleMultipleChoiceFields here as it's called on type change and form reset
};
const updateCreatedQuizzes = () => {
const quizzes = getData(storageKeys.quizzes);
createdQuizzesDiv.innerHTML = '';
quizzes.forEach((quiz, index) => {
const quizDiv = document.createElement('div');
const contentDiv = document.createElement('div');
// Basic validation before displaying
const title = quiz.quizTitle || 'Untitled Quiz';
const type = quiz.quizType || 'N/A';
const numQuestions = Array.isArray(quiz.questions) ? quiz.questions.length : 0;
contentDiv.innerHTML = `<p><strong>${title}</strong></p><p>(Type: ${type}, Questions: ${numQuestions})</p>`;
quizDiv.appendChild(contentDiv);
// No Edit button for quizzes currently
createdQuizzesDiv.appendChild(quizDiv);
});
updateDeleteChoices(); // Update delete dropdown after quizzes update
populateQuizSelectForScores(); // Update scores dropdown after quizzes update
};
// Edit Quiz function remains but is not called from UI
const editQuiz = (index) => {
const quizzes = getData(storageKeys.quizzes);
if (index >= 0 && index < quizzes.length) {
const quiz = quizzes[index];
resetQuizForm();
editQuizIndexInput.value = index;
quizTitleInput.value = quiz.quizTitle;
quizTypeSelect.value = quiz.quizType;
quizTypeSelect.disabled = true; // Disable type change when editing
questionCount = 0; // Reset count for adding existing questions
quiz.questions.forEach((q, qIndex) => {
addQuestion({ ...q, qIndex: qIndex + 1 }); // Pass existing data
});
quizModalTitle.textContent = "Edit Quiz";
quizSubmitBtn.textContent = "Save Changes";
openModal(quizModal);
toggleMultipleChoiceFields(); // Ensure fields match loaded quiz type
}
};
const handleQuizFormSubmit = (event) => {
event.preventDefault();
const editIndex = parseInt(editQuizIndexInput.value, 10);
const quizType = quizTypeSelect.value;
const quizTitle = quizTitleInput.value;
const questions = [];
const questionElements = questionsContainer.querySelectorAll('.question-container');
const currentQuestionCount = questionElements.length; // Use actual number of question divs
for (let i = 0; i < currentQuestionCount; i++) {
const qContainer = questionElements[i];
const qIndex = qContainer.dataset.qIndex; // Get index from data attribute
const questionText = qContainer.querySelector(`[name='question-${qIndex}']`)?.value.trim();
const correctAnswer = qContainer.querySelector(`[name='question-${qIndex}-correct-answer']`)?.value.trim();
const choices = [];
if (!questionText || correctAnswer === undefined || correctAnswer === null || correctAnswer === "") {
alert(`Please fill out question text and correct answer for Question ${qIndex}.`);
return;
}
if (quizType === 'multiple-choice') {
const choiceInputs = qContainer.querySelectorAll(`.multiple-choice-container input[type="text"]`);
let choicesComplete = true;
choiceInputs.forEach(input => {
const choiceValue = input.value.trim();
if (!choiceValue) choicesComplete = false;
choices.push(choiceValue);
});
if (!choicesComplete) {
alert(`Please fill out all choices for Question ${qIndex}.`);
return;
}
// Ensure the correct answer is one of the choices provided
if (!choices.includes(correctAnswer)) {
alert(`The correct answer for Question ${qIndex} ("${correctAnswer}") must exactly match one of the provided choices.`);
return;
}
}
questions.push({ questionText, choices: quizType === 'multiple-choice' ? choices : [], correctAnswer });
}
if (questions.length === 0) {
alert('Please add at least one question to the quiz.');
return;
}
const quizData = { quizType, quizTitle, questions };
let quizzes = getData(storageKeys.quizzes);
if (editIndex > -1 && editIndex < quizzes.length) {
quizzes[editIndex] = quizData;
alert('Quiz Updated!');
} else {
quizzes.push(quizData);
alert('Quiz Created!');
}
setData(storageKeys.quizzes, quizzes);
closeModal(quizModal);
updateCreatedQuizzes();
};
// --- Delete Functions (Handles both Lessons and Quizzes) ---
const updateDeleteChoices = () => {
const deleteType = deleteTypeSelect.value; // Read the selected type
deleteChoiceSelect.innerHTML = '<option value="">-- Select --</option>';
const items = getData(deleteType === "lesson" ? storageKeys.lessons : storageKeys.quizzes);
items.forEach((item, index) => {
const option = document.createElement('option');
option.value = index;
// Use 'title' for lessons and 'quizTitle' for quizzes, provide fallback
option.textContent = item.title || item.quizTitle || `${deleteType.charAt(0).toUpperCase() + deleteType.slice(1)} ${index + 1}`;
deleteChoiceSelect.appendChild(option);
});
deleteBtn.disabled = items.length === 0; // Disable button if no items
};
const handleDelete = () => {
const selectedIndex = deleteChoiceSelect.value;
const deleteType = deleteTypeSelect.value; // Get the type from the dropdown
if (selectedIndex === "") {
alert('Please select an item to delete.');
return;
}
const storageKey = deleteType === 'lesson' ? storageKeys.lessons : storageKeys.quizzes;
let items = getData(storageKey);
const itemToDelete = items[selectedIndex];
const itemName = itemToDelete?.title || itemToDelete?.quizTitle || `${deleteType} ${parseInt(selectedIndex)+1}`;
if (confirm(`Are you sure you want to delete ${deleteType} "${itemName}"? This cannot be undone.`)) {
items.splice(selectedIndex, 1);
setData(storageKey, items);
alert(`${deleteType.charAt(0).toUpperCase() + deleteType.slice(1)} Deleted!`);
// Update the correct display area
if (deleteType === 'lesson') {
updateCreatedLessons();
} else {
updateCreatedQuizzes(); // This also updates score/delete dropdowns
}
closeModal(deleteModal); // Close and reset the modal
}
};
// --- Manage Scores Functions ---
const populateQuizSelectForScores = () => {
const quizzes = getData(storageKeys.quizzes);
quizSelectForScores.innerHTML = '<option value="">-- Select a Quiz --</option>';
quizzes.forEach((quiz, index) => {
const option = document.createElement('option');
option.value = index; // Use the index as the value
option.textContent = quiz.quizTitle || `Quiz ${index + 1}`; // Fallback title
quizSelectForScores.appendChild(option);
});
};
// ===============================================================
// START: Enhanced displayStudentScores with Debugging
// ===============================================================
const displayStudentScores = () => {
const selectedQuizIndex = quizSelectForScores.value;
studentScoresDisplay.innerHTML = ''; // Clear previous content
console.log(`[DEBUG] displayStudentScores called. Selected Quiz Index: '${selectedQuizIndex}'`);
if (selectedQuizIndex === "") {
studentScoresDisplay.innerHTML = '<p class="no-scores-message">Select a quiz to view student scores.</p>';
console.log("[DEBUG] No quiz selected. Aborting.");
return;
}
const quizzes = getData(storageKeys.quizzes);
const selectedQuiz = quizzes[selectedQuizIndex];
if (!selectedQuiz) {
console.error(`[DEBUG] Error: Could not find quiz data for index ${selectedQuizIndex}`);
studentScoresDisplay.innerHTML = '<p class="no-scores-message">Error: Could not find selected quiz data.</p>';
return;
}
console.log("[DEBUG] Selected Quiz Data:", selectedQuiz);
// --- Get ALL registered users ---
const registeredUsers = getObjectData(storageKeys.registeredUsers);
console.log("[DEBUG] Raw Registered Users Data from localStorage:", registeredUsers);
if (Object.keys(registeredUsers).length === 0) {
console.warn("[DEBUG] No registered users found in localStorage key:", storageKeys.registeredUsers);
studentScoresDisplay.innerHTML = '<p class="no-scores-message">No registered users found.</p>';
return;
}
// --- Filter for users with role 'student' ---
const studentUsernames = Object.keys(registeredUsers).filter(username => {
const user = registeredUsers[username];
// Check if user exists and has a role property equal to 'student'
const isStudent = user && user.role === 'student';
if (!isStudent) {
console.log(`[DEBUG] Filtering out user '${username}'. Role is not 'student' or user data is malformed. User data:`, user);
}
return isStudent;
});
console.log("[DEBUG] Filtered Student Usernames:", studentUsernames);
if (studentUsernames.length === 0) {
console.warn("[DEBUG] No users with role 'student' found after filtering.");
studentScoresDisplay.innerHTML = '<p class="no-scores-message">No registered students found.</p>';
return;
}
let tableHTML = `
<h4>Scores for: ${selectedQuiz.quizTitle || 'Untitled Quiz'}</h4>
<table>
<thead>
<tr>
<th>Student</th>
<th>Score</th>
<th>Percentage</th>
<th>Date Taken</th>
</tr>
</thead>
<tbody>
`;
let scoresFound = false;
// --- Iterate through each student username ---
studentUsernames.forEach(username => {
const studentResultKey = `${storageKeys.studentResultPrefix}${username}`;
console.log(`[DEBUG] Checking results for student: '${username}' using key: '${studentResultKey}'`);
// Get the results object for THIS specific student
const studentResults = getObjectData(studentResultKey);
console.log(`[DEBUG] Results object fetched for '${username}':`, studentResults);
if (Object.keys(studentResults).length === 0) {
console.log(`[DEBUG] No results object found for student '${username}' at key '${studentResultKey}'. Skipping.`);
return; // Skip to the next student if no results object exists
}
// Check if THIS student has a result for the SPECIFICALLY selected quiz index
const result = studentResults[selectedQuizIndex]; // Use bracket notation
console.log(`[DEBUG] Result for quiz index '${selectedQuizIndex}' for student '${username}':`, result);
// Validate the result structure before using it
if (result && typeof result.score === 'number' && typeof result.total === 'number') {
scoresFound = true;
const score = result.score;
const total = result.total;
let percentage = 'N/A';
if (total > 0) {
percentage = ((score / total) * 100).toFixed(1) + '%';
} else if (total === 0) {
percentage = 'N/A (0 questions)';
}
const dateTaken = result.timestamp ? new Date(result.timestamp).toLocaleString() : 'N/A';
console.log(`[DEBUG] Adding row for '${username}': Score=${score}, Total=${total}, Percentage=${percentage}, Date=${dateTaken}`);
tableHTML += `
<tr>
<td>${username}</td>
<td>${score} / ${total}</td>
<td>${percentage}</td>
<td>${dateTaken}</td>
</tr>
`;
} else {
console.log(`[DEBUG] No valid result found for quiz index '${selectedQuizIndex}' for student '${username}' or result format is incorrect.`);
}
}); // End of studentUsernames.forEach
if (!scoresFound) {
console.warn("[DEBUG] No scores found for any student for this specific quiz index:", selectedQuizIndex);
tableHTML += '<tr><td colspan="4" class="no-scores-message">No students have taken this quiz yet or results are missing.</td></tr>';
}
tableHTML += '</tbody></table>';
studentScoresDisplay.innerHTML = tableHTML;
console.log("[DEBUG] displayStudentScores finished.");
};
// --- Analytics Modal Functions ---
const openAnalyticsModal = () => {
populateTeacherAnalytics();
openModal(analyticsModal);
};
const closeAnalyticsModal = () => {
closeModal(analyticsModal);
};
const populateTeacherAnalytics = () => {
const lessons = getData(storageKeys.lessons);
const quizzes = getData(storageKeys.quizzes);
lessonCountSpan.textContent = lessons.length;
quizCountSpan.textContent = quizzes.length;
let totalPercentageSum = 0;
let totalQuizzesTakenCount = 0;
const registeredUsers = getObjectData(storageKeys.registeredUsers);
const studentUsernames = Object.keys(registeredUsers).filter(username => registeredUsers[username]?.role === 'student');
studentUsernames.forEach(username => {
const studentResultKey = `${storageKeys.studentResultPrefix}${username}`;
const studentResults = getObjectData(studentResultKey);
// Iterate through results for this student
Object.values(studentResults).forEach(result => {
if (result && typeof result.score === 'number' && typeof result.total === 'number' && result.total > 0) {
totalPercentageSum += (result.score / result.total) * 100;
totalQuizzesTakenCount++;
}
});
});
if (totalQuizzesTakenCount > 0) {
averageQuizScoreSpan.textContent = (totalPercentageSum / totalQuizzesTakenCount).toFixed(1) + '%';
} else {
averageQuizScoreSpan.textContent = 'N/A';
}
};
// --- Navigation Logic ---
function goToHomepage() {
window.location.href = 'homepage.html'; // Assuming homepage.html exists
}
function logout() {
sessionStorage.removeItem('loggedInUsername');
sessionStorage.removeItem('loggedInRole');
alert('You have been logged out.');
window.location.href = 'login.html'; // Redirect to login page
}
// --- Event Listeners ---
// Standard Modals Open/Close
createLessonBtn.addEventListener('click', () => { resetLessonForm(); openModal(lessonModal); });
createQuizBtn.addEventListener('click', () => { resetQuizForm(); addQuestion(); openModal(quizModal); }); // Add one question by default
deleteLessonBtn.addEventListener('click', () => {
deleteTypeSelect.value = "lesson"; // Set type
updateDeleteChoices(); // Populate choices for lessons
openModal(deleteModal);
});
deleteQuizBtn.addEventListener('click', () => {
deleteTypeSelect.value = "quiz"; // Set type
updateDeleteChoices(); // Populate choices for quizzes
openModal(deleteModal);
});
manageScoresBtn.addEventListener('click', () => {
populateQuizSelectForScores(); // Ensure dropdown is up-to-date
studentScoresDisplay.innerHTML='<p class="no-scores-message">Select a quiz to view student scores.</p>'; // Reset display
openModal(manageScoresModal);
});
closeButtons.forEach(button => {
button.addEventListener('click', () => {
const modalId = button.getAttribute('data-modal-id');
if (modalId) {
const modalToClose = document.getElementById(modalId);
if (modalToClose) closeModal(modalToClose);
}
});
});
// Close modal if clicking outside the content (but not on fullscreen modals)
window.addEventListener('click', (event) => {
if (event.target.classList.contains('modal') && !event.target.classList.contains('fullscreen')) {
closeModal(event.target);
}
});
// Form Submissions
lessonForm.addEventListener('submit', handleLessonFormSubmit);
quizForm.addEventListener('submit', handleQuizFormSubmit);
// Quiz Form - Add Question & Type Change
addQuestionBtn.addEventListener('click', () => addQuestion());
quizTypeSelect.addEventListener('change', toggleMultipleChoiceFields);
// Delete Modal - Type Change & Delete Button
deleteTypeSelect.addEventListener('change', updateDeleteChoices); // Update choices when type changes
deleteBtn.addEventListener('click', handleDelete);
// Manage Scores Modal - Quiz Selection Change
quizSelectForScores.addEventListener('change', displayStudentScores);
// Analytics Modal Open/Close/Navigation
analyticsBtn.addEventListener('click', openAnalyticsModal);
modalDashboardBtn.addEventListener('click', closeAnalyticsModal);
modalHomeBtn.addEventListener('click', goToHomepage);
modalLogoutBtn.addEventListener('click', logout);
// --- Initial Load ---
updateCreatedLessons();
updateCreatedQuizzes(); // Load existing quizzes on page load
});
student.html
<!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>
student.css
/* 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);
}
student.js
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.html
<!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>
parent.css
/* 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;
}
parentdash.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Dashboard</title>
<link rel="stylesheet" href="parent.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"></span></p>
<p><strong>ID:</strong> <span id="studentId"></span></p>
</div>
<div class="quiz-scores">
<h3>Quiz Scores</h3>
<ul id="quizList">
<!-- Scores will be loaded here by JS -->
<li>Loading scores...</li>
</ul>
</div>
<div class="lesson-progress">
<h3>Lesson Progress</h3>
<ul id="lessonList">
<!-- Progress will be loaded here by JS -->
<li>Loading progress...</li>
</ul>
</div>
<p><a href="parent.html">Look up another student</a></p>
<p><a href="login.html" onclick="logoutParent()">Logout</a></p>
</div>
<script src="parent.js"></script> <!-- Use new JS -->
<script src="login.js"></script> <!-- Include login.js for getUsers function -->
</body>
</html>
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;
}
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);
}