akhaliq HF Staff commited on
Commit
776dfa2
Β·
verified Β·
1 Parent(s): 33d973f

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +865 -19
index.html CHANGED
@@ -1,19 +1,865 @@
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">
6
+ <title>TaskFlow - Advanced Todo App</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ :root {
15
+ --primary: #6366f1;
16
+ --primary-dark: #4f46e5;
17
+ --secondary: #8b5cf6;
18
+ --success: #10b981;
19
+ --warning: #f59e0b;
20
+ --danger: #ef4444;
21
+ --dark: #1f2937;
22
+ --gray: #6b7280;
23
+ --light: #f3f4f6;
24
+ --white: #ffffff;
25
+ --shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
26
+ --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.05);
27
+ --radius: 12px;
28
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
29
+ }
30
+
31
+ [data-theme="dark"] {
32
+ --primary: #818cf8;
33
+ --primary-dark: #6366f1;
34
+ --secondary: #a78bfa;
35
+ --dark: #f9fafb;
36
+ --gray: #d1d5db;
37
+ --light: #374151;
38
+ --white: #1f2937;
39
+ --shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
40
+ --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.3);
41
+ }
42
+
43
+ body {
44
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
45
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
46
+ min-height: 100vh;
47
+ padding: 20px;
48
+ transition: var(--transition);
49
+ }
50
+
51
+ [data-theme="dark"] body {
52
+ background: linear-gradient(135deg, #1e3a8a 0%, #312e81 100%);
53
+ }
54
+
55
+ .container {
56
+ max-width: 900px;
57
+ margin: 0 auto;
58
+ animation: fadeInUp 0.5s ease;
59
+ }
60
+
61
+ header {
62
+ text-align: center;
63
+ margin-bottom: 30px;
64
+ color: var(--white);
65
+ }
66
+
67
+ .logo {
68
+ display: inline-flex;
69
+ align-items: center;
70
+ gap: 10px;
71
+ font-size: 2rem;
72
+ font-weight: bold;
73
+ margin-bottom: 10px;
74
+ }
75
+
76
+ .logo-icon {
77
+ width: 40px;
78
+ height: 40px;
79
+ background: var(--white);
80
+ border-radius: 10px;
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ font-size: 1.5rem;
85
+ }
86
+
87
+ .header-links {
88
+ display: flex;
89
+ justify-content: center;
90
+ gap: 20px;
91
+ margin-top: 10px;
92
+ }
93
+
94
+ .header-links a {
95
+ color: var(--white);
96
+ text-decoration: none;
97
+ font-size: 0.9rem;
98
+ opacity: 0.9;
99
+ transition: opacity 0.3s;
100
+ }
101
+
102
+ .header-links a:hover {
103
+ opacity: 1;
104
+ }
105
+
106
+ .main-card {
107
+ background: var(--white);
108
+ border-radius: var(--radius);
109
+ box-shadow: var(--shadow);
110
+ overflow: hidden;
111
+ transition: var(--transition);
112
+ }
113
+
114
+ .stats-bar {
115
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
116
+ padding: 20px;
117
+ display: grid;
118
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
119
+ gap: 20px;
120
+ color: var(--white);
121
+ }
122
+
123
+ .stat-item {
124
+ text-align: center;
125
+ }
126
+
127
+ .stat-value {
128
+ font-size: 2rem;
129
+ font-weight: bold;
130
+ margin-bottom: 5px;
131
+ }
132
+
133
+ .stat-label {
134
+ font-size: 0.9rem;
135
+ opacity: 0.9;
136
+ }
137
+
138
+ .controls {
139
+ padding: 20px;
140
+ background: var(--light);
141
+ display: flex;
142
+ gap: 15px;
143
+ flex-wrap: wrap;
144
+ align-items: center;
145
+ }
146
+
147
+ .search-box {
148
+ flex: 1;
149
+ min-width: 200px;
150
+ position: relative;
151
+ }
152
+
153
+ .search-box input {
154
+ width: 100%;
155
+ padding: 12px 40px 12px 15px;
156
+ border: 2px solid transparent;
157
+ border-radius: 8px;
158
+ background: var(--white);
159
+ font-size: 1rem;
160
+ transition: var(--transition);
161
+ }
162
+
163
+ .search-box input:focus {
164
+ outline: none;
165
+ border-color: var(--primary);
166
+ }
167
+
168
+ .search-icon {
169
+ position: absolute;
170
+ right: 15px;
171
+ top: 50%;
172
+ transform: translateY(-50%);
173
+ color: var(--gray);
174
+ }
175
+
176
+ .filter-buttons {
177
+ display: flex;
178
+ gap: 10px;
179
+ }
180
+
181
+ .filter-btn {
182
+ padding: 10px 20px;
183
+ border: none;
184
+ border-radius: 8px;
185
+ background: var(--white);
186
+ color: var(--gray);
187
+ cursor: pointer;
188
+ transition: var(--transition);
189
+ font-weight: 500;
190
+ }
191
+
192
+ .filter-btn:hover {
193
+ background: var(--primary);
194
+ color: var(--white);
195
+ }
196
+
197
+ .filter-btn.active {
198
+ background: var(--primary);
199
+ color: var(--white);
200
+ }
201
+
202
+ .theme-toggle {
203
+ width: 45px;
204
+ height: 45px;
205
+ border-radius: 50%;
206
+ border: none;
207
+ background: var(--white);
208
+ cursor: pointer;
209
+ display: flex;
210
+ align-items: center;
211
+ justify-content: center;
212
+ font-size: 1.2rem;
213
+ transition: var(--transition);
214
+ box-shadow: var(--shadow-sm);
215
+ }
216
+
217
+ .theme-toggle:hover {
218
+ transform: scale(1.1);
219
+ }
220
+
221
+ .add-todo-section {
222
+ padding: 20px;
223
+ border-bottom: 1px solid var(--light);
224
+ }
225
+
226
+ .add-todo-form {
227
+ display: flex;
228
+ gap: 15px;
229
+ flex-wrap: wrap;
230
+ }
231
+
232
+ .input-group {
233
+ flex: 1;
234
+ min-width: 200px;
235
+ }
236
+
237
+ .input-group input, .input-group select {
238
+ width: 100%;
239
+ padding: 12px 15px;
240
+ border: 2px solid var(--light);
241
+ border-radius: 8px;
242
+ font-size: 1rem;
243
+ transition: var(--transition);
244
+ background: var(--white);
245
+ color: var(--dark);
246
+ }
247
+
248
+ .input-group input:focus, .input-group select:focus {
249
+ outline: none;
250
+ border-color: var(--primary);
251
+ }
252
+
253
+ .priority-select {
254
+ min-width: 150px;
255
+ }
256
+
257
+ .add-btn {
258
+ padding: 12px 30px;
259
+ border: none;
260
+ border-radius: 8px;
261
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
262
+ color: var(--white);
263
+ font-weight: bold;
264
+ cursor: pointer;
265
+ transition: var(--transition);
266
+ font-size: 1rem;
267
+ }
268
+
269
+ .add-btn:hover {
270
+ transform: translateY(-2px);
271
+ box-shadow: var(--shadow);
272
+ }
273
+
274
+ .todo-list {
275
+ padding: 20px;
276
+ min-height: 300px;
277
+ }
278
+
279
+ .todo-item {
280
+ background: var(--light);
281
+ border-radius: 10px;
282
+ padding: 15px;
283
+ margin-bottom: 15px;
284
+ display: flex;
285
+ align-items: center;
286
+ gap: 15px;
287
+ transition: var(--transition);
288
+ cursor: move;
289
+ position: relative;
290
+ overflow: hidden;
291
+ }
292
+
293
+ .todo-item:hover {
294
+ transform: translateX(5px);
295
+ box-shadow: var(--shadow-sm);
296
+ }
297
+
298
+ .todo-item.dragging {
299
+ opacity: 0.5;
300
+ transform: rotate(5deg);
301
+ }
302
+
303
+ .todo-item.completed {
304
+ opacity: 0.7;
305
+ }
306
+
307
+ .todo-item.completed .todo-text {
308
+ text-decoration: line-through;
309
+ color: var(--gray);
310
+ }
311
+
312
+ .todo-checkbox {
313
+ width: 24px;
314
+ height: 24px;
315
+ border: 2px solid var(--primary);
316
+ border-radius: 50%;
317
+ cursor: pointer;
318
+ position: relative;
319
+ transition: var(--transition);
320
+ flex-shrink: 0;
321
+ }
322
+
323
+ .todo-checkbox:hover {
324
+ background: var(--primary);
325
+ }
326
+
327
+ .todo-checkbox.checked {
328
+ background: var(--primary);
329
+ }
330
+
331
+ .todo-checkbox.checked::after {
332
+ content: 'βœ“';
333
+ position: absolute;
334
+ top: 50%;
335
+ left: 50%;
336
+ transform: translate(-50%, -50%);
337
+ color: var(--white);
338
+ font-size: 14px;
339
+ }
340
+
341
+ .todo-content {
342
+ flex: 1;
343
+ }
344
+
345
+ .todo-text {
346
+ font-size: 1rem;
347
+ color: var(--dark);
348
+ margin-bottom: 5px;
349
+ transition: var(--transition);
350
+ }
351
+
352
+ .todo-meta {
353
+ display: flex;
354
+ gap: 15px;
355
+ font-size: 0.85rem;
356
+ color: var(--gray);
357
+ }
358
+
359
+ .todo-category {
360
+ background: var(--primary);
361
+ color: var(--white);
362
+ padding: 2px 8px;
363
+ border-radius: 12px;
364
+ font-size: 0.75rem;
365
+ }
366
+
367
+ .priority-badge {
368
+ padding: 2px 8px;
369
+ border-radius: 12px;
370
+ font-size: 0.75rem;
371
+ font-weight: bold;
372
+ }
373
+
374
+ .priority-high {
375
+ background: var(--danger);
376
+ color: var(--white);
377
+ }
378
+
379
+ .priority-medium {
380
+ background: var(--warning);
381
+ color: var(--white);
382
+ }
383
+
384
+ .priority-low {
385
+ background: var(--success);
386
+ color: var(--white);
387
+ }
388
+
389
+ .todo-actions {
390
+ display: flex;
391
+ gap: 10px;
392
+ }
393
+
394
+ .action-btn {
395
+ width: 35px;
396
+ height: 35px;
397
+ border: none;
398
+ border-radius: 8px;
399
+ background: var(--white);
400
+ color: var(--gray);
401
+ cursor: pointer;
402
+ transition: var(--transition);
403
+ display: flex;
404
+ align-items: center;
405
+ justify-content: center;
406
+ }
407
+
408
+ .action-btn:hover {
409
+ background: var(--primary);
410
+ color: var(--white);
411
+ transform: scale(1.1);
412
+ }
413
+
414
+ .empty-state {
415
+ text-align: center;
416
+ padding: 60px 20px;
417
+ color: var(--gray);
418
+ }
419
+
420
+ .empty-icon {
421
+ font-size: 4rem;
422
+ margin-bottom: 20px;
423
+ opacity: 0.5;
424
+ }
425
+
426
+ .progress-bar {
427
+ height: 6px;
428
+ background: var(--light);
429
+ border-radius: 3px;
430
+ overflow: hidden;
431
+ margin-top: 20px;
432
+ }
433
+
434
+ .progress-fill {
435
+ height: 100%;
436
+ background: linear-gradient(90deg, var(--success) 0%, var(--primary) 100%);
437
+ border-radius: 3px;
438
+ transition: width 0.5s ease;
439
+ }
440
+
441
+ @keyframes fadeInUp {
442
+ from {
443
+ opacity: 0;
444
+ transform: translateY(20px);
445
+ }
446
+ to {
447
+ opacity: 1;
448
+ transform: translateY(0);
449
+ }
450
+ }
451
+
452
+ @keyframes slideIn {
453
+ from {
454
+ transform: translateX(-100%);
455
+ }
456
+ to {
457
+ transform: translateX(0);
458
+ }
459
+ }
460
+
461
+ .toast {
462
+ position: fixed;
463
+ bottom: 30px;
464
+ right: 30px;
465
+ background: var(--dark);
466
+ color: var(--white);
467
+ padding: 15px 25px;
468
+ border-radius: 8px;
469
+ box-shadow: var(--shadow);
470
+ transform: translateX(400px);
471
+ transition: transform 0.3s ease;
472
+ z-index: 1000;
473
+ }
474
+
475
+ .toast.show {
476
+ transform: translateX(0);
477
+ }
478
+
479
+ @media (max-width: 640px) {
480
+ .controls {
481
+ flex-direction: column;
482
+ }
483
+
484
+ .filter-buttons {
485
+ width: 100%;
486
+ justify-content: center;
487
+ }
488
+
489
+ .add-todo-form {
490
+ flex-direction: column;
491
+ }
492
+
493
+ .stats-bar {
494
+ grid-template-columns: repeat(2, 1fr);
495
+ }
496
+ }
497
+ </style>
498
+ </head>
499
+ <body>
500
+ <div class="container">
501
+ <header>
502
+ <div class="logo">
503
+ <div class="logo-icon">πŸ“‹</div>
504
+ <span>TaskFlow</span>
505
+ </div>
506
+ <div class="header-links">
507
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">Built with anycoder</a>
508
+ </div>
509
+ </header>
510
+
511
+ <main class="main-card">
512
+ <div class="stats-bar">
513
+ <div class="stat-item">
514
+ <div class="stat-value" id="totalTasks">0</div>
515
+ <div class="stat-label">Total Tasks</div>
516
+ </div>
517
+ <div class="stat-item">
518
+ <div class="stat-value" id="completedTasks">0</div>
519
+ <div class="stat-label">Completed</div>
520
+ </div>
521
+ <div class="stat-item">
522
+ <div class="stat-value" id="pendingTasks">0</div>
523
+ <div class="stat-label">Pending</div>
524
+ </div>
525
+ <div class="stat-item">
526
+ <div class="stat-value" id="progressPercent">0%</div>
527
+ <div class="stat-label">Progress</div>
528
+ </div>
529
+ </div>
530
+
531
+ <div class="progress-bar">
532
+ <div class="progress-fill" id="progressBar"></div>
533
+ </div>
534
+
535
+ <div class="controls">
536
+ <div class="search-box">
537
+ <input type="text" id="searchInput" placeholder="Search tasks...">
538
+ <span class="search-icon">πŸ”</span>
539
+ </div>
540
+ <div class="filter-buttons">
541
+ <button class="filter-btn active" data-filter="all">All</button>
542
+ <button class="filter-btn" data-filter="active">Active</button>
543
+ <button class="filter-btn" data-filter="completed">Completed</button>
544
+ </div>
545
+ <button class="theme-toggle" id="themeToggle">πŸŒ™</button>
546
+ </div>
547
+
548
+ <div class="add-todo-section">
549
+ <form class="add-todo-form" id="addTodoForm">
550
+ <div class="input-group">
551
+ <input type="text" id="todoInput" placeholder="What needs to be done?" required>
552
+ </div>
553
+ <div class="input-group priority-select">
554
+ <select id="prioritySelect">
555
+ <option value="low">Low Priority</option>
556
+ <option value="medium" selected>Medium Priority</option>
557
+ <option value="high">High Priority</option>
558
+ </select>
559
+ </div>
560
+ <div class="input-group">
561
+ <select id="categorySelect">
562
+ <option value="personal">Personal</option>
563
+ <option value="work">Work</option>
564
+ <option value="shopping">Shopping</option>
565
+ <option value="health">Health</option>
566
+ <option value="other">Other</option>
567
+ </select>
568
+ </div>
569
+ <div class="input-group">
570
+ <input type="date" id="dueDateInput">
571
+ </div>
572
+ <button type="submit" class="add-btn">Add Task</button>
573
+ </form>
574
+ </div>
575
+
576
+ <div class="todo-list" id="todoList">
577
+ <div class="empty-state">
578
+ <div class="empty-icon">πŸ“</div>
579
+ <h3>No tasks yet</h3>
580
+ <p>Add your first task to get started!</p>
581
+ </div>
582
+ </div>
583
+ </main>
584
+ </div>
585
+
586
+ <div class="toast" id="toast"></div>
587
+
588
+ <script>
589
+ class TodoApp {
590
+ constructor() {
591
+ this.todos = JSON.parse(localStorage.getItem('todos')) || [];
592
+ this.currentFilter = 'all';
593
+ this.searchTerm = '';
594
+ this.draggedItem = null;
595
+ this.init();
596
+ }
597
+
598
+ init() {
599
+ this.setupEventListeners();
600
+ this.loadTheme();
601
+ this.render();
602
+ this.updateStats();
603
+ }
604
+
605
+ setupEventListeners() {
606
+ // Add todo form
607
+ document.getElementById('addTodoForm').addEventListener('submit', (e) => {
608
+ e.preventDefault();
609
+ this.addTodo();
610
+ });
611
+
612
+ // Filter buttons
613
+ document.querySelectorAll('.filter-btn').forEach(btn => {
614
+ btn.addEventListener('click', (e) => {
615
+ document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
616
+ e.target.classList.add('active');
617
+ this.currentFilter = e.target.dataset.filter;
618
+ this.render();
619
+ });
620
+ });
621
+
622
+ // Search input
623
+ document.getElementById('searchInput').addEventListener('input', (e) => {
624
+ this.searchTerm = e.target.value.toLowerCase();
625
+ this.render();
626
+ });
627
+
628
+ // Theme toggle
629
+ document.getElementById('themeToggle').addEventListener('click', () => {
630
+ this.toggleTheme();
631
+ });
632
+
633
+ // Set today as default due date
634
+ document.getElementById('dueDateInput').valueAsDate = new Date();
635
+ }
636
+
637
+ addTodo() {
638
+ const input = document.getElementById('todoInput');
639
+ const priority = document.getElementById('prioritySelect').value;
640
+ const category = document.getElementById('categorySelect').value;
641
+ const dueDate = document.getElementById('dueDateInput').value;
642
+
643
+ if (!input.value.trim()) return;
644
+
645
+ const todo = {
646
+ id: Date.now(),
647
+ text: input.value.trim(),
648
+ completed: false,
649
+ priority,
650
+ category,
651
+ dueDate,
652
+ createdAt: new Date().toISOString()
653
+ };
654
+
655
+ this.todos.unshift(todo);
656
+ this.saveTodos();
657
+ this.render();
658
+ this.updateStats();
659
+
660
+ // Reset form
661
+ input.value = '';
662
+ document.getElementById('dueDateInput').valueAsDate = new Date();
663
+
664
+ this.showToast('Task added successfully! πŸŽ‰');
665
+ }
666
+
667
+ toggleTodo(id) {
668
+ const todo = this.todos.find(t => t.id === id);
669
+ if (todo) {
670
+ todo.completed = !todo.completed;
671
+ this.saveTodos();
672
+ this.render();
673
+ this.updateStats();
674
+ this.showToast(todo.completed ? 'Task completed! βœ…' : 'Task marked as active');
675
+ }
676
+ }
677
+
678
+ deleteTodo(id) {
679
+ this.todos = this.todos.filter(t => t.id !== id);
680
+ this.saveTodos();
681
+ this.render();
682
+ this.updateStats();
683
+ this.showToast('Task deleted');
684
+ }
685
+
686
+ editTodo(id) {
687
+ const todo = this.todos.find(t => t.id === id);
688
+ if (todo) {
689
+ const newText = prompt('Edit task:', todo.text);
690
+ if (newText && newText.trim()) {
691
+ todo.text = newText.trim();
692
+ this.saveTodos();
693
+ this.render();
694
+ this.showToast('Task updated');
695
+ }
696
+ }
697
+ }
698
+
699
+ getFilteredTodos() {
700
+ let filtered = [...this.todos];
701
+
702
+ // Apply filter
703
+ if (this.currentFilter === 'active') {
704
+ filtered = filtered.filter(t => !t.completed);
705
+ } else if (this.currentFilter === 'completed') {
706
+ filtered = filtered.filter(t => t.completed);
707
+ }
708
+
709
+ // Apply search
710
+ if (this.searchTerm) {
711
+ filtered = filtered.filter(t =>
712
+ t.text.toLowerCase().includes(this.searchTerm)
713
+ );
714
+ }
715
+
716
+ return filtered;
717
+ }
718
+
719
+ render() {
720
+ const todoList = document.getElementById('todoList');
721
+ const filteredTodos = this.getFilteredTodos();
722
+
723
+ if (filteredTodos.length === 0) {
724
+ todoList.innerHTML = `
725
+ <div class="empty-state">
726
+ <div class="empty-icon">πŸ“</div>
727
+ <h3>No tasks found</h3>
728
+ <p>${this.searchTerm ? 'Try a different search term' : 'Add your first task to get started!'}</p>
729
+ </div>
730
+ `;
731
+ return;
732
+ }
733
+
734
+ todoList.innerHTML = filteredTodos.map(todo => `
735
+ <div class="todo-item ${todo.completed ? 'completed' : ''}"
736
+ draggable="true"
737
+ data-id="${todo.id}">
738
+ <div class="todo-checkbox ${todo.completed ? 'checked' : ''}"
739
+ onclick="app.toggleTodo(${todo.id})"></div>
740
+ <div class="todo-content">
741
+ <div class="todo-text">${this.escapeHtml(todo.text)}</div>
742
+ <div class="todo-meta">
743
+ <span class="todo-category">${todo.category}</span>
744
+ <span class="priority-badge priority-${todo.priority}">${todo.priority}</span>
745
+ ${todo.dueDate ? `<span>πŸ“… ${this.formatDate(todo.dueDate)}</span>` : ''}
746
+ </div>
747
+ </div>
748
+ <div class="todo-actions">
749
+ <button class="action-btn" onclick="app.editTodo(${todo.id})">✏️</button>
750
+ <button class="action-btn" onclick="app.deleteTodo(${todo.id})">πŸ—‘οΈ</button>
751
+ </div>
752
+ </div>
753
+ `).join('');
754
+
755
+ this.setupDragAndDrop();
756
+ }
757
+
758
+ setupDragAndDrop() {
759
+ const items = document.querySelectorAll('.todo-item');
760
+
761
+ items.forEach(item => {
762
+ item.addEventListener('dragstart', (e) => {
763
+ this.draggedItem = item;
764
+ item.classList.add('dragging');
765
+ });
766
+
767
+ item.addEventListener('dragend', (e) => {
768
+ item.classList.remove('dragging');
769
+ });
770
+
771
+ item.addEventListener('dragover', (e) => {
772
+ e.preventDefault();
773
+ const afterElement = this.getDragAfterElement(document.getElementById('todoList'), e.clientY);
774
+ if (afterElement == null) {
775
+ document.getElementById('todoList').appendChild(this.draggedItem);
776
+ } else {
777
+ document.getElementById('todoList').insertBefore(this.draggedItem, afterElement);
778
+ }
779
+ });
780
+ });
781
+ }
782
+
783
+ getDragAfterElement(container, y) {
784
+ const draggableElements = [...container.querySelectorAll('.todo-item:not(.dragging)')];
785
+
786
+ return draggableElements.reduce((closest, child) => {
787
+ const box = child.getBoundingClientRect();
788
+ const offset = y - box.top - box.height / 2;
789
+
790
+ if (offset < 0 && offset > closest.offset) {
791
+ return { offset: offset, element: child };
792
+ } else {
793
+ return closest;
794
+ }
795
+ }, { offset: Number.NEGATIVE_INFINITY }).element;
796
+ }
797
+
798
+ updateStats() {
799
+ const total = this.todos.length;
800
+ const completed = this.todos.filter(t => t.completed).length;
801
+ const pending = total - completed;
802
+ const progress = total > 0 ? Math.round((completed / total) * 100) : 0;
803
+
804
+ document.getElementById('totalTasks').textContent = total;
805
+ document.getElementById('completedTasks').textContent = completed;
806
+ document.getElementById('pendingTasks').textContent = pending;
807
+ document.getElementById('progressPercent').textContent = progress + '%';
808
+ document.getElementById('progressBar').style.width = progress + '%';
809
+ }
810
+
811
+ saveTodos() {
812
+ localStorage.setItem('todos', JSON.stringify(this.todos));
813
+ }
814
+
815
+ toggleTheme() {
816
+ const currentTheme = document.documentElement.getAttribute('data-theme');
817
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
818
+ document.documentElement.setAttribute('data-theme', newTheme);
819
+ localStorage.setItem('theme', newTheme);
820
+ document.getElementById('themeToggle').textContent = newTheme === 'dark' ? 'β˜€οΈ' : 'πŸŒ™';
821
+ }
822
+
823
+ loadTheme() {
824
+ const savedTheme = localStorage.getItem('theme') || 'light';
825
+ document.documentElement.setAttribute('data-theme', savedTheme);
826
+ document.getElementById('themeToggle').textContent = savedTheme === 'dark' ? 'β˜€οΈ' : 'πŸŒ™';
827
+ }
828
+
829
+ formatDate(dateString) {
830
+ const date = new Date(dateString);
831
+ const today = new Date();
832
+ const tomorrow = new Date(today);
833
+ tomorrow.setDate(tomorrow.getDate() + 1);
834
+
835
+ if (date.toDateString() === today.toDateString()) {
836
+ return 'Today';
837
+ } else if (date.toDateString() === tomorrow.toDateString()) {
838
+ return 'Tomorrow';
839
+ } else {
840
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
841
+ }
842
+ }
843
+
844
+ escapeHtml(text) {
845
+ const div = document.createElement('div');
846
+ div.textContent = text;
847
+ return div.innerHTML;
848
+ }
849
+
850
+ showToast(message) {
851
+ const toast = document.getElementById('toast');
852
+ toast.textContent = message;
853
+ toast.classList.add('show');
854
+
855
+ setTimeout(() => {
856
+ toast.classList.remove('show');
857
+ }, 3000);
858
+ }
859
+ }
860
+
861
+ // Initialize app
862
+ const app = new TodoApp();
863
+ </script>
864
+ </body>
865
+ </html>