|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Mobile Attendance 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> |
|
|
.hidden { |
|
|
display: none; |
|
|
} |
|
|
.active-tab { |
|
|
border-bottom: 3px solid #3b82f6; |
|
|
color: #3b82f6; |
|
|
font-weight: 600; |
|
|
} |
|
|
.attendance-card:hover { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
.progress-bar { |
|
|
height: 8px; |
|
|
border-radius: 4px; |
|
|
background-color: #e5e7eb; |
|
|
} |
|
|
.progress-fill { |
|
|
height: 100%; |
|
|
border-radius: 4px; |
|
|
background-color: #3b82f6; |
|
|
transition: width 0.5s ease-in-out; |
|
|
} |
|
|
#deviceIdDisplay { |
|
|
font-family: monospace; |
|
|
background-color: #f3f4f6; |
|
|
padding: 0.25rem 0.5rem; |
|
|
border-radius: 0.25rem; |
|
|
font-size: 0.875rem; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-50 min-h-screen"> |
|
|
|
|
|
<div id="loginScreen" class="flex flex-col items-center justify-center min-h-screen p-4"> |
|
|
<div class="w-full max-w-md bg-white rounded-lg shadow-md p-8"> |
|
|
<div class="text-center mb-8"> |
|
|
<i class="fas fa-user-graduate text-5xl text-blue-500 mb-4"></i> |
|
|
<h1 class="text-3xl font-bold text-gray-800">Attendance System</h1> |
|
|
<p class="text-gray-600 mt-2">Sign in to access your account</p> |
|
|
</div> |
|
|
|
|
|
<form id="loginForm" class="space-y-6"> |
|
|
<div> |
|
|
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label> |
|
|
<input type="email" id="email" required class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label> |
|
|
<input type="password" id="password" required class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
|
</div> |
|
|
|
|
|
<div class="flex items-center justify-between"> |
|
|
<div class="flex items-center"> |
|
|
<input type="checkbox" id="remember" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"> |
|
|
<label for="remember" class="ml-2 block text-sm text-gray-700">Remember me</label> |
|
|
</div> |
|
|
<a href="#" class="text-sm text-blue-600 hover:text-blue-500">Forgot password?</a> |
|
|
</div> |
|
|
|
|
|
<button type="submit" class="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-150"> |
|
|
Sign In |
|
|
</button> |
|
|
</form> |
|
|
|
|
|
<div class="mt-6 text-center"> |
|
|
<p class="text-sm text-gray-600">Don't have an account? <a href="#" class="text-blue-600 hover:text-blue-500 font-medium">Contact admin</a></p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="teacherDashboard" class="hidden min-h-screen"> |
|
|
<header class="bg-white shadow-sm"> |
|
|
<div class="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8 flex justify-between items-center"> |
|
|
<h1 class="text-xl font-semibold text-gray-900">Teacher Dashboard</h1> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<span id="teacherName" class="text-gray-700">John Doe</span> |
|
|
<button id="teacherLogout" class="text-gray-500 hover:text-gray-700"> |
|
|
<i class="fas fa-sign-out-alt"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="border-b border-gray-200"> |
|
|
<nav class="-mb-px flex space-x-8 px-4"> |
|
|
<button id="teacherSessionsTab" class="py-4 px-1 border-b-2 font-medium text-sm active-tab"> |
|
|
Attendance Sessions |
|
|
</button> |
|
|
<button id="teacherStudentsTab" class="py-4 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"> |
|
|
Manage Students |
|
|
</button> |
|
|
<button id="teacherReportsTab" class="py-4 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"> |
|
|
Reports |
|
|
</button> |
|
|
</nav> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
<main class="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8"> |
|
|
|
|
|
<div id="teacherSessionsContent" class="space-y-6"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<h2 class="text-lg font-medium text-gray-900">Current Sessions</h2> |
|
|
<button id="createSessionBtn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"> |
|
|
<i class="fas fa-plus mr-2"></i> New Session |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div id="activeSessions" class="bg-white shadow overflow-hidden sm:rounded-md"> |
|
|
<ul class="divide-y divide-gray-200"> |
|
|
|
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
<h2 class="text-lg font-medium text-gray-900">Session History</h2> |
|
|
<div id="pastSessions" class="bg-white shadow overflow-hidden sm:rounded-md"> |
|
|
<ul class="divide-y divide-gray-200"> |
|
|
|
|
|
</ul> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="teacherStudentsContent" class="hidden space-y-6"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<h2 class="text-lg font-medium text-gray-900">Class Students</h2> |
|
|
<button id="addStudentBtn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"> |
|
|
<i class="fas fa-user-plus mr-2"></i> Add Student |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="bg-white shadow overflow-hidden sm:rounded-md"> |
|
|
<ul id="studentsList" class="divide-y divide-gray-200"> |
|
|
|
|
|
</ul> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="teacherReportsContent" class="hidden"> |
|
|
<h2 class="text-lg font-medium text-gray-900 mb-4">Attendance Reports</h2> |
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
|
<div class="bg-white p-6 rounded-lg shadow"> |
|
|
<h3 class="text-md font-medium text-gray-900 mb-4">Class Attendance Summary</h3> |
|
|
<div id="classAttendanceChart" class="h-64"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-white p-6 rounded-lg shadow"> |
|
|
<h3 class="text-md font-medium text-gray-900 mb-4">Recent Attendance</h3> |
|
|
<div id="recentAttendanceTable" class="overflow-x-auto"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
|
|
|
|
|
|
<div id="createSessionModal" class="hidden fixed inset-0 overflow-y-auto z-10"> |
|
|
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> |
|
|
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|
|
<div class="absolute inset-0 bg-gray-500 opacity-75"></div> |
|
|
</div> |
|
|
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> |
|
|
|
|
|
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> |
|
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
|
|
<div class="sm:flex sm:items-start"> |
|
|
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> |
|
|
<i class="fas fa-calendar-plus text-blue-600"></i> |
|
|
</div> |
|
|
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Create New Attendance Session</h3> |
|
|
<div class="mt-2"> |
|
|
<form id="createSessionForm" class="space-y-4"> |
|
|
<div> |
|
|
<label for="sessionName" class="block text-sm font-medium text-gray-700">Session Name</label> |
|
|
<input type="text" id="sessionName" required class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label for="sessionDuration" class="block text-sm font-medium text-gray-700">Duration (minutes)</label> |
|
|
<input type="number" id="sessionDuration" min="1" max="120" value="15" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label for="sessionClass" class="block text-sm font-medium text-gray-700">Class</label> |
|
|
<select id="sessionClass" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"> |
|
|
<option>CS101</option> |
|
|
<option>MATH202</option> |
|
|
<option>ENG150</option> |
|
|
</select> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> |
|
|
<button type="button" id="confirmCreateSession" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Create Session |
|
|
</button> |
|
|
<button type="button" id="cancelCreateSession" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Cancel |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="addStudentModal" class="hidden fixed inset-0 overflow-y-auto z-10"> |
|
|
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> |
|
|
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|
|
<div class="absolute inset-0 bg-gray-500 opacity-75"></div> |
|
|
</div> |
|
|
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> |
|
|
|
|
|
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> |
|
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
|
|
<div class="sm:flex sm:items-start"> |
|
|
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> |
|
|
<i class="fas fa-user-plus text-blue-600"></i> |
|
|
</div> |
|
|
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Add New Student</h3> |
|
|
<div class="mt-2"> |
|
|
<form id="addStudentForm" class="space-y-4"> |
|
|
<div> |
|
|
<label for="studentName" class="block text-sm font-medium text-gray-700">Full Name</label> |
|
|
<input type="text" id="studentName" required class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label for="studentEmail" class="block text-sm font-medium text-gray-700">Email</label> |
|
|
<input type="email" id="studentEmail" required class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label for="studentId" class="block text-sm font-medium text-gray-700">Student ID</label> |
|
|
<input type="text" id="studentId" required class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label for="studentClass" class="block text-sm font-medium text-gray-700">Class</label> |
|
|
<select id="studentClass" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"> |
|
|
<option>CS101</option> |
|
|
<option>MATH202</option> |
|
|
<option>ENG150</option> |
|
|
</select> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> |
|
|
<button type="button" id="confirmAddStudent" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Add Student |
|
|
</button> |
|
|
<button type="button" id="cancelAddStudent" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Cancel |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="studentDashboard" class="hidden min-h-screen"> |
|
|
<header class="bg-white shadow-sm"> |
|
|
<div class="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8 flex justify-between items-center"> |
|
|
<h1 class="text-xl font-semibold text-gray-900">Student Dashboard</h1> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<span id="studentName" class="text-gray-700">Jane Smith</span> |
|
|
<button id="studentLogout" class="text-gray-500 hover:text-gray-700"> |
|
|
<i class="fas fa-sign-out-alt"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="border-b border-gray-200"> |
|
|
<nav class="-mb-px flex space-x-8 px-4"> |
|
|
<button id="studentAttendanceTab" class="py-4 px-1 border-b-2 font-medium text-sm active-tab"> |
|
|
Mark Attendance |
|
|
</button> |
|
|
<button id="studentHistoryTab" class="py-4 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"> |
|
|
My History |
|
|
</button> |
|
|
<button id="studentStatsTab" class="py-4 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"> |
|
|
My Stats |
|
|
</button> |
|
|
</nav> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
<main class="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8"> |
|
|
|
|
|
<div id="studentAttendanceContent" class="space-y-6"> |
|
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg"> |
|
|
<div class="px-4 py-5 sm:px-6 border-b border-gray-200"> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Current Attendance Sessions</h3> |
|
|
<p class="mt-1 text-sm text-gray-500">Available sessions where you can mark your attendance.</p> |
|
|
</div> |
|
|
<div id="availableSessions" class="divide-y divide-gray-200"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg"> |
|
|
<div class="px-4 py-5 sm:px-6 border-b border-gray-200"> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Device Information</h3> |
|
|
<p class="mt-1 text-sm text-gray-500">Your attendance is tied to this device for security.</p> |
|
|
</div> |
|
|
<div class="px-4 py-5 sm:p-6"> |
|
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> |
|
|
<div> |
|
|
<label class="block text-sm font-medium text-gray-700">Device ID</label> |
|
|
<div id="deviceIdDisplay" class="mt-1">Loading...</div> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm font-medium text-gray-700">Status</label> |
|
|
<div class="mt-1"> |
|
|
<span id="deviceStatus" class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800"> |
|
|
<i class="fas fa-check-circle mr-1"></i> Verified |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="mt-4"> |
|
|
<p class="text-sm text-gray-600">If you need to change your device, please contact your instructor.</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="studentHistoryContent" class="hidden"> |
|
|
<h2 class="text-lg font-medium text-gray-900 mb-4">My Attendance History</h2> |
|
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg"> |
|
|
<div class="px-4 py-5 sm:px-6 border-b border-gray-200"> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Recent Attendance Records</h3> |
|
|
<p class="mt-1 text-sm text-gray-500">Your last 10 attendance marks.</p> |
|
|
</div> |
|
|
<div id="attendanceHistory" class="divide-y divide-gray-200"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="studentStatsContent" class="hidden"> |
|
|
<h2 class="text-lg font-medium text-gray-900 mb-4">My Attendance Statistics</h2> |
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
|
<div class="bg-white p-6 rounded-lg shadow"> |
|
|
<h3 class="text-md font-medium text-gray-900 mb-4">Overall Attendance</h3> |
|
|
<div class="flex items-center justify-between mb-2"> |
|
|
<span class="text-sm font-medium text-gray-700">Attendance Rate</span> |
|
|
<span id="attendanceRate" class="text-sm font-medium text-blue-600">85%</span> |
|
|
</div> |
|
|
<div class="progress-bar"> |
|
|
<div id="attendanceProgress" class="progress-fill" style="width: 85%"></div> |
|
|
</div> |
|
|
<div class="mt-4 grid grid-cols-2 gap-4"> |
|
|
<div class="bg-blue-50 p-3 rounded-lg"> |
|
|
<p class="text-sm font-medium text-gray-500">Present</p> |
|
|
<p id="presentCount" class="text-2xl font-semibold text-blue-600">17</p> |
|
|
</div> |
|
|
<div class="bg-gray-50 p-3 rounded-lg"> |
|
|
<p class="text-sm font-medium text-gray-500">Absent</p> |
|
|
<p id="absentCount" class="text-2xl font-semibold text-gray-600">3</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-white p-6 rounded-lg shadow"> |
|
|
<h3 class="text-md font-medium text-gray-900 mb-4">Attendance by Class</h3> |
|
|
<div id="classAttendanceStats" class="space-y-4"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
|
|
|
|
|
|
<div id="attendanceConfirmationModal" class="hidden fixed inset-0 overflow-y-auto z-10"> |
|
|
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> |
|
|
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|
|
<div class="absolute inset-0 bg-gray-500 opacity-75"></div> |
|
|
</div> |
|
|
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> |
|
|
|
|
|
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> |
|
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
|
|
<div class="sm:flex sm:items-start"> |
|
|
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> |
|
|
<i class="fas fa-check-circle text-blue-600"></i> |
|
|
</div> |
|
|
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Confirm Attendance</h3> |
|
|
<div class="mt-2"> |
|
|
<p class="text-sm text-gray-500">You are about to mark your attendance for <span id="confirmSessionName" class="font-medium">CS101 Lecture</span>.</p> |
|
|
<p class="text-sm text-gray-500 mt-2">This action cannot be undone.</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> |
|
|
<button type="button" id="confirmAttendance" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Confirm |
|
|
</button> |
|
|
<button type="button" id="cancelAttendance" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Cancel |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="attendanceSuccessModal" class="hidden fixed inset-0 overflow-y-auto z-10"> |
|
|
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> |
|
|
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|
|
<div class="absolute inset-0 bg-gray-500 opacity-75"></div> |
|
|
</div> |
|
|
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> |
|
|
|
|
|
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> |
|
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
|
|
<div class="sm:flex sm:items-start"> |
|
|
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-green-100 sm:mx-0 sm:h-10 sm:w-10"> |
|
|
<i class="fas fa-check text-green-600"></i> |
|
|
</div> |
|
|
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Attendance Recorded</h3> |
|
|
<div class="mt-2"> |
|
|
<p class="text-sm text-gray-500">Your attendance for <span id="successSessionName" class="font-medium">CS101 Lecture</span> has been successfully recorded.</p> |
|
|
<p class="text-sm text-gray-500 mt-2">Time: <span id="attendanceTime" class="font-medium">10:15 AM</span></p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> |
|
|
<button type="button" id="closeSuccessModal" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
OK |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
const db = { |
|
|
users: [ |
|
|
{ id: 1, email: 'teacher@school.edu', password: 'teacher123', name: 'John Doe', role: 'teacher' }, |
|
|
{ id: 2, email: 'student@school.edu', password: 'student123', name: 'Jane Smith', role: 'student', studentId: 'S1001', class: 'CS101', deviceId: 'device123' }, |
|
|
{ id: 3, email: 'student2@school.edu', password: 'student123', name: 'Mike Johnson', role: 'student', studentId: 'S1002', class: 'CS101', deviceId: 'device456' } |
|
|
], |
|
|
sessions: [ |
|
|
{ id: 1, name: 'CS101 Lecture', class: 'CS101', teacherId: 1, status: 'active', startTime: new Date(), endTime: new Date(Date.now() + 15 * 60000), attendees: [2] }, |
|
|
{ id: 2, name: 'CS101 Lab', class: 'CS101', teacherId: 1, status: 'closed', startTime: new Date(Date.now() - 86400000), endTime: new Date(Date.now() - 86400000 + 60 * 60000), attendees: [2, 3] }, |
|
|
{ id: 3, name: 'MATH202 Lecture', class: 'MATH202', teacherId: 1, status: 'closed', startTime: new Date(Date.now() - 2 * 86400000), endTime: new Date(Date.now() - 2 * 86400000 + 30 * 60000), attendees: [2] } |
|
|
], |
|
|
attendance: [ |
|
|
{ id: 1, sessionId: 2, studentId: 2, timestamp: new Date(Date.now() - 86400000 + 5 * 60000) }, |
|
|
{ id: 2, sessionId: 2, studentId: 3, timestamp: new Date(Date.now() - 86400000 + 10 * 60000) }, |
|
|
{ id: 3, sessionId: 3, studentId: 2, timestamp: new Date(Date.now() - 2 * 86400000 + 5 * 60000) } |
|
|
] |
|
|
}; |
|
|
|
|
|
|
|
|
let currentUser = null; |
|
|
const deviceId = 'device' + Math.floor(Math.random() * 1000000); |
|
|
|
|
|
|
|
|
const loginScreen = document.getElementById('loginScreen'); |
|
|
const teacherDashboard = document.getElementById('teacherDashboard'); |
|
|
const studentDashboard = document.getElementById('studentDashboard'); |
|
|
const loginForm = document.getElementById('loginForm'); |
|
|
const teacherLogout = document.getElementById('teacherLogout'); |
|
|
const studentLogout = document.getElementById('studentLogout'); |
|
|
|
|
|
|
|
|
const teacherSessionsTab = document.getElementById('teacherSessionsTab'); |
|
|
const teacherStudentsTab = document.getElementById('teacherStudentsTab'); |
|
|
const teacherReportsTab = document.getElementById('teacherReportsTab'); |
|
|
const teacherSessionsContent = document.getElementById('teacherSessionsContent'); |
|
|
const teacherStudentsContent = document.getElementById('teacherStudentsContent'); |
|
|
const teacherReportsContent = document.getElementById('teacherReportsContent'); |
|
|
const createSessionBtn = document.getElementById('createSessionBtn'); |
|
|
const createSessionModal = document.getElementById('createSessionModal'); |
|
|
const cancelCreateSession = document.getElementById('cancelCreateSession'); |
|
|
const confirmCreateSession = document.getElementById('confirmCreateSession'); |
|
|
const activeSessions = document.getElementById('activeSessions'); |
|
|
const pastSessions = document.getElementById('pastSessions'); |
|
|
const addStudentBtn = document.getElementById('addStudentBtn'); |
|
|
const addStudentModal = document.getElementById('addStudentModal'); |
|
|
const cancelAddStudent = document.getElementById('cancelAddStudent'); |
|
|
const confirmAddStudent = document.getElementById('confirmAddStudent'); |
|
|
const studentsList = document.getElementById('studentsList'); |
|
|
|
|
|
|
|
|
const studentAttendanceTab = document.getElementById('studentAttendanceTab'); |
|
|
const studentHistoryTab = document.getElementById('studentHistoryTab'); |
|
|
const studentStatsTab = document.getElementById('studentStatsTab'); |
|
|
const studentAttendanceContent = document.getElementById('studentAttendanceContent'); |
|
|
const studentHistoryContent = document.getElementById('studentHistoryContent'); |
|
|
const studentStatsContent = document.getElementById('studentStatsContent'); |
|
|
const availableSessions = document.getElementById('availableSessions'); |
|
|
const attendanceHistory = document.getElementById('attendanceHistory'); |
|
|
const attendanceConfirmationModal = document.getElementById('attendanceConfirmationModal'); |
|
|
const confirmAttendance = document.getElementById('confirmAttendance'); |
|
|
const cancelAttendance = document.getElementById('cancelAttendance'); |
|
|
const attendanceSuccessModal = document.getElementById('attendanceSuccessModal'); |
|
|
const closeSuccessModal = document.getElementById('closeSuccessModal'); |
|
|
const deviceIdDisplay = document.getElementById('deviceIdDisplay'); |
|
|
const deviceStatus = document.getElementById('deviceStatus'); |
|
|
const attendanceRate = document.getElementById('attendanceRate'); |
|
|
const attendanceProgress = document.getElementById('attendanceProgress'); |
|
|
const presentCount = document.getElementById('presentCount'); |
|
|
const absentCount = document.getElementById('absentCount'); |
|
|
const classAttendanceStats = document.getElementById('classAttendanceStats'); |
|
|
|
|
|
|
|
|
loginForm.addEventListener('submit', function(e) { |
|
|
e.preventDefault(); |
|
|
const email = document.getElementById('email').value; |
|
|
const password = document.getElementById('password').value; |
|
|
|
|
|
const user = db.users.find(u => u.email === email && u.password === password); |
|
|
|
|
|
if (user) { |
|
|
currentUser = user; |
|
|
|
|
|
if (user.role === 'teacher') { |
|
|
showTeacherDashboard(); |
|
|
} else if (user.role === 'student') { |
|
|
|
|
|
if (!user.deviceId) { |
|
|
|
|
|
user.deviceId = deviceId; |
|
|
showStudentDashboard(); |
|
|
} else if (user.deviceId === deviceId) { |
|
|
|
|
|
showStudentDashboard(); |
|
|
} else { |
|
|
|
|
|
alert('You can only access your account from your registered device. Please contact your instructor if you need to change devices.'); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
loginScreen.classList.add('hidden'); |
|
|
} else { |
|
|
alert('Invalid email or password'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
teacherLogout.addEventListener('click', function() { |
|
|
currentUser = null; |
|
|
teacherDashboard.classList.add('hidden'); |
|
|
loginScreen.classList.remove('hidden'); |
|
|
resetTeacherDashboard(); |
|
|
}); |
|
|
|
|
|
studentLogout.addEventListener('click', function() { |
|
|
currentUser = null; |
|
|
studentDashboard.classList.add('hidden'); |
|
|
loginScreen.classList.remove('hidden'); |
|
|
resetStudentDashboard(); |
|
|
}); |
|
|
|
|
|
|
|
|
teacherSessionsTab.addEventListener('click', function() { |
|
|
setActiveTab('teacher', 'sessions'); |
|
|
}); |
|
|
|
|
|
teacherStudentsTab.addEventListener('click', function() { |
|
|
setActiveTab('teacher', 'students'); |
|
|
}); |
|
|
|
|
|
teacherReportsTab.addEventListener('click', function() { |
|
|
setActiveTab('teacher', 'reports'); |
|
|
}); |
|
|
|
|
|
|
|
|
studentAttendanceTab.addEventListener('click', function() { |
|
|
setActiveTab('student', 'attendance'); |
|
|
}); |
|
|
|
|
|
studentHistoryTab.addEventListener('click', function() { |
|
|
setActiveTab('student', 'history'); |
|
|
}); |
|
|
|
|
|
studentStatsTab.addEventListener('click', function() { |
|
|
setActiveTab('student', 'stats'); |
|
|
}); |
|
|
|
|
|
|
|
|
createSessionBtn.addEventListener('click', function() { |
|
|
createSessionModal.classList.remove('hidden'); |
|
|
}); |
|
|
|
|
|
cancelCreateSession.addEventListener('click', function() { |
|
|
createSessionModal.classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
confirmCreateSession.addEventListener('click', function() { |
|
|
const sessionName = document.getElementById('sessionName').value; |
|
|
const sessionDuration = parseInt(document.getElementById('sessionDuration').value); |
|
|
const sessionClass = document.getElementById('sessionClass').value; |
|
|
|
|
|
const newSession = { |
|
|
id: db.sessions.length + 1, |
|
|
name: sessionName, |
|
|
class: sessionClass, |
|
|
teacherId: currentUser.id, |
|
|
status: 'active', |
|
|
startTime: new Date(), |
|
|
endTime: new Date(Date.now() + sessionDuration * 60000), |
|
|
attendees: [] |
|
|
}; |
|
|
|
|
|
db.sessions.push(newSession); |
|
|
createSessionModal.classList.add('hidden'); |
|
|
document.getElementById('createSessionForm').reset(); |
|
|
renderTeacherSessions(); |
|
|
}); |
|
|
|
|
|
|
|
|
addStudentBtn.addEventListener('click', function() { |
|
|
addStudentModal.classList.remove('hidden'); |
|
|
}); |
|
|
|
|
|
cancelAddStudent.addEventListener('click', function() { |
|
|
addStudentModal.classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
confirmAddStudent.addEventListener('click', function() { |
|
|
const studentName = document.getElementById('studentName').value; |
|
|
const studentEmail = document.getElementById('studentEmail').value; |
|
|
const studentId = document.getElementById('studentId').value; |
|
|
const studentClass = document.getElementById('studentClass').value; |
|
|
|
|
|
const newStudent = { |
|
|
id: db.users.length + 1, |
|
|
email: studentEmail, |
|
|
password: 'student123', |
|
|
name: studentName, |
|
|
role: 'student', |
|
|
studentId: studentId, |
|
|
class: studentClass, |
|
|
deviceId: null |
|
|
}; |
|
|
|
|
|
db.users.push(newStudent); |
|
|
addStudentModal.classList.add('hidden'); |
|
|
document.getElementById('addStudentForm').reset(); |
|
|
renderStudentsList(); |
|
|
}); |
|
|
|
|
|
|
|
|
function setupAttendanceButtons() { |
|
|
document.querySelectorAll('.mark-attendance-btn').forEach(button => { |
|
|
button.addEventListener('click', function() { |
|
|
const sessionId = parseInt(this.getAttribute('data-session-id')); |
|
|
const session = db.sessions.find(s => s.id === sessionId); |
|
|
|
|
|
document.getElementById('confirmSessionName').textContent = session.name; |
|
|
document.getElementById('confirmAttendance').setAttribute('data-session-id', sessionId); |
|
|
attendanceConfirmationModal.classList.remove('hidden'); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
confirmAttendance.addEventListener('click', function() { |
|
|
const sessionId = parseInt(this.getAttribute('data-session-id')); |
|
|
const session = db.sessions.find(s => s.id === sessionId); |
|
|
|
|
|
|
|
|
const newAttendance = { |
|
|
id: db.attendance.length + 1, |
|
|
sessionId: sessionId, |
|
|
studentId: currentUser.id, |
|
|
timestamp: new Date() |
|
|
}; |
|
|
|
|
|
db.attendance.push(newAttendance); |
|
|
session.attendees.push(currentUser.id); |
|
|
|
|
|
|
|
|
document.getElementById('successSessionName').textContent = session.name; |
|
|
document.getElementById('attendanceTime').textContent = newAttendance.timestamp.toLocaleTimeString(); |
|
|
attendanceConfirmationModal.classList.add('hidden'); |
|
|
attendanceSuccessModal.classList.remove('hidden'); |
|
|
|
|
|
|
|
|
renderAvailableSessions(); |
|
|
}); |
|
|
|
|
|
cancelAttendance.addEventListener('click', function() { |
|
|
attendanceConfirmationModal.classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
closeSuccessModal.addEventListener('click', function() { |
|
|
attendanceSuccessModal.classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
|
|
|
function showTeacherDashboard() { |
|
|
teacherDashboard.classList.remove('hidden'); |
|
|
document.getElementById('teacherName').textContent = currentUser.name; |
|
|
renderTeacherSessions(); |
|
|
renderStudentsList(); |
|
|
setActiveTab('teacher', 'sessions'); |
|
|
} |
|
|
|
|
|
function showStudentDashboard() { |
|
|
studentDashboard.classList.remove('hidden'); |
|
|
document.getElementById('studentName').textContent = currentUser.name; |
|
|
deviceIdDisplay.textContent = currentUser.deviceId; |
|
|
|
|
|
|
|
|
if (currentUser.deviceId === deviceId) { |
|
|
deviceStatus.innerHTML = '<i class="fas fa-check-circle mr-1"></i> Verified'; |
|
|
deviceStatus.className = 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800'; |
|
|
} else { |
|
|
deviceStatus.innerHTML = '<i class="fas fa-exclamation-triangle mr-1"></i> Mismatch'; |
|
|
deviceStatus.className = 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800'; |
|
|
} |
|
|
|
|
|
renderAvailableSessions(); |
|
|
renderAttendanceHistory(); |
|
|
renderStudentStats(); |
|
|
setActiveTab('student', 'attendance'); |
|
|
} |
|
|
|
|
|
function resetTeacherDashboard() { |
|
|
teacherSessionsContent.classList.remove('hidden'); |
|
|
teacherStudentsContent.classList.add('hidden'); |
|
|
teacherReportsContent.classList.add('hidden'); |
|
|
|
|
|
teacherSessionsTab.classList.add('active-tab'); |
|
|
teacherStudentsTab.classList.remove('active-tab'); |
|
|
teacherReportsTab.classList.remove('active-tab'); |
|
|
} |
|
|
|
|
|
function resetStudentDashboard() { |
|
|
studentAttendanceContent.classList.remove('hidden'); |
|
|
studentHistoryContent.classList.add('hidden'); |
|
|
studentStatsContent.classList.add('hidden'); |
|
|
|
|
|
studentAttendanceTab.classList.add('active-tab'); |
|
|
studentHistoryTab.classList.remove('active-tab'); |
|
|
studentStatsTab.classList.remove('active-tab'); |
|
|
} |
|
|
|
|
|
function setActiveTab(role, tab) { |
|
|
if (role === 'teacher') { |
|
|
teacherSessionsContent.classList.add('hidden'); |
|
|
teacherStudentsContent.classList.add('hidden'); |
|
|
teacherReportsContent.classList.add('hidden'); |
|
|
|
|
|
teacherSessionsTab.classList.remove('active-tab'); |
|
|
teacherStudentsTab.classList.remove('active-tab'); |
|
|
teacherReportsTab.classList.remove('active-tab'); |
|
|
|
|
|
if (tab === 'sessions') { |
|
|
teacherSessionsContent.classList.remove('hidden'); |
|
|
teacherSessionsTab.classList.add('active-tab'); |
|
|
renderTeacherSessions(); |
|
|
} else if (tab === 'students') { |
|
|
teacherStudentsContent.classList.remove('hidden'); |
|
|
teacherStudentsTab.classList.add('active-tab'); |
|
|
renderStudentsList(); |
|
|
} else if (tab === 'reports') { |
|
|
teacherReportsContent.classList.remove('hidden'); |
|
|
teacherReportsTab.classList.add('active-tab'); |
|
|
renderTeacherReports(); |
|
|
} |
|
|
} else if (role === 'student') { |
|
|
studentAttendanceContent.classList.add('hidden'); |
|
|
studentHistoryContent.classList.add('hidden'); |
|
|
studentStatsContent.classList.add('hidden'); |
|
|
|
|
|
studentAttendanceTab.classList.remove('active-tab'); |
|
|
studentHistoryTab.classList.remove('active-tab'); |
|
|
studentStatsTab.classList.remove('active-tab'); |
|
|
|
|
|
if (tab === 'attendance') { |
|
|
studentAttendanceContent.classList.remove('hidden'); |
|
|
studentAttendanceTab.classList.add('active-tab'); |
|
|
renderAvailableSessions(); |
|
|
} else if (tab === 'history') { |
|
|
studentHistoryContent.classList.remove('hidden'); |
|
|
studentHistoryTab.classList.add('active-tab'); |
|
|
renderAttendanceHistory(); |
|
|
} else if (tab === 'stats') { |
|
|
studentStatsContent.classList.remove('hidden'); |
|
|
studentStatsTab.classList.add('active-tab'); |
|
|
renderStudentStats(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function renderTeacherSessions() { |
|
|
|
|
|
const activeSessionsList = db.sessions.filter(s => s.status === 'active' && s.teacherId === currentUser.id); |
|
|
const activeSessionsUl = document.querySelector('#activeSessions ul'); |
|
|
activeSessionsUl.innerHTML = ''; |
|
|
|
|
|
if (activeSessionsList.length === 0) { |
|
|
activeSessionsUl.innerHTML = '<li class="px-4 py-4 text-center text-gray-500">No active sessions</li>'; |
|
|
} else { |
|
|
activeSessionsList.forEach(session => { |
|
|
const attendeesCount = session.attendees.length; |
|
|
const studentsCount = db.users.filter(u => u.role === 'student' && u.class === session.class).length; |
|
|
|
|
|
const li = document.createElement('li'); |
|
|
li.className = 'px-4 py-4'; |
|
|
li.innerHTML = ` |
|
|
<div class="flex items-center justify-between"> |
|
|
<div class="flex items-center"> |
|
|
<div class="min-w-0 flex-1"> |
|
|
<div class="flex items-center"> |
|
|
<p class="text-sm font-medium text-blue-600 truncate">${session.name}</p> |
|
|
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800"> |
|
|
${session.class} |
|
|
</span> |
|
|
</div> |
|
|
<div class="mt-2 flex"> |
|
|
<div class="flex items-center text-sm text-gray-500"> |
|
|
<i class="fas fa-users mr-1.5"></i> |
|
|
<span>${attendeesCount}/${studentsCount} students</span> |
|
|
</div> |
|
|
<div class="ml-3 flex items-center text-sm text-gray-500"> |
|
|
<i class="fas fa-clock mr-1.5"></i> |
|
|
<span>Ends at ${session.endTime.toLocaleTimeString()}</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<button class="close-session-btn inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500" data-session-id="${session.id}"> |
|
|
<i class="fas fa-times mr-1"></i> Close |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
activeSessionsUl.appendChild(li); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const pastSessionsList = db.sessions.filter(s => s.status === 'closed' && s.teacherId === currentUser.id) |
|
|
.sort((a, b) => b.startTime - a.startTime); |
|
|
const pastSessionsUl = document.querySelector('#pastSessions ul'); |
|
|
pastSessionsUl.innerHTML = ''; |
|
|
|
|
|
if (pastSessionsList.length === 0) { |
|
|
pastSessionsUl.innerHTML = '<li class="px-4 py-4 text-center text-gray-500">No past sessions</li>'; |
|
|
} else { |
|
|
pastSessionsList.forEach(session => { |
|
|
const attendeesCount = session.attendees.length; |
|
|
const studentsCount = db.users.filter(u => u.role === 'student' && u.class === session.class).length; |
|
|
|
|
|
const li = document.createElement('li'); |
|
|
li.className = 'px-4 py-4'; |
|
|
li.innerHTML = ` |
|
|
<div class="flex items-center justify-between"> |
|
|
<div class="flex items-center"> |
|
|
<div class="min-w-0 flex-1"> |
|
|
<div class="flex items-center"> |
|
|
<p class="text-sm font-medium text-gray-900 truncate">${session.name}</p> |
|
|
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800"> |
|
|
${session.class} |
|
|
</span> |
|
|
</div> |
|
|
<div class="mt-2 flex"> |
|
|
<div class="flex items-center text-sm text-gray-500"> |
|
|
<i class="fas fa-users mr-1.5"></i> |
|
|
<span>${attendeesCount}/${studentsCount} students</span> |
|
|
</div> |
|
|
<div class="ml-3 flex items-center text-sm text-gray-500"> |
|
|
<i class="fas fa-calendar-day mr-1.5"></i> |
|
|
<span>${session.startTime.toLocaleDateString()}</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<button class="view-session-btn inline-flex items-center px-3 py-1.5 border border-gray-300 text-xs font-medium rounded shadow-sm text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" data-session-id="${session.id}"> |
|
|
<i class="fas fa-eye mr-1"></i> View |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
pastSessionsUl.appendChild(li); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
document.querySelectorAll('.close-session-btn').forEach(button => { |
|
|
button.addEventListener('click', function() { |
|
|
const sessionId = parseInt(this.getAttribute('data-session-id')); |
|
|
const session = db.sessions.find(s => s.id === sessionId); |
|
|
session.status = 'closed'; |
|
|
renderTeacherSessions(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
document.querySelectorAll('.view-session-btn').forEach(button => { |
|
|
button.addEventListener('click', function() { |
|
|
const sessionId = parseInt(this.getAttribute('data-session-id')); |
|
|
|
|
|
alert('Viewing session details would show here'); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
function renderStudentsList() { |
|
|
const students = db.users.filter(u => u.role === 'student' && u.class === 'CS101'); |
|
|
studentsList.innerHTML = ''; |
|
|
|
|
|
if (students.length === 0) { |
|
|
studentsList.innerHTML = '<li class="px-4 py-4 text-center text-gray-500">No students in this class</li>'; |
|
|
} else { |
|
|
students.forEach(student => { |
|
|
const li = document.createElement('li'); |
|
|
li.className = 'px-4 py-4'; |
|
|
li.innerHTML = ` |
|
|
<div class="flex items-center justify-between"> |
|
|
<div class="flex items-center"> |
|
|
<div class="h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center"> |
|
|
<i class="fas fa-user text-gray-500"></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.email}</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex items-center"> |
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800"> |
|
|
${student.studentId} |
|
|
</span> |
|
|
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${student.deviceId ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'}"> |
|
|
${student.deviceId ? 'Device set' : 'No device'} |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
studentsList.appendChild(li); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
function renderTeacherReports() { |
|
|
|
|
|
document.getElementById('classAttendanceChart').innerHTML = ` |
|
|
<div class="flex items-center justify-center h-full"> |
|
|
<div class="text-center"> |
|
|
<i class="fas fa-chart-bar text-4xl text-gray-300 mb-2"></i> |
|
|
<p class="text-gray-500">Attendance charts would display here</p> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
document.getElementById('recentAttendanceTable').innerHTML = ` |
|
|
<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">Date</th> |
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Class</th> |
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Attendance</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody class="bg-white divide-y divide-gray-200"> |
|
|
<tr> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${new Date().toLocaleDateString()}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">CS101</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">15/20</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${new Date(Date.now() - 86400000).toLocaleDateString()}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">CS101</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">18/20</td> |
|
|
</tr> |
|
|
</tbody> |
|
|
</table> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderAvailableSessions() { |
|
|
const availableSessionsList = db.sessions.filter(s => |
|
|
s.status === 'active' && |
|
|
s.class === currentUser.class && |
|
|
!s.attendees.includes(currentUser.id) |
|
|
); |
|
|
|
|
|
availableSessions.innerHTML = ''; |
|
|
|
|
|
if (availableSessionsList.length === 0) { |
|
|
availableSessions.innerHTML = ` |
|
|
<div class="px-4 py-5 sm:p-6 text-center"> |
|
|
<i class="fas fa-calendar-check text-4xl text-gray-300 mb-3"></i> |
|
|
<h3 class="text-lg font-medium text-gray-900">No available sessions</h3> |
|
|
<p class="mt-1 text-sm text-gray-500">There are currently no active attendance sessions for your class.</p> |
|
|
</div> |
|
|
`; |
|
|
} else { |
|
|
availableSessionsList.forEach(session => { |
|
|
const endTime = new Date(session.endTime); |
|
|
const timeLeft = Math.max(0, Math.floor((endTime - new Date()) / 60000)); |
|
|
|
|
|
const div = document.createElement('div'); |
|
|
div.className = 'px-4 py-5 sm:p-6 attendance-card transition duration-150 ease-in-out'; |
|
|
div.innerHTML = ` |
|
|
<div class="flex items-center justify-between"> |
|
|
<div> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">${session.name}</h3> |
|
|
<p class="mt-1 text-sm text-gray-500">${session.class} • Ends in ${timeLeft} minutes</p> |
|
|
</div> |
|
|
<div> |
|
|
<button class="mark-attendance-btn inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" data-session-id="${session.id}"> |
|
|
<i class="fas fa-check-circle mr-2"></i> Mark Attendance |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
availableSessions.appendChild(div); |
|
|
}); |
|
|
|
|
|
setupAttendanceButtons(); |
|
|
} |
|
|
} |
|
|
|
|
|
function renderAttendanceHistory() { |
|
|
const studentAttendance = db.attendance |
|
|
.filter(a => a.studentId === currentUser.id) |
|
|
.sort((a, b) => b.timestamp - a.timestamp) |
|
|
.slice(0, 10); |
|
|
|
|
|
attendanceHistory.innerHTML = ''; |
|
|
|
|
|
if (studentAttendance.length === 0) { |
|
|
attendanceHistory.innerHTML = ` |
|
|
<div class="px-4 py-5 sm:p-6 text-center"> |
|
|
<i class="fas fa-history text-4xl text-gray-300 mb-3"></i> |
|
|
<h3 class="text-lg font-medium text-gray-900">No attendance history</h3> |
|
|
<p class="mt-1 text-sm text-gray-500">You haven't marked any attendance sessions yet.</p> |
|
|
</div> |
|
|
`; |
|
|
} else { |
|
|
studentAttendance.forEach(record => { |
|
|
const session = db.sessions.find(s => s.id === record.sessionId); |
|
|
|
|
|
const div = document.createElement('div'); |
|
|
div.className = 'px-4 py-5 sm:p-6'; |
|
|
div.innerHTML = ` |
|
|
<div class="flex items-center justify-between"> |
|
|
<div> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">${session.name}</h3> |
|
|
<p class="mt-1 text-sm text-gray-500">${session.class} • ${record.timestamp.toLocaleDateString()} at ${record.timestamp.toLocaleTimeString()}</p> |
|
|
</div> |
|
|
<div> |
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800"> |
|
|
<i class="fas fa-check mr-1"></i> Present |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
attendanceHistory.appendChild(div); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
function renderStudentStats() { |
|
|
|
|
|
const totalSessions = db.sessions.filter(s => s.class === currentUser.class && s.status === 'closed').length; |
|
|
const attendedSessions = db.attendance |
|
|
.filter(a => a.studentId === currentUser.id) |
|
|
.map(a => a.sessionId) |
|
|
.filter((v, i, a) => a.indexOf(v) === i).length; |
|
|
|
|
|
const attendancePercentage = totalSessions > 0 ? Math.round((attendedSessions / totalSessions) * 100) : 0; |
|
|
|
|
|
|
|
|
attendanceRate.textContent = `${attendancePercentage}%`; |
|
|
attendanceProgress.style.width = `${attendancePercentage}%`; |
|
|
presentCount.textContent = attendedSessions; |
|
|
absentCount.textContent = totalSessions - attendedSessions; |
|
|
|
|
|
|
|
|
const classes = [...new Set(db.sessions.map(s => s.class))]; |
|
|
classAttendanceStats.innerHTML = ''; |
|
|
|
|
|
classes.forEach(classId => { |
|
|
const classSessions = db.sessions.filter(s => s.class === classId && s.status === 'closed').length; |
|
|
const classAttended = db.attendance |
|
|
.filter(a => a.studentId === currentUser.id && db.sessions.find(s => s.id === a.sessionId && s.class === classId)) |
|
|
.map(a => a.sessionId) |
|
|
.filter((v, i, a) => a.indexOf(v) === i).length; |
|
|
|
|
|
const classPercentage = classSessions > 0 ? Math.round((classAttended / classSessions) * 100) : 0; |
|
|
|
|
|
const div = document.createElement('div'); |
|
|
div.className = 'flex items-center justify-between'; |
|
|
div.innerHTML = ` |
|
|
<div> |
|
|
<h4 class="text-sm font-medium text-gray-900">${classId}</h4> |
|
|
<p class="text-xs text-gray-500">${classAttended}/${classSessions} sessions</p> |
|
|
</div> |
|
|
<div class="w-1/2"> |
|
|
<div class="progress-bar"> |
|
|
<div class="progress-fill" style="width: ${classPercentage}%"></div> |
|
|
</div> |
|
|
<p class="text-xs text-right mt-1 text-gray-500">${classPercentage}%</p> |
|
|
</div> |
|
|
`; |
|
|
classAttendanceStats.appendChild(div); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
|
|
document.getElementById('email').value = 'teacher@school.edu'; |
|
|
document.getElementById('password').value = 'teacher123'; |
|
|
}); |
|
|
</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=Jacksonnavigator7/attendancy-system" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |