SolarumAsteridion commited on
Commit
1e9ce14
·
verified ·
1 Parent(s): bb4a445

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +146 -547
index.html CHANGED
@@ -3,430 +3,159 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Exam Countdown</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600&family=Playfair+Display:wght@700&display=swap" rel="stylesheet">
10
 
11
  <style>
 
 
12
  * { margin: 0; padding: 0; box-sizing: border-box; }
13
 
14
- :root {
15
- --glass-bg: rgba(255, 255, 255, 0.03);
16
- --glass-border: rgba(255, 255, 255, 0.08);
17
- --text-primary: rgba(255, 255, 255, 0.95);
18
- --text-secondary: rgba(255, 255, 255, 0.6);
19
- --text-muted: rgba(255, 255, 255, 0.4);
20
- }
21
-
22
  body {
23
- font-family: 'Lexend', sans-serif;
24
- background: #000000;
 
25
  min-height: 100vh;
26
- color: var(--text-primary);
27
- overflow-x: hidden;
28
- position: relative;
29
  }
30
 
31
- /* Subtle animated gradient orbs */
32
- body::before,
33
- body::after {
34
- content: '';
35
  position: fixed;
36
- border-radius: 50%;
37
- filter: blur(150px);
38
- opacity: 0.15;
39
- animation: float 20s infinite ease-in-out;
40
- pointer-events: none;
41
  }
42
- body::before {
43
- width: 600px; height: 600px;
44
- background: radial-gradient(circle, #ffffff 0%, transparent 70%);
45
- top: -200px; right: -200px;
46
- }
47
- body::after {
48
- width: 800px; height: 800px;
49
- background: radial-gradient(circle, #ffffff 0%, transparent 70%);
50
- bottom: -300px; left: -300px;
51
- animation-delay: 10s;
52
- }
53
- @keyframes float {
54
- 0%, 100% { transform: translate(0, 0) scale(1); }
55
- 33% { transform: translate(30px, -30px) scale(1.1); }
56
- 66% { transform: translate(-20px, 20px) scale(0.9); }
57
  }
 
 
58
 
59
- /* Subtle particles */
60
- .particles { position: fixed; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; z-index: 1; pointer-events: none; }
61
- .particle { position: absolute; width: 1px; height: 1px; background: white; border-radius: 50%; opacity: 0; animation: twinkle 5s infinite; }
62
- @keyframes twinkle {
63
- 0%, 100% { opacity: 0; transform: scale(0.5); }
64
- 50% { opacity: 0.8; transform: scale(1); }
65
- }
66
 
67
- /* Full-width container */
68
- .container {
69
- width: 100%;
70
- max-width: none;
71
- margin: 0 auto;
72
- padding: 3rem 2rem;
73
- position: relative;
74
- z-index: 2;
75
- }
76
 
77
- /* Clean header */
78
- .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 5rem; padding-bottom: 2rem; position: relative; }
79
- .header::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1) 20%, rgba(255, 255, 255, 0.1) 80%, transparent); }
80
 
81
- h1 {
82
- font-family: 'Lexend', sans-serif;
83
- font-size: 2.5rem;
84
- font-weight: 300;
85
- letter-spacing: -1px;
86
- background: linear-gradient(135deg, #ffffff 0%, rgba(255, 255, 255, 0.6) 100%);
87
- -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
88
  }
 
89
 
90
- /* Glass settings button */
91
- .settings-btn { width: 50px; height: 50px; background: var(--glass-bg); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border: 1px solid var(--glass-border); border-radius: 16px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.4s cubic-bezier(0.23, 1, 0.320, 1); position: relative; overflow: hidden; }
92
- .settings-btn::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: radial-gradient(circle at center, rgba(255, 255, 255, 0.1) 0%, transparent 70%); opacity: 0; transition: opacity 0.3s ease; }
93
- .settings-btn:hover { transform: scale(1.05); background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.2); }
94
- .settings-btn:hover::before { opacity: 1; }
95
- .settings-btn svg { width: 24px; height: 24px; fill: var(--text-primary); transition: transform 0.4s ease; }
96
- .settings-btn:hover svg { transform: rotate(90deg); }
97
-
98
- /* Glass exam grid (rectangular cards, auto height) */
99
- .exams-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 2rem; grid-auto-rows: auto; align-items: start; }
100
 
101
- /* Glassmorphic exam cards */
102
- .exam-card { background: var(--glass-bg); backdrop-filter: blur(40px) saturate(180%); -webkit-backdrop-filter: blur(40px) saturate(180%); border: 1px solid var(--glass-border); border-radius: 24px; padding: 2.5rem; text-align: center; transition: all 0.4s cubic-bezier(0.23, 1, 0.320, 1); position: relative; overflow: hidden; min-height: 220px; cursor: pointer; }
103
- .exam-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2) 50%, transparent); opacity: 0; transition: opacity 0.3s ease; }
104
- .exam-card::after { content: ''; position: absolute; inset: 0; background: radial-gradient( 600px circle at var(--mouse-x) var(--mouse-y), rgba(255, 255, 255, 0.06), transparent 40% ); opacity: 0; transition: opacity 0.3s ease; pointer-events: none; }
105
- .exam-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.05); border-color: rgba(255, 255, 255, 0.15); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1); }
106
- .exam-card:hover::before, .exam-card:hover::after { opacity: 1; }
107
-
108
- .exam-name {
109
- font-size: 1.4rem;
110
- font-weight: 400;
111
- margin-bottom: 2rem;
112
- color: var(--text-primary);
113
- letter-spacing: -0.5px;
114
  }
 
