Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>UC Math Evaluation System</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
.gradient-bg { | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
} | |
.evaluation-card { | |
transition: all 0.3s ease; | |
} | |
.evaluation-card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 10px 20px rgba(0,0,0,0.1); | |
} | |
.shake { | |
animation: shake 0.5s; | |
} | |
@keyframes shake { | |
0%, 100% { transform: trans | |
lateX(0); } | |
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } | |
20%, 40%, 60%, 80% { transform: translateX(5px); } | |
} | |
</style> | |
</head> | |
<body class="min-h-screen bg-gray-100"> | |
<!-- Main Container --> | |
<div id="app" class="container mx-auto px-4 py-8"> | |
<!-- Login Screen --> | |
<div id="login-screen" class="max-w-md mx-auto"> | |
<div class="gradient-bg rounded-xl shadow-xl overflow-hidden"> | |
<div class="px-10 py-12"> | |
<div class="text-center mb-8"> | |
<h1 class="text-3xl font-bold text-white">UC Math Evaluation</h1> | |
<p class="text-white opacity-80 mt-2">Student & Teacher Portal</p> | |
</div> | |
<form id="login-form" class="space-y-6"> | |
<div> | |
<label for="username" class="block text-sm font-medium text-white">Username</label> | |
<div class="mt-1 relative rounded-md shadow-sm"> | |
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> | |
<i class="fas fa-user text-gray-300"></i> | |
</div> | |
<input type="text" id="username" name="username" required | |
class="py-3 pl-10 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500"> | |
</div> | |
</div> | |
<div> | |
<label for="password" class="block text-sm font-medium text-white">Password</label> | |
<div class="mt-1 relative rounded-md shadow-sm"> | |
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> | |
<i class="fas fa-lock text-gray-300"></i> | |
</div> | |
<input type="password" id="password" name="password" required | |
class="py-3 pl-10 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500"> | |
</div> | |
</div> | |
<div> | |
<button type="submit" | |
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-indigo-700 bg-white hover:bg-indigo-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-all"> | |
Sign in | |
</button> | |
</div> | |
</form> | |
<div id="login-error" class="mt-4 hidden"> | |
<div class="bg-red-50 border-l-4 border-red-400 p-4"> | |
<div class="flex"> | |
<div class="flex-shrink-0"> | |
<i class="fas fa-exclamation-circle text-red-400"></i> | |
</div> | |
<div class="ml-3"> | |
<p class="text-sm text-red-700" id="error-message">Invalid username or password</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Student Dashboard (Hidden by default) --> | |
<div id="student-dashboard" class="hidden"> | |
<div class="flex justify-between items-center mb-8"> | |
<h1 class="text-3xl font-bold text-gray-800">Your Math Evaluations</h1> | |
<button id="logout-btn" class="flex items-center text-gray-600 hover:text-gray-900"> | |
<i class="fas fa-sign-out-alt mr-2"></i> Logout | |
</button> | |
</div> | |
<div class="bg-white rounded-xl shadow-md overflow-hidden"> | |
<div class="p-6"> | |
<div class="flex items-center mb-6"> | |
<div class="h-12 w-12 rounded-full bg-indigo-100 flex items-center justify-center"> | |
<i class="fas fa-user-graduate text-indigo-600 text-xl"></i> | |
</div> | |
<div class="ml-4"> | |
<h2 class="text-xl font-semibold text-gray-800" id="student-name">John Doe</h2> | |
<p class="text-gray-600">Student ID: <span id="student-id">S12345</span></p> | |
</div> | |
</div> | |
<div id="evaluations-container" class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
<!-- Evaluations will be loaded here --> | |
</div> | |
<div id="no-evaluations" class="text-center py-12 hidden"> | |
<i class="fas fa-book-open text-gray-300 text-5xl mb-4"></i> | |
<h3 class="text-lg font-medium text-gray-500">No evaluations available</h3> | |
<p class="text-gray-400 mt-1">Your teacher hasn't uploaded any evaluations yet.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Teacher Dashboard (Hidden by default) --> | |
<div id="teacher-dashboard" class="hidden"> | |
<div class="flex justify-between items-center mb-8"> | |
<h1 class="text-3xl font-bold text-gray-800">Teacher Control Panel</h1> | |
<button id="teacher-logout-btn" class="flex items-center text-gray-600 hover:text-gray-900"> | |
<i class="fas fa-sign-out-alt mr-2"></i> Logout | |
</button> | |
</div> | |
<div class="bg-white rounded-xl shadow-md overflow-hidden mb-8"> | |
<div class="p-6"> | |
<div class="flex items-center mb-6"> | |
<div class="h-12 w-12 rounded-full bg-purple-100 flex items-center justify-center"> | |
<i class="fas fa-chalkboard-teacher text-purple-600 text-xl"></i> | |
</div> | |
<div class="ml-4"> | |
<h2 class="text-xl font-semibold text-gray-800">Prof. Shaimaa Robaa</h2> | |
<p class="text-gray-600">Math Department</p> | |
</div> | |
</div> | |
<div class="border-b border-gray-200"> | |
<nav class="-mb-px flex space-x-8"> | |
<button id="manage-students-tab" class="border-indigo-500 text-indigo-600 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"> | |
Manage Students | |
</button> | |
<button id="upload-evaluations-tab" class="border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"> | |
Upload Evaluations | |
</button> | |
</nav> | |
</div> | |
</div> | |
</div> | |
<!-- Manage Students Panel --> | |
<div id="manage-students-panel"> | |
<div class="bg-white rounded-xl shadow-md overflow-hidden mb-6"> | |
<div class="p-6"> | |
<h2 class="text-xl font-semibold text-gray-800 mb-4">Add New Student</h2> | |
<form id="add-student-form" class="space-y-4"> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
<div> | |
<label for="student-firstname" class="block text-sm font-medium text-gray-700">First Name</label> | |
<input type="text" id="student-firstname" name="firstname" required | |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 py-2 px-3 border"> | |
</div> | |
<div> | |
<label for="student-lastname" class="block text-sm font-medium text-gray-700">Last Name</label> | |
<input type="text" id="student-lastname" name="lastname" required | |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 py-2 px-3 border"> | |
</div> | |
</div> | |
<div> | |
<label for="student-email" class="block text-sm font-medium text-gray-700">Email</label> | |
<input type="email" id="student-email" name="email" required | |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 py-2 px-3 border"> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
<div> | |
<label for="student-username" class="block text-sm font-medium text-gray-700">Username</label> | |
<input type="text" id="student-username" name="username" required | |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 py-2 px-3 border"> | |
</div> | |
<div> | |
<label for="student-password" class="block text-sm font-medium text-gray-700">Password</label> | |
<input type="password" id="student-password" name="password" required | |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 py-2 px-3 border"> | |
</div> | |
</div> | |
<div class="pt-2"> | |
<button type="submit" | |
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | |
Add Student | |
</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<div class="bg-white rounded-xl shadow-md overflow-hidden"> | |
<div class="p-6"> | |
<h2 class="text-xl font-semibold text-gray-800 mb-4">Student List</h2> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<thead class="bg-gray-50"> | |
<tr> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Username</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> | |
</tr> | |
</thead> | |
<tbody id="student-list" class="bg-white divide-y divide-gray-200"> | |
<!-- Student list will be loaded here --> | |
</tbody> | |
</table> | |
</div> | |
<div id="no-students" class="text-center py-12 hidden"> | |
<i class="fas fa-user-graduate text-gray-300 text-5xl mb-4"></i> | |
<h3 class="text-lg font-medium text-gray-500">No students added</h3> | |
<p class="text-gray-400 mt-1">Add your first student using the form above.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Upload Evaluations Panel (Hidden by default) --> | |
<div id="upload-evaluations-panel" class="hidden"> | |
<div class="bg-white rounded-xl shadow-md overflow-hidden mb-6"> | |
<div class="p-6"> | |
<h2 class="text-xl font-semibold text-gray-800 mb-4">Upload New Evaluation</h2> | |
<form id="upload-evaluation-form" class="space-y-4"> | |
<div> | |
<label for="evaluation-title" class="block text-sm font-medium text-gray-700">Title</label> | |
<input type="text" id="evaluation-title" name="title" required | |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 py-2 px-3 border"> | |
</div> | |
<div> | |
<label for="evaluation-description" class="block text-sm font-medium text-gray-700">Description</label> | |
<textarea id="evaluation-description" name="description" rows="3" | |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 py-2 px-3 border"></textarea> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
<div> | |
<label for="evaluation-date" class="block text-sm font-medium text-gray-700">Due Date</label> | |
<input type="date" id="evaluation-date" name="date" required | |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 py-2 px-3 border"> | |
</div> | |
<div> | |
<label for="evaluation-file" class="block text-sm font-medium text-gray-700">Evaluation File</label> | |
<div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"> | |
<div class="space-y-1 text-center"> | |
<div class="flex text-sm text-gray-600"> | |
<label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"> | |
<span>Upload a file</span> | |
<input id="file-upload" name="file-upload" type="file" class="sr-only"> | |
</label> | |
<p class="pl-1">or drag and drop</p> | |
</div> | |
<p class="text-xs text-gray-500">PDF, DOCX up to 10MB</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="pt-2"> | |
<button type="submit" | |
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | |
Upload Evaluation | |
</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<div class="bg-white rounded-xl shadow-md overflow-hidden"> | |
<div class="p-6"> | |
<h2 class="text-xl font-semibold text-gray-800 mb-4">Recent Evaluations</h2> | |
<div id="teacher-evaluations-container" class="space-y-4"> | |
<!-- Teacher's evaluations will be loaded here --> | |
</div> | |
<div id="no-teacher-evaluations" class="text-center py-12 hidden"> | |
<i class="fas fa-book text-gray-300 text-5xl mb-4"></i> | |
<h3 class="text-lg font-medium text-gray-500">No evaluations uploaded</h3> | |
<p class="text-gray-400 mt-1">Upload your first evaluation using the form above.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Database keys for localStorage | |
const DB_KEYS = { | |
STUDENTS: 'uc_math_students', | |
EVALUATIONS: 'uc_math_evaluations' | |
}; | |
// Initialize database with default data if not exists | |
function initializeDatabase() { | |
if (!localStorage.getItem(DB_KEYS.STUDENTS)) { | |
const defaultStudents = [ | |
{ id: 'S1001', username: 'student1', password: 'password1', name: 'Alice Johnson', email: '[email protected]' }, | |
{ id: 'S1002', username: 'student2', password: 'password2', name: 'Bob Smith', email: '[email protected]' }, | |
{ id: 'S1003', username: 'student3', password: 'password3', name: 'Charlie Brown', email: '[email protected]' } | |
]; | |
localStorage.setItem(DB_KEYS.STUDENTS, JSON.stringify(defaultStudents)); | |
} | |
if (!localStorage.getItem(DB_KEYS.EVALUATIONS)) { | |
const defaultEvaluations = [ | |
{ | |
id: 'E101', | |
title: 'Linear Algebra Midterm', | |
description: 'Covers chapters 1-4 on vector spaces and linear transformations', | |
date: '2023-11-15', | |
file: 'linear-algebra-midterm.pdf', | |
assignedTo: ['S1001', 'S1002', 'S1003'], | |
status: { | |
'S1001': 'Submitted', | |
'S1002': 'Pending', | |
'S1003': 'Submitted' | |
} | |
}, | |
{ | |
id: 'E102', | |
title: 'Calculus Assignment 3', | |
description: 'Integration techniques and applications', | |
date: '2023-11-22', | |
file: 'calculus-assignment3.pdf', | |
assignedTo: ['S1001', 'S1002'], | |
status: { | |
'S1001': 'Pending', | |
'S1002': 'Submitted' | |
} | |
} | |
]; | |
localStorage.setItem(DB_KEYS.EVALUATIONS, JSON.stringify(defaultEvaluations)); | |
} | |
} | |
// Database functions | |
const database = { | |
getStudents() { | |
const students = localStorage.getItem(DB_KEYS.STUDENTS); | |
return students ? JSON.parse(students) : []; | |
}, | |
saveStudents(students) { | |
localStorage.setItem(DB_KEYS.STUDENTS, JSON.stringify(students)); | |
}, | |
getEvaluations() { | |
const evaluations = localStorage.getItem(DB_KEYS.EVALUATIONS); | |
return evaluations ? JSON.parse(evaluations) : []; | |
}, | |
saveEvaluations(evaluations) { | |
localStorage.setItem(DB_KEYS.EVALUATIONS, JSON.stringify(evaluations)); | |
}, | |
teacher: { | |
username: 'Shaimaa Robaa', | |
password: 'controlpanel' | |
} | |
}; | |
// Current user | |
let currentUser = null; | |
// DOM Elements | |
const loginScreen = document.getElementById('login-screen'); | |
const studentDashboard = document.getElementById('student-dashboard'); | |
const teacherDashboard = document.getElementById('teacher-dashboard'); | |
const loginForm = document.getElementById('login-form'); | |
const logoutBtn = document.getElementById('logout-btn'); | |
const teacherLogoutBtn = document.getElementById('teacher-logout-btn'); | |
const loginError = document.getElementById('login-error'); | |
const errorMessage = document.getElementById('error-message'); | |
const evaluationsContainer = document.getElementById('evaluations-container'); | |
const noEvaluations = document.getElementById('no-evaluations'); | |
const studentName = document.getElementById('student-name'); | |
const studentId = document.getElementById('student-id'); | |
// Teacher dashboard elements | |
const manageStudentsTab = document.getElementById('manage-students-tab'); | |
const uploadEvaluationsTab = document.getElementById('upload-evaluations-tab'); | |
const manageStudentsPanel = document.getElementById('manage-students-panel'); | |
const uploadEvaluationsPanel = document.getElementById('upload-evaluations-panel'); | |
const addStudentForm = document.getElementById('add-student-form'); | |
const studentList = document.getElementById('student-list'); | |
const noStudents = document.getElementById('no-students'); | |
const uploadEvaluationForm = document.getElementById('upload-evaluation-form'); | |
const teacherEvaluationsContainer = document.getElementById('teacher-evaluations-container'); | |
const noTeacherEvaluations = document.getElementById('no-teacher-evaluations'); | |
// Event Listeners | |
loginForm.addEventListener('submit', handleLogin); | |
logoutBtn.addEventListener('click', handleLogout); | |
teacherLogoutBtn.addEventListener('click', handleLogout); | |
manageStudentsTab.addEventListener('click', () => switchTeacherTab('manage')); | |
uploadEvaluationsTab.addEventListener('click', () => switchTeacherTab('upload')); | |
addStudentForm.addEventListener('submit', handleAddStudent); | |
uploadEvaluationForm.addEventListener('submit', handleUploadEvaluation); | |
// Initialize the app | |
function init() { | |
initializeDatabase(); | |
// Check if user is already logged in (from session storage) | |
const storedUser = sessionStorage.getItem('currentUser'); | |
if (storedUser) { | |
currentUser = JSON.parse(storedUser); | |
if (currentUser.username === database.teacher.username) { | |
showTeacherDashboard(); | |
loadStudents(); | |
loadTeacherEvaluations(); | |
} else { | |
showStudentDashboard(); | |
loadStudentEvaluations(); | |
} | |
} else { | |
showLoginScreen(); | |
} | |
} | |
// Handle login | |
function handleLogin(e) { | |
e.preventDefault(); | |
const username = document.getElementById('username').value; | |
const password = document.getElementById('password').value; | |
// Check if teacher is logging in | |
if (username === database.teacher.username && password === database.teacher.password) { | |
currentUser = { username: database.teacher.username, role: 'teacher' }; | |
sessionStorage.setItem('currentUser', JSON.stringify(currentUser)); | |
showTeacherDashboard(); | |
loadStudents(); | |
loadTeacherEvaluations(); | |
return; | |
} | |
// Check if student is logging in | |
const students = database.getStudents(); | |
const student = students.find(s => s.username === username && s.password === password); | |
if (student) { | |
currentUser = { | |
username: student.username, | |
id: student.id, | |
name: student.name, | |
email: student.email, | |
role: 'student' | |
}; | |
sessionStorage.setItem('currentUser', JSON.stringify(currentUser)); | |
showStudentDashboard(); | |
loadStudentEvaluations(); | |
return; | |
} | |
// Invalid credentials | |
showLoginError(); | |
} | |
// Show login error | |
function showLoginError() { | |
loginError.classList.remove('hidden'); | |
loginForm.classList.add('shake'); | |
setTimeout(() => { | |
loginForm.classList.remove('shake'); | |
}, 500); | |
} | |
// Handle logout | |
function handleLogout() { | |
currentUser = null; | |
sessionStorage.removeItem('currentUser'); | |
showLoginScreen(); | |
loginForm.reset(); | |
} | |
// Show login screen | |
function showLoginScreen() { | |
loginScreen.classList.remove('hidden'); | |
studentDashboard.classList.add('hidden'); | |
teacherDashboard.classList.add('hidden'); | |
loginError.classList.add('hidden'); | |
} | |
// Show student dashboard | |
function showStudentDashboard() { | |
loginScreen.classList.add('hidden'); | |
studentDashboard.classList.remove('hidden'); | |
teacherDashboard.classList.add('hidden'); | |
// Set student info | |
studentName.textContent = currentUser.name; | |
studentId.textContent = currentUser.id; | |
} | |
// Show teacher dashboard | |
function showTeacherDashboard() { | |
loginScreen.classList.add('hidden'); | |
studentDashboard.classList.add('hidden'); | |
teacherDashboard.classList.remove('hidden'); | |
} | |
// Load student evaluations | |
function loadStudentEvaluations() { | |
const studentEvaluations = database.getEvaluations().filter(eval => | |
eval.assignedTo.includes(currentUser.id) | |
); | |
evaluationsContainer.innerHTML = ''; | |
if (studentEvaluations.length === 0) { | |
noEvaluations.classList.remove('hidden'); | |
return; | |
} | |
noEvaluations.classList.add('hidden'); | |
studentEvaluations.forEach(eval => { | |
const status = eval.status[currentUser.id] || 'Pending'; | |
const statusColor = getStatusColor(status); | |
const evalCard = document.createElement('div'); | |
evalCard.className = 'evaluation-card bg-white rounded-lg shadow p-6'; | |
evalCard.innerHTML = ` | |
<div class="flex justify-between items-start"> | |
<div> | |
<h3 class="text-lg font-semibold text-gray-800">${eval.title}</h3> | |
<p class="text-gray-600 mt-1">${eval.description}</p> | |
</div> | |
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${statusColor}"> | |
${status} | |
</span> | |
</div> | |
<div class="mt-4 flex items-center text-sm text-gray-500"> | |
<i class="far fa-calendar-alt mr-2"></i> | |
Due: ${formatDate(eval.date)} | |
</div> | |
<div class="mt-4"> | |
<a href="#" class="inline-flex items-center text-indigo-600 hover:text-indigo-800"> | |
<i class="far fa-file-pdf mr-2"></i> Download Evaluation | |
</a> | |
</div> | |
${status === 'Pending' ? ` | |
<div class="mt-4"> | |
<button class="w-full inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | |
Submit Solution | |
</button> | |
</div> | |
` : ''} | |
`; | |
evaluationsContainer.appendChild(evalCard); | |
}); | |
} | |
// Load students for teacher dashboard | |
function loadStudents() { | |
studentList.innerHTML = ''; | |
const students = database.getStudents(); | |
if (students.length === 0) { | |
noStudents.classList.remove('hidden'); | |
return; | |
} | |
noStudents.classList.add('hidden'); | |
students.forEach(student => { | |
const studentRow = document.createElement('tr'); | |
studentRow.innerHTML = ` | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="flex items-center"> | |
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-indigo-100 flex items-center justify-center"> | |
<i class="fas fa-user text-indigo-600"></i> | |
</div> | |
<div class="ml-4"> | |
<div class="text-sm font-medium text-gray-900">${student.name}</div> | |
<div class="text-sm text-gray-500">${student.id}</div> | |
</div> | |
</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">${student.username}</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">${student.email}</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<button class="text-indigo-600 hover:text-indigo-900 mr-3 edit-student" data-id="${student.id}"> | |
<i class="fas fa-edit"></i> | |
</button> | |
<button class="text-red-600 hover:text-red-900 delete-student" data-id="${student.id}"> | |
<i class="fas fa-trash"></i> | |
</button> | |
</td> | |
`; | |
studentList.appendChild(studentRow); | |
}); | |
// Add event listeners to edit and delete buttons | |
document.querySelectorAll('.edit-student').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const studentId = e.currentTarget.getAttribute('data-id'); | |
editStudent(studentId); | |
}); | |
}); | |
document.querySelectorAll('.delete-student').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const studentId = e.currentTarget.getAttribute('data-id'); | |
deleteStudent(studentId); | |
}); | |
}); | |
} | |
// Load teacher evaluations | |
function loadTeacherEvaluations() { | |
teacherEvaluationsContainer.innerHTML = ''; | |
const evaluations = database.getEvaluations(); | |
if (evaluations.length === 0) { | |
noTeacherEvaluations.classList.remove('hidden'); | |
return; | |
} | |
noTeacherEvaluations.classList.add('hidden'); | |
evaluations.forEach(eval => { | |
const students = database.getStudents(); | |
const assignedStudents = eval.assignedTo.map(id => { | |
const student = students.find(s => s.id === id); | |
return student ? student.name : ''; | |
}).filter(name => name !== ''); | |
const submittedCount = Object.values(eval.status).filter(s => s === 'Submitted').length; | |
const totalCount = eval.assignedTo.length; | |
const evalCard = document.createElement('div'); | |
evalCard.className = 'evaluation-card bg-white rounded-lg shadow p-6'; | |
evalCard.innerHTML = ` | |
<div class="flex justify-between items-start"> | |
<div> | |
<h3 class="text-lg font-semibold text-gray-800">${eval.title}</h3> | |
<p class="text-gray-600 mt-1">${eval.description}</p> | |
</div> | |
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800"> | |
${submittedCount}/${totalCount} submitted | |
</span> | |
</div> | |
<div class="mt-4 flex items-center text-sm text-gray-500"> | |
<i class="far fa-calendar-alt mr-2"></i> | |
Due: ${formatDate(eval.date)} | |
</div> | |
<div class="mt-3"> | |
<span class="text-sm text-gray-600">Assigned to:</span> | |
<div class="mt-1 flex flex-wrap gap-2"> | |
${assignedStudents.map(name => ` | |
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800"> | |
${name} | |
</span> | |
`).join('')} | |
</div> | |
</div> | |
<div class="mt-4 flex space-x-3"> | |
<a href="#" class="inline-flex items-center text-indigo-600 hover:text-indigo-800"> | |
<i class="far fa-file-pdf mr-2"></i> Download | |
</a> | |
<a href="#" class="inline-flex items-center text-gray-600 hover:text-gray-800"> | |
<i class="fas fa-list-check mr-2"></i> View Submissions | |
</a> | |
<a href="#" class="inline-flex items-center text-gray-600 hover:text-gray-800"> | |
<i class="fas fa-edit mr-2"></i> Edit | |
</a> | |
</div> | |
`; | |
teacherEvaluationsContainer.appendChild(evalCard); | |
}); | |
} | |
// Switch between teacher tabs | |
function switchTeacherTab(tab) { | |
if (tab === 'manage') { | |
manageStudentsTab.classList.remove('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300'); | |
manageStudentsTab.classList.add('border-indigo-500', 'text-indigo-600'); | |
uploadEvaluationsTab.classList.remove('border-indigo-500', 'text-indigo-600'); | |
uploadEvaluationsTab.classList.add('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300'); | |
manageStudentsPanel.classList.remove('hidden'); | |
uploadEvaluationsPanel.classList.add('hidden'); | |
} else { | |
uploadEvaluationsTab.classList.remove('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300'); | |
uploadEvaluationsTab.classList.add('border-indigo-500', 'text-indigo-600'); | |
manageStudentsTab.classList.remove('border-indigo-500', 'text-indigo-600'); | |
manageStudentsTab.classList.add('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300'); | |
uploadEvaluationsPanel.classList.remove('hidden'); | |
manageStudentsPanel.classList.add('hidden'); | |
} | |
} | |
// Handle add student | |
function handleAddStudent(e) { | |
e.preventDefault(); | |
const firstname = document.getElementById('student-firstname').value; | |
const lastname = document.getElementById('student-lastname').value; | |
const email = document.getElementById('student-email').value; | |
const username = document.getElementById('student-username').value; | |
const password = document.getElementById('student-password').value; | |
// Generate student ID | |
const students = database.getStudents(); | |
const id = 'S' + (1000 + students.length + 1); | |
// Create new student | |
const newStudent = { | |
id, | |
username, | |
password, | |
name: `${firstname} ${lastname}`, | |
}; | |
// Add to database | |
students.push(newStudent); | |
database.saveStudents(students); | |
// Reload students | |
loadStudents(); | |
// Reset form | |
addStudentForm.reset(); | |
// Show success message | |
showAlert('Student added successfully!', 'success'); | |
} | |
// Edit student | |
function editStudent(studentId) { | |
const students = database.getStudents(); | |
const student = students.find(s => s.id === studentId); | |
if (student) { | |
// In a real app, you would show a modal or form to edit the student | |
// For this demo, we'll just show a prompt for each field | |
const newName = prompt('Edit student name:', student.name); | |
if (newName !== null) { | |
student.name = newName; | |
const newUsername = prompt('Edit username:', student.username); | |
if (newUsername !== null) student.username = newUsername; | |
const newEmail = prompt('Edit email:', student.email); | |
if (newEmail !== null) student.email = newEmail; | |
// Save changes | |
database.saveStudents(students); | |
loadStudents(); | |
showAlert('Student updated successfully!', 'success'); | |
} | |
} | |
} | |
// Delete student | |
function deleteStudent(studentId) { | |
if (confirm('Are you sure you want to delete this student?')) { | |
const students = database.getStudents(); | |
const index = students.findIndex(s => s.id === studentId); | |
if (index !== -1) { | |
students.splice(index, 1); | |
database.saveStudents(students); | |
loadStudents(); | |
showAlert('Student deleted successfully!', 'success'); | |
} | |
} | |
} | |
// Handle upload evaluation | |
function handleUploadEvaluation(e) { | |
e.preventDefault(); | |
const title = document.getElementById('evaluation-title').value; | |
const description = document.getElementById('evaluation-description').value; | |
const date = document.getElementById('evaluation-date').value; | |
// In a real app, you would handle file upload here | |
const fileInput = document.getElementById('file-upload'); | |
const fileName = fileInput.files.length > 0 ? fileInput.files[0].name : 'evaluation.pdf'; | |
// Generate evaluation ID | |
const evaluations = database.getEvaluations(); | |
const id = 'E' + (100 + evaluations.length + 1); | |
// Get all student IDs | |
const students = database.getStudents(); | |
const assignedTo = students.map(s => s.id); | |
// Create status object with all students as pending | |
const status = {}; | |
assignedTo.forEach(id => { | |
status[id] = 'Pending'; | |
}); | |
// Create new evaluation | |
const newEvaluation = { | |
id, | |
title, | |
description, | |
date, | |
file: fileName, | |
assignedTo, | |
status | |
}; | |
// Add to database | |
evaluations.push(newEvaluation); | |
database.saveEvaluations(evaluations); | |
// Reload evaluations | |
loadTeacherEvaluations(); | |
// Reset form | |
uploadEvaluationForm.reset(); | |
// Show success message | |
showAlert('Evaluation uploaded successfully!', 'success'); | |
} | |
// Show alert message | |
function showAlert(message, type) { | |
const alertDiv = document.createElement('div'); | |
alertDiv.className = `fixed top-4 right-4 px-4 py-3 rounded shadow-lg ${type === 'success' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`; | |
alertDiv.innerHTML = ` | |
<div class="flex items-center"> | |
<i class="fas ${type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle'} mr-2"></i> | |
<span>${message}</span> | |
</div> | |
`; | |
document.body.appendChild(alertDiv); | |
setTimeout(() => { | |
alertDiv.classList.add('opacity-0', 'transition-opacity', 'duration-500'); | |
setTimeout(() => { | |
alertDiv.remove(); | |
}, 500); | |
}, 3000); | |
} | |
// Helper functions | |
function formatDate(dateString) { | |
const options = { year: 'numeric', month: 'long', day: 'numeric' }; | |
return new Date(dateString).toLocaleDateString(undefined, options); | |
} | |
function getStatusColor(status) { | |
if (status === 'Submitted') return 'bg-green-100 text-green-800'; | |
if (status === 'Pending') return 'bg-yellow-100 text-yellow-800'; | |
if (status === 'Late') return 'bg-red-100 text-red-800'; | |
return 'bg-gray-100 text-gray-800'; | |
} | |
// Initialize the app | |
init(); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=ABSONUMBER1/evaluation" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |