Drew4564 commited on
Commit
78282a4
Β·
verified Β·
1 Parent(s): 94638a5

can you make the exam page have like this border red because in this app we will have eye tracker use and that red border is the edge and make tghis on exam pc layout

Browse files
Files changed (4) hide show
  1. README.md +8 -5
  2. app.js +493 -0
  3. index.html +206 -19
  4. styles.css +70 -0
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Examproctor Eyetrack
3
- emoji: πŸ‘
4
- colorFrom: gray
5
- colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: ExamProctor EyeTrack πŸš€πŸ‘οΈ
3
+ colorFrom: green
4
+ colorTo: purple
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
app.js ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // App State
2
+ const state = {
3
+ isLoggedIn: false,
4
+ currentStudent: null,
5
+ exams: [
6
+ {
7
+ id: 1,
8
+ subject: "Mathematics",
9
+ date: "2023-06-15",
10
+ duration: 30, // minutes
11
+ questions: [
12
+ {
13
+ id: 1,
14
+ type: "multiple",
15
+ text: "What is the derivative of xΒ²?",
16
+ options: ["x", "2x", "xΒ²", "2xΒ²"],
17
+ answer: 1
18
+ },
19
+ {
20
+ id: 2,
21
+ type: "multiple",
22
+ text: "What is the value of Ο€ (pi) to two decimal places?",
23
+ options: ["3.14", "3.16", "3.12", "3.18"],
24
+ answer: 0
25
+ },
26
+ {
27
+ id: 3,
28
+ type: "short",
29
+ text: "Solve for x: 2x + 5 = 15",
30
+ answer: "5"
31
+ },
32
+ {
33
+ id: 4,
34
+ type: "multiple",
35
+ text: "Which of these is a prime number?",
36
+ options: ["4", "9", "11", "15"],
37
+ answer: 2
38
+ },
39
+ {
40
+ id: 5,
41
+ type: "short",
42
+ text: "What is the area of a circle with radius 3? (Use Ο€ β‰ˆ 3.14)",
43
+ answer: "28.26"
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ id: 2,
49
+ subject: "Physics",
50
+ date: "2023-06-20",
51
+ duration: 45,
52
+ questions: [
53
+ {
54
+ id: 1,
55
+ type: "multiple",
56
+ text: "What is the SI unit of force?",
57
+ options: ["Joule", "Watt", "Newton", "Pascal"],
58
+ answer: 2
59
+ },
60
+ {
61
+ id: 2,
62
+ type: "short",
63
+ text: "What is the acceleration due to gravity on Earth (in m/sΒ²)?",
64
+ answer: "9.8"
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ currentExam: null,
70
+ examStartTime: null,
71
+ examTimer: null,
72
+ suspiciousActivities: 0,
73
+ submissions: []
74
+ };
75
+
76
+ // DOM Elements
77
+ const loginScreen = document.getElementById('login-screen');
78
+ const appScreens = document.getElementById('app-screens');
79
+ const loginForm = document.getElementById('login-form');
80
+ const forgotPasswordBtn = document.getElementById('forgot-password');
81
+ const forgotModal = document.getElementById('forgot-modal');
82
+ const resetConfirmation = document.getElementById('reset-confirmation');
83
+ const studentName = document.getElementById('student-name');
84
+ const studentAvatar = document.getElementById('student-avatar');
85
+ const statusBadge = document.getElementById('status-badge');
86
+ const examList = document.getElementById('exam-list');
87
+ const examScreen = document.getElementById('exam-screen');
88
+ const examQuestions = document.getElementById('exam-questions');
89
+ const examTitle = document.getElementById('exam-title');
90
+ const examTimer = document.getElementById('exam-timer');
91
+ const examProgress = document.getElementById('exam-progress');
92
+ const submitExamBtn = document.getElementById('submit-exam');
93
+ const submissionList = document.getElementById('submission-list');
94
+ const logoutBtn = document.getElementById('logout-btn');
95
+ const navButtons = document.querySelectorAll('.nav-btn');
96
+
97
+ // Initialize the app
98
+ function init() {
99
+ checkAuth();
100
+ setupEventListeners();
101
+ renderExamList();
102
+ loadSubmissions();
103
+ }
104
+
105
+ // Check if user is logged in
106
+ function checkAuth() {
107
+ const student = localStorage.getItem('examGuardStudent');
108
+ if (student) {
109
+ state.isLoggedIn = true;
110
+ state.currentStudent = JSON.parse(student);
111
+ showApp();
112
+ } else {
113
+ showLogin();
114
+ }
115
+ }
116
+
117
+ // Show login screen
118
+ function showLogin() {
119
+ loginScreen.classList.remove('hidden');
120
+ appScreens.classList.add('hidden');
121
+ state.isLoggedIn = false;
122
+ }
123
+
124
+ // Show main app
125
+ function showApp() {
126
+ loginScreen.classList.add('hidden');
127
+ appScreens.classList.remove('hidden');
128
+
129
+ // Update student info
130
+ studentName.textContent = state.currentStudent?.name || 'Student';
131
+ studentAvatar.src = `https://static.photos/people/120x120/${state.currentStudent?.id || 1}`;
132
+
133
+ // Show dashboard by default
134
+ showScreen('dashboard');
135
+ updateStatusBadge();
136
+ }
137
+
138
+ // Show a specific screen
139
+ function showScreen(screenId) {
140
+ // Hide all screens
141
+ document.querySelectorAll('[id$="-screen"]').forEach(screen => {
142
+ screen.classList.add('hidden');
143
+ });
144
+
145
+ // Show requested screen
146
+ document.getElementById(`${screenId}-screen`)?.classList.remove('hidden');
147
+
148
+ // Update active nav button
149
+ navButtons.forEach(btn => {
150
+ if (btn.dataset.screen === screenId) {
151
+ btn.classList.add('text-indigo-600');
152
+ btn.classList.remove('text-gray-500');
153
+ } else {
154
+ btn.classList.remove('text-indigo-600');
155
+ btn.classList.add('text-gray-500');
156
+ }
157
+ });
158
+ }
159
+
160
+ // Update status badge
161
+ function updateStatusBadge() {
162
+ const pendingExams = state.exams.filter(exam =>
163
+ !state.submissions.some(sub => sub.examId === exam.id)
164
+ );
165
+
166
+ if (pendingExams.length > 0) {
167
+ statusBadge.textContent = `${pendingExams.length} pending exam${pendingExams.length > 1 ? 's' : ''}`;
168
+ statusBadge.className = 'text-xs px-2 py-1 bg-amber-100 text-amber-800 rounded-full';
169
+ } else {
170
+ statusBadge.textContent = 'No active exams';
171
+ statusBadge.className = 'text-xs px-2 py-1 bg-emerald-100 text-emerald-800 rounded-full';
172
+ }
173
+ }
174
+
175
+ // Render exam list
176
+ function renderExamList() {
177
+ examList.innerHTML = '';
178
+
179
+ state.exams.forEach(exam => {
180
+ const isSubmitted = state.submissions.some(sub => sub.examId === exam.id);
181
+
182
+ const examCard = document.createElement('div');
183
+ examCard.className = 'bg-white rounded-xl shadow-sm p-4';
184
+ examCard.innerHTML = `
185
+ <div class="flex justify-between items-start">
186
+ <div>
187
+ <h3 class="font-medium text-gray-800">${exam.subject}</h3>
188
+ <p class="text-sm text-gray-500">${new Date(exam.date).toLocaleDateString()} β€’ ${exam.duration} min</p>
189
+ </div>
190
+ ${isSubmitted ?
191
+ '<span class="text-xs px-2 py-1 bg-green-100 text-green-800 rounded-full">Submitted</span>' :
192
+ `<button data-exam-id="${exam.id}" class="take-exam-btn text-sm bg-indigo-600 text-white px-3 py-1 rounded-lg hover:bg-indigo-700">
193
+ Take Exam
194
+ </button>`
195
+ }
196
+ </div>
197
+ `;
198
+
199
+ examList.appendChild(examCard);
200
+ });
201
+
202
+ // Add event listeners to take exam buttons
203
+ document.querySelectorAll('.take-exam-btn').forEach(btn => {
204
+ btn.addEventListener('click', () => {
205
+ const examId = parseInt(btn.dataset.examId);
206
+ startExam(examId);
207
+ });
208
+ });
209
+ }
210
+
211
+ // Start an exam
212
+ function startExam(examId) {
213
+ const exam = state.exams.find(e => e.id === examId);
214
+ if (!exam) return;
215
+
216
+ state.currentExam = exam;
217
+ state.examStartTime = new Date();
218
+ state.suspiciousActivities = 0;
219
+
220
+ // Setup exam UI
221
+ examTitle.textContent = exam.subject;
222
+ examQuestions.innerHTML = '';
223
+
224
+ // Render questions
225
+ exam.questions.forEach((q, index) => {
226
+ const questionCard = document.createElement('div');
227
+ questionCard.className = 'question-card bg-white rounded-xl shadow-sm p-4';
228
+
229
+ if (q.type === 'multiple') {
230
+ questionCard.innerHTML = `
231
+ <h4 class="font-medium mb-3">${index + 1}. ${q.text}</h4>
232
+ <div class="space-y-2">
233
+ ${q.options.map((opt, i) => `
234
+ <label class="flex items-center space-x-3 cursor-pointer">
235
+ <input type="radio" name="q${q.id}" value="${i}" class="w-4 h-4 text-indigo-600">
236
+ <span>${opt}</span>
237
+ </label>
238
+ `).join('')}
239
+ </div>
240
+ `;
241
+ } else {
242
+ questionCard.innerHTML = `
243
+ <h4 class="font-medium mb-3">${index + 1}. ${q.text}</h4>
244
+ <input type="text" name="q${q.id}"
245
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg">
246
+ `;
247
+ }
248
+
249
+ examQuestions.appendChild(questionCard);
250
+ });
251
+
252
+ // Start timer
253
+ const durationInSeconds = exam.duration * 60;
254
+ let remainingTime = durationInSeconds;
255
+
256
+ updateTimerDisplay(remainingTime);
257
+
258
+ state.examTimer = setInterval(() => {
259
+ remainingTime--;
260
+ updateTimerDisplay(remainingTime);
261
+
262
+ if (remainingTime <= 0) {
263
+ clearInterval(state.examTimer);
264
+ submitExam();
265
+ }
266
+ }, 1000);
267
+
268
+ // Setup anti-cheat
269
+ setupAntiCheat();
270
+
271
+ // Show exam screen
272
+ showScreen('exam');
273
+ }
274
+
275
+ // Update timer display
276
+ function updateTimerDisplay(seconds) {
277
+ const mins = Math.floor(seconds / 60);
278
+ const secs = seconds % 60;
279
+ examTimer.textContent = `${mins}:${secs < 10 ? '0' + secs : secs}`;
280
+
281
+ if (seconds < 60) {
282
+ examTimer.classList.add('timer-urgent');
283
+ } else {
284
+ examTimer.classList.remove('timer-urgent');
285
+ }
286
+
287
+ // Update progress bar
288
+ const examDuration = state.currentExam.duration * 60;
289
+ const percent = (seconds / examDuration) * 100;
290
+ examProgress.style.width = `${percent}%`;
291
+ }
292
+ // Setup anti-cheat measures
293
+ function setupAntiCheat() {
294
+ // Add exam layout class
295
+ document.getElementById('app').classList.add('exam-layout');
296
+ // Detect tab switching
297
+ document.addEventListener('visibilitychange', () => {
298
+ if (document.visibilityState !== 'visible') {
299
+ handleSuspiciousActivity('Tab switched');
300
+ }
301
+ });
302
+
303
+ // Disable right click
304
+ document.addEventListener('contextmenu', (e) => {
305
+ e.preventDefault();
306
+ handleSuspiciousActivity('Right click attempt');
307
+ });
308
+
309
+ // Disable text selection
310
+ document.addEventListener('selectstart', (e) => {
311
+ e.preventDefault();
312
+ handleSuspiciousActivity('Text selection attempt');
313
+ });
314
+
315
+ // Disable copy/paste
316
+ document.addEventListener('copy', (e) => {
317
+ e.preventDefault();
318
+ handleSuspiciousActivity('Copy attempt');
319
+ });
320
+
321
+ document.addEventListener('paste', (e) => {
322
+ e.preventDefault();
323
+ handleSuspiciousActivity('Paste attempt');
324
+ });
325
+
326
+ // Try to access camera
327
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
328
+ navigator.mediaDevices.getUserMedia({ video: true })
329
+ .then(stream => {
330
+ // Camera access granted
331
+ console.log('Camera access granted for monitoring');
332
+ })
333
+ .catch(err => {
334
+ console.log('Camera access denied or not available');
335
+ });
336
+ }
337
+ }
338
+
339
+ // Handle suspicious activity
340
+ function handleSuspiciousActivity(reason) {
341
+ state.suspiciousActivities++;
342
+
343
+ // Show warning
344
+ const warning = document.createElement('div');
345
+ warning.className = 'warning-flash fixed top-4 left-1/2 transform -translate-x-1/2 bg-white shadow-md rounded-lg px-4 py-2 z-50';
346
+ warning.textContent = `⚠️ Suspicious activity detected: ${reason}`;
347
+ document.body.appendChild(warning);
348
+
349
+ setTimeout(() => {
350
+ warning.remove();
351
+ }, 3000);
352
+ }
353
+ // Submit exam
354
+ function submitExam() {
355
+ // Remove exam layout class
356
+ document.getElementById('app').classList.remove('exam-layout');
357
+ if (!state.currentExam) return;
358
+
359
+ clearInterval(state.examTimer);
360
+
361
+ // Create submission
362
+ const submission = {
363
+ examId: state.currentExam.id,
364
+ subject: state.currentExam.subject,
365
+ submittedAt: new Date().toISOString(),
366
+ suspiciousCount: state.suspiciousActivities
367
+ };
368
+
369
+ // Save to state and localStorage
370
+ state.submissions.push(submission);
371
+ localStorage.setItem('examGuardSubmissions', JSON.stringify(state.submissions));
372
+
373
+ // Update UI
374
+ updateStatusBadge();
375
+ renderExamList();
376
+ loadSubmissions();
377
+
378
+ // Show pending screen
379
+ showScreen('pending');
380
+ }
381
+
382
+ // Load submissions from localStorage
383
+ function loadSubmissions() {
384
+ const savedSubmissions = localStorage.getItem('examGuardSubmissions');
385
+ if (savedSubmissions) {
386
+ state.submissions = JSON.parse(savedSubmissions);
387
+ }
388
+
389
+ renderSubmissions();
390
+ }
391
+
392
+ // Render submissions list
393
+ function renderSubmissions() {
394
+ submissionList.innerHTML = '';
395
+
396
+ if (state.submissions.length === 0) {
397
+ submissionList.innerHTML = '<p class="text-gray-500 text-center py-4">No submissions yet</p>';
398
+ return;
399
+ }
400
+
401
+ state.submissions.forEach(sub => {
402
+ const subCard = document.createElement('div');
403
+ subCard.className = 'bg-white rounded-xl shadow-sm p-4';
404
+ subCard.innerHTML = `
405
+ <div class="flex justify-between items-start">
406
+ <div>
407
+ <h3 class="font-medium text-gray-800">${sub.subject}</h3>
408
+ <p class="text-sm text-gray-500">Submitted: ${new Date(sub.submittedAt).toLocaleString()}</p>
409
+ </div>
410
+ <div class="text-right">
411
+ <span class="block text-xs px-2 py-1 bg-blue-100 text-blue-800 rounded-full mb-1">Pending Review</span>
412
+ ${sub.suspiciousCount > 0 ?
413
+ `<span class="text-xs px-2 py-1 bg-red-100 text-red-800 rounded-full">
414
+ ${sub.suspiciousCount} suspicious event${sub.suspiciousCount > 1 ? 's' : ''}
415
+ </span>` :
416
+ `<span class="text-xs px-2 py-1 bg-green-100 text-green-800 rounded-full">
417
+ No issues detected
418
+ </span>`
419
+ }
420
+ </div>
421
+ </div>
422
+ `;
423
+
424
+ submissionList.appendChild(subCard);
425
+ });
426
+ }
427
+
428
+ // Setup event listeners
429
+ function setupEventListeners() {
430
+ // Login form
431
+ loginForm.addEventListener('submit', (e) => {
432
+ e.preventDefault();
433
+
434
+ const studentId = document.getElementById('student-id').value;
435
+ const password = document.getElementById('password').value;
436
+
437
+ // Mock authentication
438
+ state.currentStudent = {
439
+ id: studentId,
440
+ name: `Student ${studentId}`,
441
+ email: `student${studentId}@example.com`
442
+ };
443
+
444
+ // Save to localStorage
445
+ localStorage.setItem('examGuardStudent', JSON.stringify(state.currentStudent));
446
+
447
+ // Show app
448
+ showApp();
449
+ });
450
+
451
+ // Forgot password
452
+ forgotPasswordBtn.addEventListener('click', () => {
453
+ forgotModal.classList.remove('hidden');
454
+ });
455
+
456
+ // Cancel reset
457
+ document.getElementById('cancel-reset').addEventListener('click', () => {
458
+ forgotModal.classList.add('hidden');
459
+ });
460
+
461
+ // Submit reset
462
+ document.getElementById('submit-reset').addEventListener('click', () => {
463
+ const email = document.getElementById('reset-email').value;
464
+ if (email) {
465
+ forgotModal.classList.add('hidden');
466
+ resetConfirmation.classList.remove('hidden');
467
+ }
468
+ });
469
+
470
+ // Close confirmation
471
+ document.getElementById('close-confirmation').addEventListener('click', () => {
472
+ resetConfirmation.classList.add('hidden');
473
+ });
474
+
475
+ // Navigation buttons
476
+ navButtons.forEach(btn => {
477
+ btn.addEventListener('click', () => {
478
+ showScreen(btn.dataset.screen);
479
+ });
480
+ });
481
+
482
+ // Submit exam button
483
+ submitExamBtn.addEventListener('click', submitExam);
484
+
485
+ // Logout button
486
+ logoutBtn.addEventListener('click', () => {
487
+ localStorage.removeItem('examGuardStudent');
488
+ showLogin();
489
+ });
490
+ }
491
+
492
+ // Initialize the app when DOM is loaded
493
+ document.addEventListener('DOMContentLoaded', init);
index.html CHANGED
@@ -1,19 +1,206 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>Nova ExamGuard</title>
7
+ <link rel="stylesheet" href="styles.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <style>
12
+ :root {
13
+ --primary: #4f46e5;
14
+ --secondary: #10b981;
15
+ }
16
+ </style>
17
+ </head>
18
+ <body class="bg-gray-50 min-h-screen font-sans">
19
+ <div id="app" class="max-w-md mx-auto relative overflow-hidden exam-layout">
20
+ <!-- Login Screen -->
21
+ <section id="login-screen" class="p-6 h-screen flex flex-col justify-center">
22
+ <div class="bg-white rounded-xl shadow-md p-6">
23
+ <div class="text-center mb-8">
24
+ <h1 class="text-2xl font-bold text-gray-800 mb-2">Nova ExamGuard</h1>
25
+ <p class="text-gray-600">Secure exam platform</p>
26
+ </div>
27
+
28
+ <form id="login-form" class="space-y-4">
29
+ <div>
30
+ <label for="student-id" class="block text-sm font-medium text-gray-700 mb-1">Student ID</label>
31
+ <input type="text" id="student-id" required
32
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
33
+ </div>
34
+
35
+ <div>
36
+ <label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
37
+ <input type="password" id="password" required
38
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
39
+ </div>
40
+
41
+ <button type="submit" class="w-full bg-indigo-600 text-white py-2 px-4 rounded-lg hover:bg-indigo-700 transition">
42
+ Login
43
+ </button>
44
+
45
+ <div class="text-center">
46
+ <button type="button" id="forgot-password" class="text-sm text-indigo-600 hover:text-indigo-800">
47
+ Forgot Password?
48
+ </button>
49
+ </div>
50
+ </form>
51
+ </div>
52
+ </section>
53
+
54
+ <!-- Forgot Password Modal -->
55
+ <div id="forgot-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
56
+ <div class="bg-white rounded-xl p-6 w-full max-w-sm">
57
+ <h3 class="text-lg font-medium mb-4">Reset Password</h3>
58
+ <input type="email" id="reset-email" placeholder="Enter your email"
59
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg mb-4">
60
+ <div class="flex justify-end space-x-3">
61
+ <button id="cancel-reset" class="px-4 py-2 text-gray-600 rounded-lg">Cancel</button>
62
+ <button id="submit-reset" class="px-4 py-2 bg-indigo-600 text-white rounded-lg">Submit</button>
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ <!-- Reset Confirmation -->
68
+ <div id="reset-confirmation" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
69
+ <div class="bg-white rounded-xl p-6 w-full max-w-sm">
70
+ <h3 class="text-lg font-medium mb-4">Reset Link Sent</h3>
71
+ <p class="text-gray-600 mb-4">Password reset link has been sent to your email (simulation only).</p>
72
+ <button id="close-confirmation" class="w-full px-4 py-2 bg-indigo-600 text-white rounded-lg">OK</button>
73
+ </div>
74
+ </div>
75
+
76
+ <!-- Main App Screens (hidden initially) -->
77
+ <div id="app-screens" class="hidden h-screen flex flex-col">
78
+ <!-- Header -->
79
+ <header class="bg-white shadow-sm p-4">
80
+ <div class="flex items-center justify-between">
81
+ <div class="flex items-center space-x-3">
82
+ <img id="student-avatar" src="https://static.photos/people/120x120/1" alt="Student" class="w-10 h-10 rounded-full">
83
+ <div>
84
+ <h2 id="student-name" class="font-medium text-gray-800">Student</h2>
85
+ <span id="status-badge" class="text-xs px-2 py-1 bg-emerald-100 text-emerald-800 rounded-full">No active exam</span>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </header>
90
+
91
+ <!-- Main Content -->
92
+ <main id="main-content" class="flex-1 overflow-y-auto p-4 bg-gray-50">
93
+ <!-- Dashboard Screen -->
94
+ <section id="dashboard-screen" class="space-y-6">
95
+ <div class="bg-white rounded-xl shadow-sm p-6">
96
+ <h3 class="text-lg font-medium mb-4">Upcoming Exams</h3>
97
+ <div class="space-y-4">
98
+ <div class="border border-gray-200 rounded-lg p-4">
99
+ <h4 class="font-medium">Mathematics Final</h4>
100
+ <p class="text-sm text-gray-500">Tomorrow at 9:00 AM</p>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </section>
105
+
106
+ <!-- Exams Screen -->
107
+ <section id="exams-screen" class="hidden space-y-4">
108
+ <h2 class="text-xl font-bold text-gray-800 mb-4">Available Exams</h2>
109
+ <div id="exam-list" class="space-y-3"></div>
110
+ </section>
111
+
112
+ <!-- Take Exam Screen -->
113
+ <section id="exam-screen" class="hidden">
114
+ <div class="bg-white rounded-xl shadow-sm p-4 mb-4">
115
+ <div class="flex justify-between items-center mb-2">
116
+ <h3 id="exam-title" class="text-lg font-medium">Mathematics Final</h3>
117
+ <span id="exam-timer" class="bg-red-100 text-red-800 px-3 py-1 rounded-full text-sm">30:00</span>
118
+ </div>
119
+
120
+ <div class="relative h-2 bg-gray-200 rounded-full mb-4">
121
+ <div id="exam-progress" class="absolute top-0 left-0 h-full bg-indigo-600 rounded-full" style="width: 100%"></div>
122
+ </div>
123
+ </div>
124
+
125
+ <div id="exam-questions" class="space-y-6"></div>
126
+
127
+ <div class="fixed bottom-4 left-0 right-0 px-4">
128
+ <button id="submit-exam" class="w-full bg-indigo-600 text-white py-3 px-4 rounded-lg shadow-md hover:bg-indigo-700">
129
+ Submit Exam
130
+ </button>
131
+ </div>
132
+ </section>
133
+
134
+ <!-- Pending Screen -->
135
+ <section id="pending-screen" class="hidden">
136
+ <h2 class="text-xl font-bold text-gray-800 mb-4">Pending Submissions</h2>
137
+ <div id="submission-list" class="space-y-3"></div>
138
+ </section>
139
+
140
+ <!-- Profile Screen -->
141
+ <section id="profile-screen" class="hidden space-y-6">
142
+ <div class="bg-white rounded-xl shadow-sm p-6">
143
+ <div class="flex flex-col items-center mb-6">
144
+ <img src="https://static.photos/people/120x120/1" alt="Student" class="w-20 h-20 rounded-full mb-3">
145
+ <h3 id="profile-name" class="text-lg font-medium">John Doe</h3>
146
+ <p id="profile-id" class="text-sm text-gray-500">ID: 12345</p>
147
+ </div>
148
+
149
+ <div class="space-y-4">
150
+ <div>
151
+ <label class="block text-sm font-medium text-gray-700 mb-1">Email</label>
152
+ <input type="email" id="profile-email" value="student@example.com" class="w-full px-4 py-2 border border-gray-300 rounded-lg">
153
+ </div>
154
+
155
+ <div>
156
+ <label class="block text-sm font-medium text-gray-700 mb-1">Change Password</label>
157
+ <input type="password" placeholder="New password" class="w-full px-4 py-2 border border-gray-300 rounded-lg">
158
+ </div>
159
+
160
+ <button class="w-full bg-indigo-600 text-white py-2 px-4 rounded-lg hover:bg-indigo-700">
161
+ Save Changes
162
+ </button>
163
+ </div>
164
+ </div>
165
+
166
+ <button id="logout-btn" class="w-full bg-red-600 text-white py-2 px-4 rounded-lg hover:bg-red-700">
167
+ Logout
168
+ </button>
169
+ </section>
170
+ </main>
171
+
172
+ <!-- Bottom Navigation -->
173
+ <nav class="bg-white border-t border-gray-200 p-2">
174
+ <div class="flex justify-around">
175
+ <button data-screen="dashboard" class="nav-btn flex flex-col items-center text-indigo-600 p-2">
176
+ <i data-feather="home" class="w-5 h-5"></i>
177
+ <span class="text-xs mt-1">Home</span>
178
+ </button>
179
+ <button data-screen="exams" class="nav-btn flex flex-col items-center text-gray-500 p-2">
180
+ <i data-feather="book" class="w-5 h-5"></i>
181
+ <span class="text-xs mt-1">Exams</span>
182
+ </button>
183
+ <button data-screen="take-exam" class="nav-btn flex flex-col items-center text-gray-500 p-2">
184
+ <i data-feather="edit-3" class="w-5 h-5"></i>
185
+ <span class="text-xs mt-1">Take Exam</span>
186
+ </button>
187
+ <button data-screen="pending" class="nav-btn flex flex-col items-center text-gray-500 p-2">
188
+ <i data-feather="clock" class="w-5 h-5"></i>
189
+ <span class="text-xs mt-1">Pending</span>
190
+ </button>
191
+ <button data-screen="profile" class="nav-btn flex flex-col items-center text-gray-500 p-2">
192
+ <i data-feather="user" class="w-5 h-5"></i>
193
+ <span class="text-xs mt-1">Profile</span>
194
+ </button>
195
+ </div>
196
+ </nav>
197
+ </div>
198
+ </div>
199
+
200
+ <script src="app.js"></script>
201
+ <script>
202
+ feather.replace();
203
+ </script>
204
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
205
+ </body>
206
+ </html>
styles.css ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Base Styles */
2
+ body {
3
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
4
+ -webkit-tap-highlight-color: transparent;
5
+ }
6
+
7
+ /* Custom scrollbar */
8
+ ::-webkit-scrollbar {
9
+ width: 6px;
10
+ height: 6px;
11
+ }
12
+ ::-webkit-scrollbar-track {
13
+ background: #f1f1f1;
14
+ }
15
+ ::-webkit-scrollbar-thumb {
16
+ background: #c1c1c1;
17
+ border-radius: 3px;
18
+ }
19
+ ::-webkit-scrollbar-thumb:hover {
20
+ background: #a1a1a1;
21
+ }
22
+
23
+ /* Animation classes */
24
+ .fade-in {
25
+ animation: fadeIn 0.3s ease-in-out;
26
+ }
27
+
28
+ @keyframes fadeIn {
29
+ from { opacity: 0; }
30
+ to { opacity: 1; }
31
+ }
32
+
33
+ .slide-up {
34
+ animation: slideUp 0.3s ease-out;
35
+ }
36
+
37
+ @keyframes slideUp {
38
+ from { transform: translateY(20px); opacity: 0; }
39
+ to { transform: translateY(0); opacity: 1; }
40
+ }
41
+
42
+ /* Question styles */
43
+ .question-card {
44
+ transition: all 0.2s ease;
45
+ }
46
+
47
+ .question-card:hover {
48
+ transform: translateY(-2px);
49
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
50
+ }
51
+
52
+ /* Anti-cheat warnings */
53
+ .warning-flash {
54
+ animation: warningFlash 0.5s ease 3;
55
+ }
56
+
57
+ @keyframes warningFlash {
58
+ 0%, 100% { background-color: transparent; }
59
+ 50% { background-color: rgba(239, 68, 68, 0.2); }
60
+ }
61
+
62
+ /* Exam timer urgent state */
63
+ .timer-urgent {
64
+ animation: pulse 1s infinite;
65
+ }
66
+
67
+ @keyframes pulse {
68
+ 0%, 100% { opacity: 1; }
69
+ 50% { opacity: 0.7; }
70
+ }