115
 
116
- .countdown {
117
- font-family: 'Playfair Display', serif;
118
- font-size: 4.5rem;
119
- font-weight: 700;
120
- margin-bottom: 0.5rem;
121
- letter-spacing: -2px;
122
- line-height: 1.1;
123
- background: linear-gradient(180deg, #ffffff 0%, rgba(255, 255, 255, 0.7) 100%);
124
- -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
125
  }
 
 
126
 
127
- .countdown-label { font-size: 0.85rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 2px; font-weight: 500; }
128
- .exam-date { font-size: 0.9rem; color: var(--text-muted); margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(255, 255, 255, 0.05); }
129
-
130
- /* Syllabus indicator badge */
131
- .syllabus-badge {
132
- position: absolute;
133
- top: 1.5rem;
134
- left: 1.5rem;
135
- padding: 0.4rem 0.8rem;
136
- background: rgba(255, 255, 255, 0.08);
137
- border: 1px solid rgba(255, 255, 255, 0.1);
138
- border-radius: 8px;
139
- font-size: 0.7rem;
140
- color: var(--text-secondary);
141
- text-transform: uppercase;
142
- letter-spacing: 1px;
143
- display: flex;
144
- align-items: center;
145
- gap: 0.4rem;
146
- opacity: 0;
147
- transition: opacity 0.3s ease;
148
- }
149
- .syllabus-badge.has-syllabus {
150
- opacity: 1;
151
- background: rgba(100, 200, 100, 0.15);
152
- border-color: rgba(100, 200, 100, 0.3);
153
- color: rgba(150, 255, 150, 0.9);
154
- }
155
- .exam-card:hover .syllabus-badge {
156
- opacity: 1;
157
- }
158
- .syllabus-badge svg {
159
- width: 12px;
160
- height: 12px;
161
- fill: currentColor;
162
  }
 
163
 
164
- /* Click hint */
165
- .click-hint {
166
- position: absolute;
167
- bottom: 1rem;
168
- left: 50%;
169
- transform: translateX(-50%);
170
- font-size: 0.7rem;
171
- color: var(--text-muted);
172
- opacity: 0;
173
- transition: opacity 0.3s ease;
174
- text-transform: uppercase;
175
- letter-spacing: 1px;
176
- }
177
- .exam-card:hover .click-hint {
178
- opacity: 0.6;
179
- }
180
-
181
- /* Minimal delete button */
182
- .delete-btn { position: absolute; top: 1.5rem; right: 1.5rem; width: 32px; height: 32px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 10px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; opacity: 0; z-index: 10; }
183
- .exam-card:hover .delete-btn { opacity: 1; }
184
- .delete-btn:hover { background: rgba(255, 100, 100, 0.15); border-color: rgba(255, 100, 100, 0.3); transform: scale(1.1); }
185
- .delete-btn svg { width: 16px; height: 16px; fill: var(--text-secondary); transition: fill 0.3s ease; pointer-events: none; }
186
- .delete-btn:hover svg { fill: rgba(255, 150, 150, 1); }
187
-
188
- /* Glass modal */
189
- .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); z-index: 1000; align-items: center; justify-content: center; padding: 2rem; opacity: 0; transition: opacity 0.3s ease; }
190
  .modal.active { display: flex; opacity: 1; }
191
- .modal-content { background: rgba(255, 255, 255, 0.03); backdrop-filter: blur(40px) saturate(180%); -webkit-backdrop-filter: blur(40px) saturate(180%); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 24px; padding: 3rem; width: 100%; max-width: 450px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1); animation: modalSlide 0.4s cubic-bezier(0.23, 1, 0.320, 1); }
192
 
