Artificial35 commited on
Commit
e569c43
Β·
verified Β·
1 Parent(s): 4cd067a

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +490 -0
app.py ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Todo App</title>
7
+ <style>
8
+ :root {
9
+ --primary-color: #4f46e5;
10
+ --primary-hover: #4338ca;
11
+ --secondary-color: #f3f4f6;
12
+ --text-color: #1f2937;
13
+ --text-light: #6b7280;
14
+ --border-color: #e5e7eb;
15
+ --success-color: #10b981;
16
+ --danger-color: #ef4444;
17
+ --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
18
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
19
+ }
20
+
21
+ * {
22
+ margin: 0;
23
+ padding: 0;
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ body {
28
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
29
+ background-color: #f9fafb;
30
+ color: var(--text-color);
31
+ line-height: 1.6;
32
+ min-height: 100vh;
33
+ display: flex;
34
+ flex-direction: column;
35
+ }
36
+
37
+ .container {
38
+ max-width: 600px;
39
+ width: 100%;
40
+ margin: 0 auto;
41
+ padding: 20px;
42
+ flex: 1;
43
+ }
44
+
45
+ header {
46
+ text-align: center;
47
+ margin-bottom: 40px;
48
+ padding-top: 40px;
49
+ }
50
+
51
+ h1 {
52
+ font-size: 3rem;
53
+ font-weight: 800;
54
+ background: linear-gradient(135deg, var(--primary-color), #8b5cf6);
55
+ -webkit-background-clip: text;
56
+ -webkit-text-fill-color: transparent;
57
+ background-clip: text;
58
+ margin-bottom: 10px;
59
+ }
60
+
61
+ .subtitle {
62
+ color: var(--text-light);
63
+ font-size: 1.1rem;
64
+ }
65
+
66
+ .todo-input-container {
67
+ background: white;
68
+ border-radius: 12px;
69
+ padding: 20px;
70
+ box-shadow: var(--shadow);
71
+ margin-bottom: 30px;
72
+ display: flex;
73
+ gap: 12px;
74
+ align-items: center;
75
+ }
76
+
77
+ .todo-input {
78
+ flex: 1;
79
+ padding: 12px 16px;
80
+ border: 2px solid var(--border-color);
81
+ border-radius: 8px;
82
+ font-size: 1rem;
83
+ transition: all 0.3s ease;
84
+ outline: none;
85
+ }
86
+
87
+ .todo-input:focus {
88
+ border-color: var(--primary-color);
89
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
90
+ }
91
+
92
+ .add-btn {
93
+ background: var(--primary-color);
94
+ color: white;
95
+ border: none;
96
+ padding: 12px 24px;
97
+ border-radius: 8px;
98
+ font-size: 1rem;
99
+ font-weight: 600;
100
+ cursor: pointer;
101
+ transition: all 0.3s ease;
102
+ display: flex;
103
+ align-items: center;
104
+ gap: 8px;
105
+ }
106
+
107
+ .add-btn:hover {
108
+ background: var(--primary-hover);
109
+ transform: translateY(-1px);
110
+ box-shadow: var(--shadow-lg);
111
+ }
112
+
113
+ .add-btn:active {
114
+ transform: translateY(0);
115
+ }
116
+
117
+ .filters {
118
+ display: flex;
119
+ gap: 10px;
120
+ margin-bottom: 20px;
121
+ justify-content: center;
122
+ flex-wrap: wrap;
123
+ }
124
+
125
+ .filter-btn {
126
+ padding: 8px 16px;
127
+ border: 2px solid var(--border-color);
128
+ background: white;
129
+ border-radius: 20px;
130
+ cursor: pointer;
131
+ transition: all 0.3s ease;
132
+ font-weight: 500;
133
+ color: var(--text-light);
134
+ }
135
+
136
+ .filter-btn.active {
137
+ background: var(--primary-color);
138
+ color: white;
139
+ border-color: var(--primary-color);
140
+ }
141
+
142
+ .filter-btn:hover:not(.active) {
143
+ border-color: var(--primary-color);
144
+ color: var(--primary-color);
145
+ }
146
+
147
+ .todo-list {
148
+ background: white;
149
+ border-radius: 12px;
150
+ box-shadow: var(--shadow);
151
+ overflow: hidden;
152
+ }
153
+
154
+ .todo-item {
155
+ display: flex;
156
+ align-items: center;
157
+ padding: 16px 20px;
158
+ border-bottom: 1px solid var(--border-color);
159
+ transition: all 0.3s ease;
160
+ gap: 12px;
161
+ }
162
+
163
+ .todo-item:last-child {
164
+ border-bottom: none;
165
+ }
166
+
167
+ .todo-item:hover {
168
+ background: var(--secondary-color);
169
+ }
170
+
171
+ .todo-checkbox {
172
+ width: 22px;
173
+ height: 22px;
174
+ cursor: pointer;
175
+ accent-color: var(--success-color);
176
+ }
177
+
178
+ .todo-text {
179
+ flex: 1;
180
+ font-size: 1rem;
181
+ transition: all 0.3s ease;
182
+ }
183
+
184
+ .todo-item.completed .todo-text {
185
+ text-decoration: line-through;
186
+ color: var(--text-light);
187
+ }
188
+
189
+ .delete-btn {
190
+ background: none;
191
+ border: none;
192
+ color: var(--danger-color);
193
+ cursor: pointer;
194
+ padding: 6px;
195
+ border-radius: 6px;
196
+ transition: all 0.3s ease;
197
+ opacity: 0.7;
198
+ }
199
+
200
+ .delete-btn:hover {
201
+ opacity: 1;
202
+ background: rgba(239, 68, 68, 0.1);
203
+ }
204
+
205
+ .todo-stats {
206
+ display: flex;
207
+ justify-content: space-between;
208
+ align-items: center;
209
+ padding: 16px 20px;
210
+ background: var(--secondary-color);
211
+ color: var(--text-light);
212
+ font-size: 0.9rem;
213
+ }
214
+
215
+ .clear-completed {
216
+ background: none;
217
+ border: none;
218
+ color: var(--danger-color);
219
+ cursor: pointer;
220
+ font-weight: 500;
221
+ transition: all 0.3s ease;
222
+ padding: 4px 8px;
223
+ border-radius: 4px;
224
+ }
225
+
226
+ .clear-completed:hover {
227
+ background: rgba(239, 68, 68, 0.1);
228
+ }
229
+
230
+ .empty-state {
231
+ text-align: center;
232
+ padding: 60px 20px;
233
+ color: var(--text-light);
234
+ }
235
+
236
+ .empty-state-icon {
237
+ font-size: 4rem;
238
+ margin-bottom: 16px;
239
+ opacity: 0.3;
240
+ }
241
+
242
+ .empty-state-text {
243
+ font-size: 1.1rem;
244
+ }
245
+
246
+ @media (max-width: 480px) {
247
+ h1 {
248
+ font-size: 2.5rem;
249
+ }
250
+
251
+ .todo-input-container {
252
+ flex-direction: column;
253
+ }
254
+
255
+ .add-btn {
256
+ width: 100%;
257
+ justify-content: center;
258
+ }
259
+
260
+ .todo-stats {
261
+ flex-direction: column;
262
+ gap: 12px;
263
+ text-align: center;
264
+ }
265
+ }
266
+
267
+ /* Animations */
268
+ @keyframes slideIn {
269
+ from {
270
+ opacity: 0;
271
+ transform: translateY(-10px);
272
+ }
273
+ to {
274
+ opacity: 1;
275
+ transform: translateY(0);
276
+ }
277
+ }
278
+
279
+ .todo-item {
280
+ animation: slideIn 0.3s ease;
281
+ }
282
+
283
+ /* Custom checkbox styling */
284
+ .todo-checkbox {
285
+ -webkit-appearance: none;
286
+ appearance: none;
287
+ background: white;
288
+ border: 2px solid var(--border-color);
289
+ border-radius: 6px;
290
+ position: relative;
291
+ transition: all 0.3s ease;
292
+ }
293
+
294
+ .todo-checkbox:checked {
295
+ background: var(--success-color);
296
+ border-color: var(--success-color);
297
+ }
298
+
299
+ .todo-checkbox:checked::after {
300
+ content: 'βœ“';
301
+ position: absolute;
302
+ top: 50%;
303
+ left: 50%;
304
+ transform: translate(-50%, -50%);
305
+ color: white;
306
+ font-weight: bold;
307
+ }
308
+ </style>
309
+ </head>
310
+ <body>
311
+ <div class="container">
312
+ <header>
313
+ <h1>Todo App</h1>
314
+ <p class="subtitle">Stay organized and productive</p>
315
+ </header>
316
+
317
+ <main>
318
+ <div class="todo-input-container">
319
+ <input
320
+ type="text"
321
+ class="todo-input"
322
+ id="todoInput"
323
+ placeholder="What needs to be done?"
324
+ autocomplete="off"
325
+ >
326
+ <button class="add-btn" id="addBtn">
327
+ <span>+</span>
328
+ <span>Add Task</span>
329
+ </button>
330
+ </div>
331
+
332
+ <div class="filters">
333
+ <button class="filter-btn active" data-filter="all">All</button>
334
+ <button class="filter-btn" data-filter="active">Active</button>
335
+ <button class="filter-btn" data-filter="completed">Completed</button>
336
+ </div>
337
+
338
+ <div class="todo-list" id="todoList">
339
+ <div class="empty-state" id="emptyState">
340
+ <div class="empty-state-icon">πŸ“</div>
341
+ <p class="empty-state-text">No tasks yet. Add one above!</p>
342
+ </div>
343
+ </div>
344
+
345
+ <div class="todo-stats" id="todoStats" style="display: none;">
346
+ <span id="itemsLeft">0 items left</span>
347
+ <button class="clear-completed" id="clearCompleted">Clear completed</button>
348
+ </div>
349
+ </main>
350
+ </div>
351
+
352
+ <script>
353
+ class TodoApp {
354
+ constructor() {
355
+ this.todos = JSON.parse(localStorage.getItem('todos')) || [];
356
+ this.filter = 'all';
357
+ this.init();
358
+ }
359
+
360
+ init() {
361
+ this.todoInput = document.getElementById('todoInput');
362
+ this.addBtn = document.getElementById('addBtn');
363
+ this.todoList = document.getElementById('todoList');
364
+ this.emptyState = document.getElementById('emptyState');
365
+ this.todoStats = document.getElementById('todoStats');
366
+ this.itemsLeft = document.getElementById('itemsLeft');
367
+ this.clearCompleted = document.getElementById('clearCompleted');
368
+ this.filterBtns = document.querySelectorAll('.filter-btn');
369
+
370
+ this.addBtn.addEventListener('click', () => this.addTodo());
371
+ this.todoInput.addEventListener('keypress', (e) => {
372
+ if (e.key === 'Enter') this.addTodo();
373
+ });
374
+ this.clearCompleted.addEventListener('click', () => this.clearCompletedTodos());
375
+
376
+ this.filterBtns.forEach(btn => {
377
+ btn.addEventListener('click', () => {
378
+ this.filterBtns.forEach(b => b.classList.remove('active'));
379
+ btn.classList.add('active');
380
+ this.filter = btn.dataset.filter;
381
+ this.render();
382
+ });
383
+ });
384
+
385
+ this.render();
386
+ }
387
+
388
+ addTodo() {
389
+ const text = this.todoInput.value.trim();
390
+ if (!text) return;
391
+
392
+ const todo = {
393
+ id: Date.now(),
394
+ text,
395
+ completed: false,
396
+ createdAt: new Date()
397
+ };
398
+
399
+ this.todos.unshift(todo);
400
+ this.todoInput.value = '';
401
+ this.saveTodos();
402
+ this.render();
403
+ }
404
+
405
+ deleteTodo(id) {
406
+ this.todos = this.todos.filter(todo => todo.id !== id);
407
+ this.saveTodos();
408
+ this.render();
409
+ }
410
+
411
+ toggleTodo(id) {
412
+ const todo = this.todos.find(todo => todo.id === id);
413
+ if (todo) {
414
+ todo.completed = !todo.completed;
415
+ this.saveTodos();
416
+ this.render();
417
+ }
418
+ }
419
+
420
+ clearCompletedTodos() {
421
+ this.todos = this.todos.filter(todo => !todo.completed);
422
+ this.saveTodos();
423
+ this.render();
424
+ }
425
+
426
+ saveTodos() {
427
+ localStorage.setItem('todos', JSON.stringify(this.todos));
428
+ }
429
+
430
+ getFilteredTodos() {
431
+ switch (this.filter) {
432
+ case 'active':
433
+ return this.todos.filter(todo => !todo.completed);
434
+ case 'completed':
435
+ return this.todos.filter(todo => todo.completed);
436
+ default:
437
+ return this.todos;
438
+ }
439
+ }
440
+
441
+ render() {
442
+ const filteredTodos = this.getFilteredTodos();
443
+
444
+ if (filteredTodos.length === 0) {
445
+ this.todoList.innerHTML = `
446
+ <div class="empty-state">
447
+ <div class="empty-state-icon">πŸ“</div>
448
+ <p class="empty-state-text">
449
+ ${this.filter === 'all' ? 'No tasks yet. Add one above!' :
450
+ this.filter === 'active' ? 'No active tasks!' :
451
+ 'No completed tasks!'}
452
+ </p>
453
+ </div>
454
+ `;
455
+ this.todoStats.style.display = 'none';
456
+ return;
457
+ }
458
+
459
+ this.todoStats.style.display = 'flex';
460
+
461
+ const activeCount = this.todos.filter(todo => !todo.completed).length;
462
+ this.itemsLeft.textContent = `${activeCount} item${activeCount !== 1 ? 's' : ''} left`;
463
+
464
+ this.todoList.innerHTML = filteredTodos.map(todo => `
465
+ <div class="todo-item ${todo.completed ? 'completed' : ''}" data-id="${todo.id}">
466
+ <input
467
+ type="checkbox"
468
+ class="todo-checkbox"
469
+ ${todo.completed ? 'checked' : ''}
470
+ onchange="app.toggleTodo(${todo.id})"
471
+ >
472
+ <span class="todo-text">${this.escapeHtml(todo.text)}</span>
473
+ <button class="delete-btn" onclick="app.deleteTodo(${todo.id})">
474
+ βœ•
475
+ </button>
476
+ </div>
477
+ `).join('');
478
+ }
479
+
480
+ escapeHtml(text) {
481
+ const div = document.createElement('div');
482
+ div.textContent = text;
483
+ return div.innerHTML;
484
+ }
485
+ }
486
+
487
+ const app = new TodoApp();
488
+ </script>
489
+ </body>
490
+ </html>