SolarumAsteridion commited on
Commit
42d2284
·
verified ·
1 Parent(s): 1eac0d3

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +79 -435
index.html CHANGED
@@ -6,386 +6,69 @@
6
  <title>Neon Exam Countdown</title>
7
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
8
  <style>
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family: 'Inter', sans-serif;
17
- background: linear-gradient(135deg, #0A0A0B 0%, #141419 100%);
18
- min-height: 100vh;
19
- color: #fff;
20
- overflow-x: hidden;
21
- position: relative;
22
- }
23
-
24
- /* Animated background */
25
- .particles {
26
- position: fixed;
27
- top: 0;
28
- left: 0;
29
- width: 100%;
30
- height: 100%;
31
- overflow: hidden;
32
- z-index: 1;
33
- }
34
-
35
- .particle {
36
- position: absolute;
37
- width: 4px;
38
- height: 4px;
39
- background: radial-gradient(circle, rgba(0, 212, 255, 0.5) 0%, transparent 70%);
40
- border-radius: 50%;
41
- animation: float 20s infinite linear;
42
- }
43
-
44
- @keyframes float {
45
- from {
46
- transform: translateY(100vh) translateX(0);
47
- opacity: 0;
48
- }
49
- 10% {
50
- opacity: 1;
51
- }
52
- 90% {
53
- opacity: 1;
54
- }
55
- to {
56
- transform: translateY(-100vh) translateX(100px);
57
- opacity: 0;
58
- }
59
- }
60
-
61
- /* Main container */
62
- .container {
63
- max-width: 1200px;
64
- margin: 0 auto;
65
- padding: 2rem;
66
- position: relative;
67
- z-index: 2;
68
- }
69
-
70
- /* Header */
71
- .header {
72
- display: flex;
73
- justify-content: space-between;
74
- align-items: center;
75
- margin-bottom: 3rem;
76
- }
77
-
78
- h1 {
79
- font-size: 2.5rem;
80
- font-weight: 700;
81
- background: linear-gradient(135deg, #00D4FF 0%, #FF0080 50%, #00FF88 100%);
82
- -webkit-background-clip: text;
83
- -webkit-text-fill-color: transparent;
84
- background-clip: text;
85
- text-shadow: 0 0 30px rgba(0, 212, 255, 0.5);
86
- }
87
-
88
- /* Settings button */
89
- .settings-btn {
90
- width: 50px;
91
- height: 50px;
92
- background: rgba(255, 255, 255, 0.05);
93
- backdrop-filter: blur(10px);
94
- border: 1px solid rgba(255, 255, 255, 0.1);
95
- border-radius: 12px;
96
- display: flex;
97
- align-items: center;
98
- justify-content: center;
99
- cursor: pointer;
100
- transition: all 0.3s ease;
101
- }
102
-
103
- .settings-btn:hover {
104
- background: rgba(255, 255, 255, 0.1);
105
- border-color: #00D4FF;
106
- box-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
107
- transform: scale(1.05);
108
- }
109
-
110
- .settings-btn svg {
111
- width: 24px;
112
- height: 24px;
113
- fill: #00D4FF;
114
- }
115
-
116
- /* Exam cards grid */
117
- .exams-grid {
118
- display: grid;
119
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
120
- gap: 2rem;
121
- }
122
-
123
- /* Exam card */
124
- .exam-card {
125
- background: rgba(255, 255, 255, 0.05);
126
- backdrop-filter: blur(20px);
127
- border: 1px solid rgba(255, 255, 255, 0.1);
128
- border-radius: 20px;
129
- padding: 2rem;
130
- text-align: center;
131
- transition: all 0.3s ease;
132
- position: relative;
133
- overflow: hidden;
134
- }
135
-
136
- .exam-card::before {
137
- content: '';
138
- position: absolute;
139
- top: -2px;
140
- left: -2px;
141
- right: -2px;
142
- bottom: -2px;
143
- background: linear-gradient(45deg, #00D4FF, #FF0080, #00FF88, #00D4FF);
144
- border-radius: 20px;
145
- opacity: 0;
146
- z-index: -1;
147
- transition: opacity 0.3s ease;
148
- }
149
-
150
- .exam-card:hover {
151
- transform: translateY(-5px);
152
- box-shadow: 0 10px 30px rgba(0, 212, 255, 0.3);
153
- }
154
-
155
- .exam-card:hover::before {
156
- opacity: 0.5;
157
- }
158
-
159
- .exam-name {
160
- font-size: 1.5rem;
161
- font-weight: 600;
162
- margin-bottom: 1rem;
163
- color: #fff;
164
- }
165
-
166
- .countdown {
167
- font-size: 3rem;
168
- font-weight: 700;
169
- color: #00D4FF;
170
- text-shadow: 0 0 20px rgba(0, 212, 255, 0.8);
171
- margin-bottom: 0.5rem;
172
- }
173
-
174
- .countdown-label {
175
- font-size: 1rem;
176
- color: rgba(255, 255, 255, 0.7);
177
- }
178
-
179
- .exam-date {
180
- font-size: 0.9rem;
181
- color: rgba(255, 255, 255, 0.5);
182
- margin-top: 1rem;
183
- }
184
-
185
- .delete-btn {
186
- position: absolute;
187
- top: 1rem;
188
- right: 1rem;
189
- width: 30px;
190
- height: 30px;
191
- background: rgba(255, 0, 128, 0.2);
192
- border: 1px solid rgba(255, 0, 128, 0.5);
193
- border-radius: 8px;
194
- display: flex;
195
- align-items: center;
196
- justify-content: center;
197
- cursor: pointer;
198
- transition: all 0.3s ease;
199
- opacity: 0;
200
- }
201
-
202
- .exam-card:hover .delete-btn {
203
- opacity: 1;
204
- }
205
-
206
- .delete-btn:hover {
207
- background: rgba(255, 0, 128, 0.4);
208
- transform: scale(1.1);
209
- }
210
-
211
- /* Modal */
212
- .modal {
213
- display: none;
214
- position: fixed;
215
- top: 0;
216
- left: 0;
217
- width: 100%;
218
- height: 100%;
219
- background: rgba(0, 0, 0, 0.8);
220
- z-index: 1000;
221
- align-items: center;
222
- justify-content: center;
223
- padding: 2rem;
224
- }
225
-
226
- .modal.active {
227
- display: flex;
228
- }
229
-
230
- .modal-content {
231
- background: rgba(20, 20, 25, 0.95);
232
- backdrop-filter: blur(20px);
233
- border: 1px solid rgba(255, 255, 255, 0.1);
234
- border-radius: 20px;
235
- padding: 2rem;
236
- width: 100%;
237
- max-width: 400px;
238
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
239
- }
240
-
241
- .modal-header {
242
- display: flex;
243
- justify-content: space-between;
244
- align-items: center;
245
- margin-bottom: 2rem;
246
- }
247
-
248
- .modal-title {
249
- font-size: 1.5rem;
250
- font-weight: 600;
251
- }
252
-
253
- .close-btn {
254
- width: 30px;
255
- height: 30px;
256
- background: transparent;
257
- border: none;
258
- cursor: pointer;
259
- display: flex;
260
- align-items: center;
261
- justify-content: center;
262
- transition: transform 0.3s ease;
263
- }
264
-
265
- .close-btn:hover {
266
- transform: rotate(90deg);
267
- }
268
-
269
- .form-group {
270
- margin-bottom: 1.5rem;
271
- }
272
-
273
- label {
274
- display: block;
275
- margin-bottom: 0.5rem;
276
- color: rgba(255, 255, 255, 0.8);
277
- font-size: 0.9rem;
278
- }
279
-
280
- input {
281
- width: 100%;
282
- padding: 0.75rem 1rem;
283
- background: rgba(255, 255, 255, 0.05);
284
- border: 1px solid rgba(255, 255, 255, 0.2);
285
- border-radius: 10px;
286
- color: #fff;
287
- font-size: 1rem;
288
- transition: all 0.3s ease;
289
- }
290
-
291
- input:focus {
292
- outline: none;
293
- border-color: #00D4FF;
294
- box-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
295
- }
296
-
297
- .add-btn {
298
- width: 100%;
299
- padding: 1rem;
300
- background: linear-gradient(135deg, #00D4FF 0%, #FF0080 100%);
301
- border: none;
302
- border-radius: 10px;
303
- color: #fff;
304
- font-size: 1rem;
305
- font-weight: 600;
306
- cursor: pointer;
307
- transition: all 0.3s ease;
308
- }
309
-
310
- .add-btn:hover {
311
- transform: translateY(-2px);
312
- box-shadow: 0 5px 20px rgba(0, 212, 255, 0.5);
313
- }
314
-
315
- /* Empty state */
316
- .empty-state {
317
- text-align: center;
318
- padding: 4rem 2rem;
319
- }
320
-
321
- .empty-state p {
322
- font-size: 1.2rem;
323
- color: rgba(255, 255, 255, 0.5);
324
- margin-bottom: 2rem;
325
- }
326
-
327
- /* Responsive */
328
- @media (max-width: 768px) {
329
- h1 {
330
- font-size: 2rem;
331
- }
332
-
333
- .exams-grid {
334
- grid-template-columns: 1fr;
335
- }
336
-
337
- .countdown {
338
- font-size: 2.5rem;
339
- }
340
- }
341
  </style>
342
  </head>
343
  <body>
344
  <div class="particles" id="particles"></div>
345
  <div class="container">
346
- <div class="header">
347
- <h1>Exam Countdown</h1>
348
- <div class="settings-btn" id="openModalBtn">
349
- <svg viewBox="0 0 24 24">
350
- <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>
351
- </svg>
352
- </div>
353
- </div>
354
  <div class="exams-grid" id="examsGrid"></div>
355
- <div class="empty-state" id="emptyState" style="display: none;">
356
- <p>No exams scheduled yet. Click the + button to add your first exam!</p>
357
- </div>
358
  </div>
359
  <div class="modal" id="modal">
360
  <div class="modal-content">
361
- <div class="modal-header">
362
- <h2 class="modal-title">Add New Exam</h2>
363
- <button class="close-btn" id="closeModalBtn">
364
- <svg width="24" height="24" viewBox="0 0 24 24" fill="#fff">
365
- <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
366
- </svg>
367
- </button>
368
- </div>
369
  <form id="addExamForm">
370
- <div class="form-group">
371
- <label for="examName">Exam Name</label>
372
- <input type="text" id="examName" required placeholder="e.g., Physics Final">
373
- </div>
374
- <div class="form-group">
375
- <label for="examDate">Exam Date</label>
376
- <input type="date" id="examDate" required>
377
- </div>
378
  <button type="submit" class="add-btn">Add Exam</button>
379
  </form>
380
  </div>
381
  </div>
382
 
383
  <script type="module">
384
- // Import the functions you need from the SDKs you need
385
  import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.0/firebase-app.js";
386
  import { getDatabase, ref, onValue, push, remove, child } from "https://www.gstatic.com/firebasejs/10.7.0/firebase-database.js";
387
 
388
- // Your web app's Firebase configuration
389
  const firebaseConfig = {
390
  apiKey: "AIzaSyCBGYdGdPjYJiKsTMjVYZ9mf9F82ns7g4Q",
391
  authDomain: "pikachu-rxppbp.firebaseapp.com",
@@ -396,12 +79,10 @@
396
  appId: "1:241970333280:web:704e8930bd591c138d6505"
397
  };
398
 
399
- // Initialize Firebase
400
  const app = initializeApp(firebaseConfig);
401
  const database = getDatabase(app);
402
  const examsRef = ref(database, 'exams');
403
 
404
- // --- DOM Elements ---
405
  const openModalBtn = document.getElementById('openModalBtn');
406
  const closeModalBtn = document.getElementById('closeModalBtn');
407
  const modal = document.getElementById('modal');
@@ -409,67 +90,51 @@
409
  const examNameInput = document.getElementById('examName');
410
  const examDateInput = document.getElementById('examDate');
411
 
412
- // --- Particle Background ---
413
  function createParticles() {
414
- const particlesContainer = document.getElementById('particles');
415
- if (particlesContainer.children.length > 0) return;
416
  for (let i = 0; i < 50; i++) {
417
- const particle = document.createElement('div');
418
- particle.className = 'particle';
419
- particle.style.left = Math.random() * 100 + '%';
420
- particle.style.animationDelay = Math.random() * 20 + 's';
421
- particle.style.animationDuration = (15 + Math.random() * 10) + 's';
422
- particlesContainer.appendChild(particle);
423
  }
424
  }
425
 
426
- // --- Modal ---
427
  function openModal() { modal.classList.add('active'); }
428
  function closeModal() { modal.classList.remove('active'); addExamForm.reset(); }
429
 
430
- // --- Firebase Data Operations ---
431
  function addExam(event) {
432
  event.preventDefault();
433
- const newExam = { name: examNameInput.value, date: examDateInput.value };
434
- push(examsRef, newExam);
435
  closeModal();
436
  }
437
 
438
- function deleteExam(id) {
439
- remove(child(examsRef, id));
440
- }
441
 
442
- // --- Rendering Logic ---
443
  function calculateDaysUntil(examDate) {
444
  const today = new Date();
445
  today.setHours(0, 0, 0, 0);
446
- const exam = new Date(examDate);
447
- return Math.ceil((exam - today) / (1000 * 60 * 60 * 24));
448
  }
449
 
450
  function renderExams(examsList) {
451
  const grid = document.getElementById('examsGrid');
452
  const emptyState = document.getElementById('emptyState');
453
  grid.innerHTML = '';
454
-
455
- if (!examsList || examsList.length === 0) {
456
- emptyState.style.display = 'block';
457
- return;
458
- }
459
-
460
- emptyState.style.display = 'none';
461
- const sortedExams = [...examsList].sort((a, b) => new Date(a.date) - new Date(b.date));
462
-
463
- sortedExams.forEach(exam => {
464
  const daysUntil = calculateDaysUntil(exam.date);
465
  const card = document.createElement('div');
466
  card.className = 'exam-card';
467
- let color = daysUntil <= 3 ? '#FF0080' : (daysUntil <= 7 ? '#00FF88' : '#00D4FF');
468
 
469
  card.innerHTML = `
470
- <div class="delete-btn">
471
- <svg width="16" height="16" viewBox="0 0 24 24" fill="#FF0080"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
472
- </div>
473
  <h3 class="exam-name">${exam.name}</h3>
474
  <div class="countdown" style="color: ${color}; text-shadow: 0 0 20px ${color}80;">${daysUntil}</div>
475
  <div class="countdown-label">days remaining</div>
@@ -481,64 +146,43 @@
481
  }
482
 
483
  // ===================================================================================
484
- // ONE-TIME MANUAL IMPORT FUNCTION
485
  // TO USE: Open developer console (F12) and type `runManualImport()` then press Enter.
486
  // AFTER RUNNING ONCE, DELETE THIS ENTIRE FUNCTION.
487
  // ===================================================================================
488
  window.runManualImport = function() {
489
- const localExamsJSON = localStorage.getItem('exams');
490
- if (!localExamsJSON) {
491
- console.log("No data found in LocalStorage. Nothing to import.");
492
- return;
493
- }
494
 
495
- try {
496
- const localExams = JSON.parse(localExamsJSON);
497
- if (Array.isArray(localExams) && localExams.length > 0) {
498
- console.log(`Found ${localExams.length} exams in LocalStorage. Importing...`);
499
-
500
- const importPromises = localExams.map(exam => {
501
- const examToPush = { name: exam.name, date: exam.date };
502
- return push(examsRef, examToPush);
503
- });
504
 
505
- Promise.all(importPromises).then(() => {
506
- console.log("SUCCESS: All exams have been imported to Firebase.");
507
- alert("Import complete! Your data is now in Firebase. You can now delete the runManualImport function from your code.");
508
- });
509
- } else {
510
- console.log("LocalStorage data is empty or invalid. Nothing to import.");
511
- }
512
- } catch (error) {
513
- console.error("FAILED to parse or import from LocalStorage:", error);
514
- alert("Import failed. Check the console for errors.");
515
- }
516
  };
517
  // ===================================================================================
518
  // END OF IMPORT FUNCTION - REMOVE AFTER USE
519
  // ===================================================================================
520
 
521
- // --- INITIALIZATION & EVENT LISTENERS ---
522
  createParticles();
523
  examDateInput.min = new Date().toISOString().split('T')[0];
524
-
525
  openModalBtn.addEventListener('click', openModal);
526
  closeModalBtn.addEventListener('click', closeModal);
527
  addExamForm.addEventListener('submit', addExam);
528
- window.addEventListener('click', (event) => {
529
- if (event.target === modal) closeModal();
530
- });
531
 
532
- // Real-time listener for Firebase data
533
  onValue(examsRef, (snapshot) => {
534
- const examsData = snapshot.val();
535
- const examsList = [];
536
- for (let id in examsData) {
537
- examsList.push({ id, ...examsData[id] });
538
- }
539
- renderExams(examsList);
540
  });
541
-
542
  </script>
543
  </body>
544
  </html>
 
6
  <title>Neon Exam Countdown</title>
7
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
8
  <style>
9
+ * { margin: 0; padding: 0; box-sizing: border-box; }
10
+ body { font-family: 'Inter', sans-serif; background: linear-gradient(135deg, #0A0A0B 0%, #141419 100%); min-height: 100vh; color: #fff; overflow-x: hidden; position: relative; }
11
+ .particles { position: fixed; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; z-index: 1; }
12
+ .particle { position: absolute; width: 4px; height: 4px; background: radial-gradient(circle, rgba(0, 212, 255, 0.5) 0%, transparent 70%); border-radius: 50%; animation: float 20s infinite linear; }
13
+ @keyframes float { from { transform: translateY(100vh) translateX(0); opacity: 0; } 10% { opacity: 1; } 90% { opacity: 1; } to { transform: translateY(-100vh) translateX(100px); opacity: 0; } }
14
+ .container { max-width: 1200px; margin: 0 auto; padding: 2rem; position: relative; z-index: 2; }
15
+ .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 3rem; }
16
+ h1 { font-size: 2.5rem; font-weight: 700; background: linear-gradient(135deg, #00D4FF 0%, #FF0080 50%, #00FF88 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; text-shadow: 0 0 30px rgba(0, 212, 255, 0.5); }
17
+ .settings-btn { width: 50px; height: 50px; background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; }
18
+ .settings-btn:hover { background: rgba(255, 255, 255, 0.1); border-color: #00D4FF; box-shadow: 0 0 20px rgba(0, 212, 255, 0.5); transform: scale(1.05); }
19
+ .settings-btn svg { width: 24px; height: 24px; fill: #00D4FF; }
20
+ .exams-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 2rem; }
21
+ .exam-card { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 20px; padding: 2rem; text-align: center; transition: all 0.3s ease; position: relative; overflow: hidden; }
22
+ .exam-card::before { content: ''; position: absolute; top: -2px; left: -2px; right: -2px; bottom: -2px; background: linear-gradient(45deg, #00D4FF, #FF0080, #00FF88, #00D4FF); border-radius: 20px; opacity: 0; z-index: -1; transition: opacity 0.3s ease; }
23
+ .exam-card:hover { transform: translateY(-5px); box-shadow: 0 10px 30px rgba(0, 212, 255, 0.3); }
24
+ .exam-card:hover::before { opacity: 0.5; }
25
+ .exam-name { font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #fff; }
26
+ .countdown { font-size: 3rem; font-weight: 700; color: #00D4FF; text-shadow: 0 0 20px rgba(0, 212, 255, 0.8); margin-bottom: 0.5rem; }
27
+ .countdown-label { font-size: 1rem; color: rgba(255, 255, 255, 0.7); }
28
+ .exam-date { font-size: 0.9rem; color: rgba(255, 255, 255, 0.5); margin-top: 1rem; }
29
+ .delete-btn { position: absolute; top: 1rem; right: 1rem; width: 30px; height: 30px; background: rgba(255, 0, 128, 0.2); border: 1px solid rgba(255, 0, 128, 0.5); border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; opacity: 0; }
30
+ .exam-card:hover .delete-btn { opacity: 1; }
31
+ .delete-btn:hover { background: rgba(255, 0, 128, 0.4); transform: scale(1.1); }
32
+ .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 1000; align-items: center; justify-content: center; padding: 2rem; }
33
+ .modal.active { display: flex; }
34
+ .modal-content { background: rgba(20, 20, 25, 0.95); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 20px; padding: 2rem; width: 100%; max-width: 400px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5); }
35
+ .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; }
36
+ .modal-title { font-size: 1.5rem; font-weight: 600; }
37
+ .close-btn { width: 30px; height: 30px; background: transparent; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: transform 0.3s ease; }
38
+ .close-btn:hover { transform: rotate(90deg); }
39
+ .form-group { margin-bottom: 1.5rem; }
40
+ label { display: block; margin-bottom: 0.5rem; color: rgba(255, 255, 255, 0.8); font-size: 0.9rem; }
41
+ input { width: 100%; padding: 0.75rem 1rem; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 10px; color: #fff; font-size: 1rem; transition: all 0.3s ease; }
42
+ input:focus { outline: none; border-color: #00D4FF; box-shadow: 0 0 10px rgba(0, 212, 255, 0.5); }
43
+ .add-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #00D4FF 0%, #FF0080 100%); border: none; border-radius: 10px; color: #fff; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; }
44
+ .add-btn:hover { transform: translateY(-2px); box-shadow: 0 5px 20px rgba(0, 212, 255, 0.5); }
45
+ .empty-state { text-align: center; padding: 4rem 2rem; }
46
+ .empty-state p { font-size: 1.2rem; color: rgba(255, 255, 255, 0.5); margin-bottom: 2rem; }
47
+ @media (max-width: 768px) { h1 { font-size: 2rem; } .exams-grid { grid-template-columns: 1fr; } .countdown { font-size: 2.5rem; } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  </style>
49
  </head>
50
  <body>
51
  <div class="particles" id="particles"></div>
52
  <div class="container">
53
+ <div class="header"><h1>Exam Countdown</h1><div class="settings-btn" id="openModalBtn"><svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/></svg></div></div>
 
 
 
 
 
 
 
54
  <div class="exams-grid" id="examsGrid"></div>
55
+ <div class="empty-state" id="emptyState" style="display: none;"><p>No exams scheduled yet. Click the + button to add your first exam!</p></div>
 
 
56
  </div>
57
  <div class="modal" id="modal">
58
  <div class="modal-content">
59
+ <div class="modal-header"><h2 class="modal-title">Add New Exam</h2><button class="close-btn" id="closeModalBtn"><svg width="24" height="24" viewBox="0 0 24 24" fill="#fff"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></button></div>
 
 
 
 
 
 
 
60
  <form id="addExamForm">
61
+ <div class="form-group"><label for="examName">Exam Name</label><input type="text" id="examName" required placeholder="e.g., Physics Final"></div>
62
+ <div class="form-group"><label for="examDate">Exam Date</label><input type="date" id="examDate" required></div>
 
 
 
 
 
 
63
  <button type="submit" class="add-btn">Add Exam</button>
64
  </form>
65
  </div>
66
  </div>
67
 
68
  <script type="module">
 
69
  import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.0/firebase-app.js";
70
  import { getDatabase, ref, onValue, push, remove, child } from "https://www.gstatic.com/firebasejs/10.7.0/firebase-database.js";
71
 
 
72
  const firebaseConfig = {
73
  apiKey: "AIzaSyCBGYdGdPjYJiKsTMjVYZ9mf9F82ns7g4Q",
74
  authDomain: "pikachu-rxppbp.firebaseapp.com",
 
79
  appId: "1:241970333280:web:704e8930bd591c138d6505"
80
  };
81
 
 
82
  const app = initializeApp(firebaseConfig);
83
  const database = getDatabase(app);
84
  const examsRef = ref(database, 'exams');
85
 
 
86
  const openModalBtn = document.getElementById('openModalBtn');
87
  const closeModalBtn = document.getElementById('closeModalBtn');
88
  const modal = document.getElementById('modal');
 
90
  const examNameInput = document.getElementById('examName');
91
  const examDateInput = document.getElementById('examDate');
92
 
 
93
  function createParticles() {
94
+ const container = document.getElementById('particles');
95
+ if (container.children.length > 0) return;
96
  for (let i = 0; i < 50; i++) {
97
+ const p = document.createElement('div');
98
+ p.className = 'particle';
99
+ p.style.left = `${Math.random() * 100}%`;
100
+ p.style.animationDelay = `${Math.random() * 20}s`;
101
+ p.style.animationDuration = `${15 + Math.random() * 10}s`;
102
+ container.appendChild(p);
103
  }
104
  }
105
 
 
106
  function openModal() { modal.classList.add('active'); }
107
  function closeModal() { modal.classList.remove('active'); addExamForm.reset(); }
108
 
 
109
  function addExam(event) {
110
  event.preventDefault();
111
+ push(examsRef, { name: examNameInput.value, date: examDateInput.value });
 
112
  closeModal();
113
  }
114
 
115
+ function deleteExam(id) { remove(child(examsRef, id)); }
 
 
116
 
 
117
  function calculateDaysUntil(examDate) {
118
  const today = new Date();
119
  today.setHours(0, 0, 0, 0);
120
+ return Math.ceil((new Date(examDate) - today) / 86400000);
 
121
  }
122
 
123
  function renderExams(examsList) {
124
  const grid = document.getElementById('examsGrid');
125
  const emptyState = document.getElementById('emptyState');
126
  grid.innerHTML = '';
127
+ emptyState.style.display = (!examsList || examsList.length === 0) ? 'block' : 'none';
128
+ if (!examsList) return;
129
+
130
+ examsList.sort((a, b) => new Date(a.date) - new Date(b.date)).forEach(exam => {
 
 
 
 
 
 
131
  const daysUntil = calculateDaysUntil(exam.date);
132
  const card = document.createElement('div');
133
  card.className = 'exam-card';
134
+ const color = daysUntil <= 3 ? '#FF0080' : (daysUntil <= 7 ? '#00FF88' : '#00D4FF');
135
 
136
  card.innerHTML = `
137
+ <div class="delete-btn"><svg width="16" height="16" viewBox="0 0 24 24" fill="#FF0080"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg></div>
 
 
138
  <h3 class="exam-name">${exam.name}</h3>
139
  <div class="countdown" style="color: ${color}; text-shadow: 0 0 20px ${color}80;">${daysUntil}</div>
140
  <div class="countdown-label">days remaining</div>
 
146
  }
147
 
148
  // ===================================================================================
149
+ // START: ONE-TIME MANUAL IMPORT FUNCTION
150
  // TO USE: Open developer console (F12) and type `runManualImport()` then press Enter.
151
  // AFTER RUNNING ONCE, DELETE THIS ENTIRE FUNCTION.
152
  // ===================================================================================
153
  window.runManualImport = function() {
154
+ const dataToImport = [{"name":"NSEA","date":"2025-11-22"},{"name":"NSEP","date":"2025-11-23"},{"name":"IMO ","date":"2025-11-28"},{"name":"NSO ","date":"2025-12-11"},{"name":"IEO ","date":"2025-11-26"},{"name":"Mathematics Boards","date":"2026-02-17"},{"name":"Science Boards","date":"2026-02-25"},{"name":"ISSO ","date":"2025-11-20"},{"name":"Periodic Test 2","date":"2025-11-03"},{"name":"Pre-Boards 2 ","date":"2026-01-19"},{"name":"Pre-Boards 1","date":"2025-12-29"},{"name":"English Boards","date":"2026-02-21"},{"name":"IT Boards","date":"2026-02-27"},{"name":"Hindi Boards","date":"2026-03-02"},{"name":"Social Science Boards","date":"2026-03-07"}];
 
 
 
 
155
 
156
+ console.log(`Starting import of ${dataToImport.length} exams...`);
 
 
 
 
 
 
 
 
157
 
158
+ const importPromises = dataToImport.map(exam => push(examsRef, { name: exam.name, date: exam.date }));
159
+
160
+ Promise.all(importPromises)
161
+ .then(() => {
162
+ console.log("SUCCESS: All exams have been imported to Firebase.");
163
+ alert("Import complete! Your data is now in Firebase. You can now delete the `runManualImport` function from your code.");
164
+ })
165
+ .catch(error => {
166
+ console.error("IMPORT FAILED:", error);
167
+ alert("An error occurred during import. Check the console for details.");
168
+ });
169
  };
170
  // ===================================================================================
171
  // END OF IMPORT FUNCTION - REMOVE AFTER USE
172
  // ===================================================================================
173
 
 
174
  createParticles();
175
  examDateInput.min = new Date().toISOString().split('T')[0];
 
176
  openModalBtn.addEventListener('click', openModal);
177
  closeModalBtn.addEventListener('click', closeModal);
178
  addExamForm.addEventListener('submit', addExam);
179
+ window.addEventListener('click', e => { if (e.target === modal) closeModal(); });
 
 
180
 
 
181
  onValue(examsRef, (snapshot) => {
182
+ const data = snapshot.val();
183
+ const list = data ? Object.entries(data).map(([id, value]) => ({ id, ...value })) : [];
184
+ renderExams(list);
 
 
 
185
  });
 
186
  </script>
187
  </body>
188
  </html>