193
- /* Syllabus modal is wider */
194
- .modal-content.syllabus-modal-content {
195
- max-width: 650px;
196
- max-height: 85vh;
197
- overflow-y: auto;
198
- }
199
-
200
- @keyframes modalSlide { from { transform: translateY(-20px) scale(0.95); opacity: 0; } to { transform: translateY(0) scale(1); opacity: 1; } }
201
- .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2.5rem; padding-bottom: 1.5rem; border-bottom: 1px solid rgba(255, 255, 255, 0.05); }
202
- .modal-title { font-family: 'Lexend', sans-serif; font-size: 1.5rem; font-weight: 400; letter-spacing: -0.5px; }
203
- .modal-subtitle { font-size: 0.85rem; color: var(--text-muted); margin-top: 0.5rem; }
204
- .close-btn { width: 36px; height: 36px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 10px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; flex-shrink: 0; }
205
- .close-btn:hover { background: rgba(255, 255, 255, 0.08); transform: rotate(90deg); }
206
- .close-btn svg { width: 18px; height: 18px; fill: var(--text-secondary); }
207
-
208
- /* Clean form */
209
- .form-group { margin-bottom: 2rem; }
210
- .form-group label { display: block; margin-bottom: 0.75rem; font-size: 0.9rem; color: var(--text-secondary); font-weight: 500; letter-spacing: 0.5px; }
211
- .form-group input,
212
- .form-group textarea {
213
- width: 100%;
214
- padding: 1rem 1.25rem;
215
- background: rgba(255, 255, 255, 0.03);
216
- border: 1px solid rgba(255, 255, 255, 0.08);
217
- border-radius: 12px;
218
- color: var(--text-primary);
219
- font-size: 1rem;
220
- transition: all 0.3s ease;
221
- font-family: 'Lexend', sans-serif;
222
- }
223
- .form-group textarea {
224
- resize: vertical;
225
- min-height: 200px;
226
- line-height: 1.7;
227
- }
228
- .form-group input:focus,
229
- .form-group textarea:focus {
230
- outline: none;
231
- background: rgba(255, 255, 255, 0.05);
232
- border-color: rgba(255, 255, 255, 0.2);
233
- box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.02), inset 0 1px 0 rgba(255, 255, 255, 0.1);
234
- }
235
- .form-group input::placeholder,
236
- .form-group textarea::placeholder {
237
- color: var(--text-muted);
238
- }
239
-
240
- /* Syllabus tips */
241
- .syllabus-tips {
242
- background: rgba(255, 255, 255, 0.02);
243
- border: 1px solid rgba(255, 255, 255, 0.05);
244
- border-radius: 12px;
245
- padding: 1.25rem;
246
- margin-bottom: 2rem;
247
- }
248
- .syllabus-tips-title {
249
- font-size: 0.8rem;
250
- color: var(--text-secondary);
251
- text-transform: uppercase;
252
- letter-spacing: 1px;
253
- margin-bottom: 0.75rem;
254
- display: flex;
255
- align-items: center;
256
- gap: 0.5rem;
257
- }
258
- .syllabus-tips-title svg {
259
- width: 14px;
260
- height: 14px;
261
- fill: var(--text-secondary);
262
- }
263
- .syllabus-tips ul {
264
- list-style: none;
265
- font-size: 0.85rem;
266
- color: var(--text-muted);
267
- line-height: 1.8;
268
- }
269
- .syllabus-tips li::before {
270
- content: '→';
271
- margin-right: 0.5rem;
272
- color: var(--text-secondary);
273
- }
274
-
275
- /* Sleek add button */
276
- .add-btn { width: 100%; padding: 1.25rem; background: rgba(255, 255, 255, 0.95); border: none; border-radius: 12px; color: #000; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s cubic-bezier(0.23, 1, 0.320, 1); position: relative; overflow: hidden; letter-spacing: 0.5px; }
277
- .add-btn:hover { transform: translateY(-2px); background: white; box-shadow: 0 10px 30px rgba(255, 255, 255, 0.2), 0 1px 0 rgba(255, 255, 255, 0.2); }
278
- .add-btn:active { transform: translateY(0); }
279
-
280
- /* Saved indicator */
281
- .saved-indicator {
282
- display: none;
283
- align-items: center;
284
- justify-content: center;
285
- gap: 0.5rem;
286
- padding: 1rem;
287
- background: rgba(100, 200, 100, 0.1);
288
- border: 1px solid rgba(100, 200, 100, 0.2);
289
- border-radius: 12px;
290
- color: rgba(150, 255, 150, 0.9);
291
- font-size: 0.9rem;
292
- margin-top: 1rem;
293
- animation: fadeIn 0.3s ease;
294
- }
295
- .saved-indicator.show {
296
- display: flex;
297
- }
298
- .saved-indicator svg {
299
- width: 18px;
300
- height: 18px;
301
- fill: currentColor;
302
- }
303
- @keyframes fadeIn {
304
- from { opacity: 0; transform: translateY(-10px); }
305
- to { opacity: 1; transform: translateY(0); }
306
- }
307
-
308
- /* Elegant empty state */
309
- .empty-state { text-align: center; padding: 6rem 2rem; display: flex; flex-direction: column; align-items: center; justify-content: center; }
310
- .empty-icon { width: 80px; height: 80px; margin-bottom: 2rem; background: var(--glass-bg); backdrop-filter: blur(20px); border: 1px solid var(--glass-border); border-radius: 20px; display: flex; align-items: center; justify-content: center; font-size: 2rem; }
311
- .empty-state p { font-size: 1.1rem; color: var(--text-secondary); max-width: 400px; line-height: 1.6; font-weight: 300; }
312
-
313
- input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1); cursor: pointer; }
314
 
315
- /* Enhanced scrollbar */
316
- ::-webkit-scrollbar { width: 8px; }
317
- ::-webkit-scrollbar-track { background: transparent; }
318
- ::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 10px; border: 2px solid transparent; background-clip: content-box; }
319
- ::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.2); }
320
 
321
- /* Responsive tweaks */
322
- @media (max-width: 768px) {
323
- h1 { font-size: 2rem; }
324
- .countdown { font-size: 3.5rem; }
325
- .exams-grid { grid-template-columns: 1fr; }
326
- .container { padding: 2rem 1rem; }
327
- .modal-content.syllabus-modal-content { max-width: 100%; }
328
  }
329
  </style>
330
  </head>
331
  <body>
