| <!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> |