332
- <div class="particles" id="particles"></div>
 
 
 
 
333
 
334
  <div class="container">
335
- <div class="header">
336
- <h1>Exam Countdown</h1>
337
- <div class="settings-btn" id="openModalBtn">
338
- <svg viewBox="0 0 24 24">
339
- <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"/>
340
- </svg>
341
- </div>
342
- </div>
343
-
344
- <div class="exams-grid" id="examsGrid"></div>
345
 
346
- <div class="empty-state" id="emptyState" style="display: none;">
347
- <div class="empty-icon">📚</div>
348
- <p>No exams scheduled yet. Add your first exam to start tracking.</p>
349
- </div>
350
  </div>
351
-
352
- <!-- Add Exam Modal -->
353
  <div class="modal" id="modal">
354
  <div class="modal-content">
355
- <div class="modal-header">
356
- <h2 class="modal-title">Add New Exam</h2>
357
- <button class="close-btn" id="closeModalBtn">
358
- <svg viewBox="0 0 24 24">
359
- <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"/>
360
- </svg>
361
- </button>
362
- </div>
363
-
364
  <form id="addExamForm">
365
- <div class="form-group">
366
- <label for="examName">Exam Name</label>
367
- <input type="text" id="examName" required placeholder="e.g., Mathematics Final">
368
- </div>
369
- <div class="form-group">
370
- <label for="examDate">Exam Date</label>
371
- <input type="date" id="examDate" required>
372
- </div>
373
- <button type="submit" class="add-btn">Add Exam</button>
374
  </form>
375
  </div>
376
  </div>
377
 
378
- <!-- Syllabus Modal -->
379
  <div class="modal" id="syllabusModal">
380
- <div class="modal-content syllabus-modal-content">
381
- <div class="modal-header">
382
- <div>
383
- <h2 class="modal-title" id="syllabusExamName">Exam Syllabus</h2>
384
- <p class="modal-subtitle" id="syllabusExamDate"></p>
385
- </div>
386
- <button class="close-btn" id="closeSyllabusModalBtn">
387
- <svg viewBox="0 0 24 24">
388
- <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"/>
389
- </svg>
390
- </button>
391
- </div>
392
-
393
-
394
  <form id="syllabusForm">
395
- <div class="form-group">
396
- <label for="syllabusContent">Syllabus / Topics to Study</label>
397
- <textarea id="syllabusContent" rows="12" placeholder="Enter your syllabus here...
398
-
399
- Example:
400
- Chapter 1: Introduction to Calculus
401
- - Limits and Continuity (pg 1-25)
402
- - Derivatives (pg 26-50) ★
403
-
404
- Chapter 2: Integration
405
- - Definite Integrals (pg 51-75)
406
- - Applications (pg 76-100)
407
-
408
- Important Formulas:
409
- - d/dx(x^n) = nx^(n-1)
410
- - ∫x^n dx = x^(n+1)/(n+1) + C"></textarea>
411
- </div>
412
  <input type="hidden" id="syllabusExamId">
413
- <button type="submit" class="add-btn">Save Syllabus</button>
414
- <div class="saved-indicator" id="savedIndicator">
415
- <svg viewBox="0 0 24 24">
416
- <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
417
- </svg>
418
- Syllabus saved successfully!
419
- </div>
420
  </form>
421
  </div>
422
  </div>
423
 
424
  <script type="module">
425
- // Firebase SDK modules
426
  import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js";
427
  import { getDatabase, ref, onValue, push, remove, update } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-database.js";
428
 
429
- // Your web app's Firebase configuration
430
  const firebaseConfig = {
431
  apiKey: "AIzaSyCBGYdGdPjYJiKsTMjVYZ9mf9F82ns7g4Q",
432
  authDomain: "pikachu-rxppbp.firebaseapp.com",
@@ -437,217 +166,87 @@ Important Formulas:
437
  appId: "1:241970333280:web:704e8930bd591c138d6505"
438
  };
439
 
440
- // Initialize Firebase
441
  const app = initializeApp(firebaseConfig);
442
  const database = getDatabase(app);
443
  const examsRef = ref(database, 'exams');
444
-
445
- // Store exams data globally for easy access
446
  let examsData = [];
447
 
448
- // DOM Elements
449
  const elements = {
450
  modal: document.getElementById('modal'),
451
  openModalBtn: document.getElementById('openModalBtn'),
452
- closeModalBtn: document.getElementById('closeModalBtn'),
453
  addExamForm: document.getElementById('addExamForm'),
454
- examNameInput: document.getElementById('examName'),
455
- examDateInput: document.getElementById('examDate'),
456
  examsGrid: document.getElementById('examsGrid'),
457
  emptyState: document.getElementById('emptyState'),
458
- particlesContainer: document.getElementById('particles'),
459
- // Syllabus modal elements
460
  syllabusModal: document.getElementById('syllabusModal'),
461
- closeSyllabusModalBtn: document.getElementById('closeSyllabusModalBtn'),
462
  syllabusForm: document.getElementById('syllabusForm'),
463
- syllabusExamName: document.getElementById('syllabusExamName'),
464
- syllabusExamDate: document.getElementById('syllabusExamDate'),
465
  syllabusContent: document.getElementById('syllabusContent'),
466
  syllabusExamId: document.getElementById('syllabusExamId'),
 
 
467
  savedIndicator: document.getElementById('savedIndicator')
468
  };
469
 
470
- // --- Core Functions ---
471
- const openModal = () => elements.modal.classList.add('active');
472
- const closeModal = () => {
473
- elements.modal.classList.remove('active');
474
- elements.addExamForm.reset();
 
475
  };
476
 
477
- const openSyllabusModal = (examId) => {
478
- const exam = examsData.find(e => e.id === examId);
479
- if (!exam) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
 
 
 
481
  elements.syllabusExamName.textContent = exam.name;
482
- const dateObject = new Date(exam.date);
483
- elements.syllabusExamDate.textContent = dateObject.toLocaleDateString('en-US', {
484
- weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
485
- });
486
  elements.syllabusContent.value = exam.syllabus || '';
487
- elements.syllabusExamId.value = examId;
488
- elements.savedIndicator.classList.remove('show');
489
  elements.syllabusModal.classList.add('active');
490
  };
491
 
492
- const closeSyllabusModal = () => {
493
- elements.syllabusModal.classList.remove('active');
494
- elements.savedIndicator.classList.remove('show');
495
- };
496
 
497
- const saveSyllabus = (event) => {
498
- event.preventDefault();
499
- const examId = elements.syllabusExamId.value;
500
- const syllabusContent = elements.syllabusContent.value;
501
 
502
- // Update the syllabus in Firebase
503
- const examRef = ref(database, `exams/${examId}`);
504
- update(examRef, { syllabus: syllabusContent })
505
- .then(() => {
506
- elements.savedIndicator.classList.add('show');
507
- setTimeout(() => {
508
- elements.savedIndicator.classList.remove('show');
509
- }, 3000);
510
- })
511
- .catch((error) => {
512
- console.error("Error saving syllabus:", error);
513
- alert("Failed to save syllabus. Please try again.");
514
- });
515
- };
516
-
517
- const addExam = (event) => {
518
- event.preventDefault();
519
- const newExam = {
520
- name: elements.examNameInput.value,
521
- date: new Date(elements.examDateInput.value).getTime(),
522
  syllabus: ''
523
- };
524
- push(examsRef, newExam);
525
- closeModal();
526
- };
527
-
528
- const deleteExam = (id) => remove(ref(database, `exams/${id}`));
529
-
530
- // --- Rendering Logic ---
531
- const calculateCountdown = (examDate) => {
532
- const now = new Date();
533
- now.setHours(0, 0, 0, 0);
534
-
535
- const targetDate = new Date(examDate);
536
- targetDate.setHours(0, 0, 0, 0);
537
-
538
- const diff = targetDate.getTime() - now.getTime();
539
- const days = Math.ceil(diff / (1000 * 60 * 60 * 24));
540
-
541
- if (days < 0) {
542
- return { days: '✔', label: 'COMPLETED' };
543
- }
544
- return { days, label: days === 1 ? 'DAY LEFT' : 'DAYS LEFT' };
545
  };
546
 
547
- const renderExams = (exams) => {
548
- examsData = exams; // Store for later access
549
- const hasExams = exams && exams.length > 0;
550
- elements.examsGrid.style.display = hasExams ? 'grid' : 'none';
551
- elements.emptyState.style.display = hasExams ? 'none' : 'flex';
552
-
553
- if (!hasExams) return;
554
-
555
- exams.sort((a, b) => new Date(a.date) - new Date(b.date));
556
-
557
- elements.examsGrid.innerHTML = exams.map(exam => {
558
- const countdown = calculateCountdown(exam.date);
559
- const dateObject = new Date(exam.date);
560
- const hasSyllabus = exam.syllabus && exam.syllabus.trim().length > 0;
561
-
562
- return `
563
- <div class="exam-card" data-exam-id="${exam.id}">
564
- <button class="delete-btn" data-delete-id="${exam.id}">
565
- <svg viewBox="0 0 24 24">
566
- <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"/>
567
- </svg>
568
- </button>
569
- <div class="exam-name">${exam.name}</div>
570
- <div class="countdown">${countdown.days}</div>
571
- <div class="countdown-label">${countdown.label}</div>
572
- <div class="exam-date">${dateObject.toLocaleDateString('en-US', {
573
- weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
574
- })}</div>
575
- <div class="click-hint">Click to ${hasSyllabus ? 'view' : 'add'} syllabus</div>
576
- </div>`;
577
- }).join('');
578
  };
579
-
580
- // --- Visual Effects ---
581
- function createParticles() {
582
- const particleCount = 50;
583
- let particleHtml = '';
584
- for (let i = 0; i < particleCount; i++) {
585
- const delay = Math.random() * 5;
586
- const duration = 5 + Math.random() * 5;
587
- const style = `
588
- left: ${Math.random() * 100}%;
589
- top: ${Math.random() * 100}%;
590
- animation-delay: ${delay}s;
591
- animation-duration: ${duration}s;
592
- `;
593
- particleHtml += `<div class="particle" style="${style}"></div>`;
594
- }
595
- elements.particlesContainer.innerHTML = particleHtml;
596
- }
597
-
598
- document.addEventListener('mousemove', (e) => {
599
- document.querySelectorAll('.exam-card').forEach(card => {
600
- const rect = card.getBoundingClientRect();
601
- const x = e.clientX - rect.left;
602
- const y = e.clientY - rect.top;
603
- card.style.setProperty('--mouse-x', `${x}px`);
604
- card.style.setProperty('--mouse-y', `${y}px`);
605
- });
606
- });
607
-
608
- // --- App Initialization & Event Listeners ---
609
- createParticles();
610
- elements.examDateInput.min = new Date().toISOString().split('T')[0];
611
-
612
- // Modal Listeners
613
- elements.openModalBtn.addEventListener('click', openModal);
614
- elements.closeModalBtn.addEventListener('click', closeModal);
615
- elements.addExamForm.addEventListener('submit', addExam);
616
- window.addEventListener('click', e => {
617
- if (e.target === elements.modal) closeModal();
618
- if (e.target === elements.syllabusModal) closeSyllabusModal();
619
- });
620
-
621
- // Syllabus Modal Listeners
622
- elements.closeSyllabusModalBtn.addEventListener('click', closeSyllabusModal);
623
- elements.syllabusForm.addEventListener('submit', saveSyllabus);
624
-
625
- // Exam card click listener (using event delegation)
626
- elements.examsGrid.addEventListener('click', (event) => {
627
- const deleteBtn = event.target.closest('.delete-btn');
628
- if (deleteBtn) {
629
- event.stopPropagation();
630
- deleteExam(deleteBtn.dataset.deleteId);
631
- return;
632
- }
633
-
634
- const examCard = event.target.closest('.exam-card');
635
- if (examCard) {
636
- const examId = examCard.dataset.examId;
637
- openSyllabusModal(examId);
638
- }
639
- });
640
-
641
- // Firebase real-time data listener
642
- onValue(examsRef, (snapshot) => {
643
- const data = snapshot.val();
644
- const examList = data ? Object.entries(data).map(([id, value]) => ({ id, ...value })) : [];
645
- renderExams(examList);
646
- }, (error) => {
647
- console.error("Firebase read failed:", error);
648
- alert("Could not connect to the database. Please check your configuration.");
649
- });
650
-
651
  </script>
652
  </body>
653
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Nebula Exam Countdown</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@200;400;600&display=swap" rel="stylesheet">
10
 
11
  <style>
12
+ @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@200;600&display=swap');
13
+
14
  * { margin: 0; padding: 0; box-sizing: border-box; }
15
 
 
 
 
 
 
 
 
 
16
  body {
17
+ background: #020012;
18
+ color: #fff;
19
+ font-family: 'Outfit', sans-serif;
20
  min-height: 100vh;
21
+ overflow-x: hidden;
 
 
22
  }
23
 
24
+ /* Nebula Background Effects */
25
+ .nebula-bg {
 
 
26
  position: fixed;
27
+ top: 0; left: 0; width: 100%; height: 100%;
28
+ z-index: -1;
29
+ overflow: hidden;
 
 
30
  }
31
+ .bg-glow {
32
+ position: absolute;
33
+ width: 600px; height: 600px;
34
+ background: radial-gradient(circle, rgba(100,50,255,0.2) 0%, rgba(0,212,255,0) 70%);
35
+ filter: blur(60px);
36
+ animation: pulse 12s infinite alternate;
 
 
 
 
 
 
 
 
 
37
  }
38
+ .bg-glow:nth-child(2) { top: -200px; right: -100px; background: radial-gradient(circle, rgba(0,212,255,0.15) 0%, rgba(0,212,255,0) 70%); animation-delay: -4s; }
39
+ .bg-glow:nth-child(3) { bottom: -200px; left: -100px; background: radial-gradient(circle, rgba(255,50,150,0.1) 0%, rgba(0,212,255,0) 70%); animation-delay: -8s; }
40
 
41
+ @keyframes pulse { 0% { transform: scale(1) translate(0, 0); opacity: 0.5; } 100% { transform: scale(1.3) translate(50px, 50px); opacity: 0.8; } }
 
 
 
 
 
 
42
 
43
+ .container { width: 100%; max-width: 1200px; margin: 0 auto; padding: 4rem 2rem; position: relative; z-index: 1; }
 
 
 
 
 
 
 
 
44
 
45
+ header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4rem; }
46
+ h1 { font-weight: 200; letter-spacing: 0.3em; text-transform: uppercase; font-size: 1.5rem; color: #00d4ff; }
 
47
 
48
+ /* Add Button */
49
+ .add-trigger {
50
+ background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1);
51
+ color: white; padding: 12px 24px; border-radius: 50px; cursor: pointer; backdrop-filter: blur(10px);
52
+ transition: all 0.3s ease; font-family: 'Outfit'; font-size: 0.9rem; letter-spacing: 0.1em;
 
 
53
  }
54
+ .add-trigger:hover { background: rgba(0, 212, 255, 0.2); border-color: #00d4ff; transform: translateY(-3px); }
55
 
56
+ /* Grid */
57
+ .exams-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); gap: 3rem; }
 
 
 
 
 
 
 
 
58
 
59
+ /* Nebula Card */
60
+ .glass-card {
61
+ background: rgba(255, 255, 255, 0.02);
62
+ backdrop-filter: blur(20px);
63
+ border: 1px solid rgba(255, 255, 255, 0.08);
64
+ border-radius: 40px;
65
+ padding: 40px;
66
+ position: relative;
67
+ box-shadow: 0 8px 32px 0 rgba(0,0,0,0.4);
68
+ transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
69
+ text-align: center;
70
+ cursor: pointer;
 
71
  }
72
+ .glass-card:hover { transform: translateY(-10px) rotateX(5deg); background: rgba(255, 255, 255, 0.05); border-color: rgba(0,212,255,0.3); }
73
 
74
+ .subject { font-size: 0.8rem; letter-spacing: 0.4em; color: #00d4ff; font-weight: 200; margin-bottom: 15px; text-transform: uppercase; }
75
+ .counter {
76
+ font-size: 6rem; font-weight: 600; line-height: 1;
77
+ background: linear-gradient(180deg, #fff 30%, rgba(255,255,255,0.2) 100%);
78
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
79
+ filter: drop-shadow(0 0 20px rgba(0,212,255,0.2));
 
 
 
80
  }
81
+ .sub-text { font-size: 1rem; opacity: 0.5; margin-bottom: 25px; font-weight: 200; letter-spacing: 0.2em; }
82
+ .footer-date { font-size: 0.75rem; letter-spacing: 0.1em; color: rgba(255,255,255,0.3); }
83
 
84
+ /* Delete Button */
85
+ .delete-btn {
86
+ position: absolute; top: 20px; right: 25px; background: none; border: none; color: rgba(255,255,255,0.2);
87
+ cursor: pointer; font-size: 1.2rem; transition: color 0.3s; z-index: 5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
+ .delete-btn:hover { color: #ff4b2b; }
90
 
91
+ /* Modal Styling */
92
+ .modal { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.8); backdrop-filter: blur(15px); z-index: 100; align-items: center; justify-content: center; padding: 20px; opacity: 0; transition: opacity 0.3s ease; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  .modal.active { display: flex; opacity: 1; }
94
+ .modal-content { background: #0a0a1a; border: 1px solid rgba(0,212,255,0.2); padding: 40px; border-radius: 30px; width: 100%; max-width: 500px; box-shadow: 0 0 50px rgba(0,212,255,0.1); }
95
 
96
+ input, textarea { width: 100%; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); padding: 15px; border-radius: 12px; color: white; font-family: 'Outfit'; margin-top: 10px; margin-bottom: 20px; outline: none; }
97
+ input:focus { border-color: #00d4ff; }
98
+ label { font-size: 0.8rem; color: #00d4ff; letter-spacing: 0.1em; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ .save-btn { width: 100%; padding: 15px; border-radius: 12px; border: none; background: #00d4ff; color: #020012; font-weight: 600; cursor: pointer; transition: 0.3s; text-transform: uppercase; letter-spacing: 0.1em; }
101
+ .save-btn:hover { background: #fff; transform: scale(1.02); }
 
 
 
102
 
103
+ .empty-state { text-align: center; padding: 100px; opacity: 0.5; font-weight: 200; letter-spacing: 0.2em; }
104
+
105
+ @media (max-width: 600px) {
106
+ .exams-grid { grid-template-columns: 1fr; }
107
+ .counter { font-size: 4.5rem; }
 
 
108
  }
109
  </style>
110
  </head>
111
  <body>
112
+ <div class="nebula-bg">
113
+ <div class="bg-glow"></div>
114
+ <div class="bg-glow"></div>
115
+ <div class="bg-glow"></div>
116
+ </div>
117
 
118
  <div class="container">
119
+ <header>
120
+ <h1>Exams</h1>
121
+ <button class="add-trigger" id="openModalBtn">+ ADD EXAM</button>
122
+ </header>
 
 
 
 
 
 
123
 
124
+ <div id="examsGrid" class="exams-grid"></div>
125
+ <div id="emptyState" class="empty-state">NO EXAMS SCHEDULED</div>
 
 
126
  </div>
127
+
128
+ <!-- Modals (Add & Syllabus) same structure as original but with new CSS -->
129
  <div class="modal" id="modal">
130
  <div class="modal-content">
131
+ <h2 style="font-weight: 200; margin-bottom: 20px; letter-spacing: 2px;">NEW SCHEDULE</h2>
 
 
 
 
 
 
 
 
132
  <form id="addExamForm">
133
+ <label>EXAM NAME</label>
134
+ <input type="text" id="examName" required placeholder="e.g. Physics Midterm">
135
+ <label>DATE</label>
136
+ <input type="date" id="examDate" required>
137
+ <button type="submit" class="save-btn">Initialize</button>
 
 
 
 
138
  </form>
139
  </div>
140
  </div>
141
 
 
142
  <div class="modal" id="syllabusModal">
143
+ <div class="modal-content" style="max-width: 700px;">
144
+ <h2 id="syllabusExamName" style="font-weight: 200; color: #00d4ff; letter-spacing: 2px;"></h2>
145
+ <p id="syllabusExamDate" style="font-size: 0.8rem; opacity: 0.5; margin-bottom: 20px;"></p>
 
 
 
 
 
 
 
 
 
 
 
146
  <form id="syllabusForm">
147
+ <textarea id="syllabusContent" rows="10" placeholder="Enter study topics..."></textarea>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  <input type="hidden" id="syllabusExamId">
149
+ <button type="submit" class="save-btn">Update Syllabus</button>
150
+ <div id="savedIndicator" style="display:none; color: #00d4ff; text-align: center; margin-top: 10px; font-size: 0.8rem;">Saved successfully</div>
 
 
 
 
 
151
  </form>
152
  </div>
153
  </div>
154
 
155
  <script type="module">
 
156
  import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js";
157
  import { getDatabase, ref, onValue, push, remove, update } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-database.js";
158
 
 
159
  const firebaseConfig = {
160
  apiKey: "AIzaSyCBGYdGdPjYJiKsTMjVYZ9mf9F82ns7g4Q",
161
  authDomain: "pikachu-rxppbp.firebaseapp.com",
 
166
  appId: "1:241970333280:web:704e8930bd591c138d6505"
167
  };
168
 
 
169
  const app = initializeApp(firebaseConfig);
170
  const database = getDatabase(app);
171
  const examsRef = ref(database, 'exams');
 
 
172
  let examsData = [];
173
 
 
174
  const elements = {
175
  modal: document.getElementById('modal'),
176
  openModalBtn: document.getElementById('openModalBtn'),
 
177
  addExamForm: document.getElementById('addExamForm'),
 
 
178
  examsGrid: document.getElementById('examsGrid'),
179
  emptyState: document.getElementById('emptyState'),
 
 
180
  syllabusModal: document.getElementById('syllabusModal'),
 
181
  syllabusForm: document.getElementById('syllabusForm'),
 
 
182
  syllabusContent: document.getElementById('syllabusContent'),
183
  syllabusExamId: document.getElementById('syllabusExamId'),
184
+ syllabusExamName: document.getElementById('syllabusExamName'),
185
+ syllabusExamDate: document.getElementById('syllabusExamDate'),
186
  savedIndicator: document.getElementById('savedIndicator')
187
  };
188
 
189
+ const calculateCountdown = (examDate) => {
190
+ const now = new Date(); now.setHours(0,0,0,0);
191
+ const target = new Date(examDate); target.setHours(0,0,0,0);
192
+ const diff = target.getTime() - now.getTime();
193
+ const days = Math.ceil(diff / (1000 * 60 * 60 * 24));
194
+ return days < 0 ? { days: '✔', label: 'COMPLETE' } : { days, label: 'DAYS REMAINING' };
195
  };
196
 
197
+ onValue(examsRef, (snapshot) => {
198
+ const data = snapshot.val();
199
+ examsData = data ? Object.entries(data).map(([id, val]) => ({ id, ...val })) : [];
200
+ renderExams();
201
+ });
202
+
203
+ function renderExams() {
204
+ elements.emptyState.style.display = examsData.length ? 'none' : 'block';
205
+ elements.examsGrid.innerHTML = examsData.sort((a,b) => a.date - b.date).map(exam => {
206
+ const count = calculateCountdown(exam.date);
207
+ const dateStr = new Date(exam.date).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
208
+ return `
209
+ <div class="glass-card" onclick="openSyllabus('${exam.id}')">
210
+ <button class="delete-btn" onclick="event.stopPropagation(); deleteExam('${exam.id}')">✕</button>
211
+ <div class="subject">${exam.name}</div>
212
+ <div class="counter">${count.days}</div>
213
+ <div class="sub-text">${count.label}</div>
214
+ <div class="footer-date">${dateStr}</div>
215
+ </div>`;
216
+ }).join('');
217
+ }
218
 
219
+ window.openSyllabus = (id) => {
220
+ const exam = examsData.find(e => e.id === id);
221
  elements.syllabusExamName.textContent = exam.name;
222
+ elements.syllabusExamDate.textContent = new Date(exam.date).toDateString();
 
 
 
223
  elements.syllabusContent.value = exam.syllabus || '';
224
+ elements.syllabusExamId.value = id;
 
225
  elements.syllabusModal.classList.add('active');
226
  };
227
 
228
+ window.deleteExam = (id) => { if(confirm('Remove this exam?')) remove(ref(database, `exams/${id}`)); };
 
 
 
229
 
230
+ elements.openModalBtn.onclick = () => elements.modal.classList.add('active');
231
+ window.onclick = (e) => { if(e.target.classList.contains('modal')) { e.target.classList.remove('active'); elements.savedIndicator.style.display='none'; }};
 
 
232
 
233
+ elements.addExamForm.onsubmit = (e) => {
234
+ e.preventDefault();
235
+ push(examsRef, {
236
+ name: document.getElementById('examName').value,
237
+ date: new Date(document.getElementById('examDate').value).getTime(),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  syllabus: ''
239
+ });
240
+ elements.modal.classList.remove('active');
241
+ elements.addExamForm.reset();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  };
243
 
244
+ elements.syllabusForm.onsubmit = (e) => {
245
+ e.preventDefault();
246
+ update(ref(database, `exams/${elements.syllabusExamId.value}`), { syllabus: elements.syllabusContent.value });
247
+ elements.savedIndicator.style.display = 'block';
248
+ setTimeout(() => elements.syllabusModal.classList.remove('active'), 1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  </script>
251
  </body>
252
  </html>