jphermans commited on
Commit
2f5db26
·
verified ·
1 Parent(s): 52edcec

add also an option for due date/time - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +353 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Todo
3
- emoji: 👀
4
- colorFrom: purple
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: todo
3
+ emoji: 🐳
4
+ colorFrom: green
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,353 @@
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>Modern Todo App</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ /* Custom scrollbar */
11
+ .custom-scrollbar::-webkit-scrollbar {
12
+ width: 6px;
13
+ }
14
+ .custom-scrollbar::-webkit-scrollbar-track {
15
+ background: #f1f1f1;
16
+ border-radius: 10px;
17
+ }
18
+ .custom-scrollbar::-webkit-scrollbar-thumb {
19
+ background: #888;
20
+ border-radius: 10px;
21
+ }
22
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
23
+ background: #555;
24
+ }
25
+
26
+ /* Checkbox animation */
27
+ .task-checkbox:checked ~ .task-text {
28
+ text-decoration: line-through;
29
+ color: #9ca3af;
30
+ }
31
+
32
+ /* Floating action button animation */
33
+ @keyframes pulse {
34
+ 0% { transform: scale(1); }
35
+ 50% { transform: scale(1.1); }
36
+ 100% { transform: scale(1); }
37
+ }
38
+ .pulse-animation {
39
+ animation: pulse 2s infinite;
40
+ }
41
+
42
+ /* Date input styling */
43
+ input[type="datetime-local"]::-webkit-calendar-picker-indicator {
44
+ filter: invert(0.5);
45
+ cursor: pointer;
46
+ }
47
+ input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover {
48
+ filter: invert(0.3);
49
+ }
50
+
51
+ /* Task item animation */
52
+ @keyframes fadeIn {
53
+ from { opacity: 0; transform: translateY(10px); }
54
+ to { opacity: 1; transform: translateY(0); }
55
+ }
56
+ .task-item {
57
+ animation: fadeIn 0.3s ease-out forwards;
58
+ }
59
+ </style>
60
+ </head>
61
+ <body class="bg-gradient-to-br from-indigo-50 to-purple-50 min-h-screen">
62
+ <div class="container mx-auto px-4 py-8 max-w-3xl">
63
+ <!-- Header -->
64
+ <header class="flex flex-col items-center mb-8">
65
+ <h1 class="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-indigo-600 to-purple-600 mb-2">
66
+ TaskFlow
67
+ </h1>
68
+ <p class="text-gray-500">Organize your day, one task at a time</p>
69
+
70
+ <!-- Stats -->
71
+ <div class="flex gap-4 mt-4">
72
+ <div class="bg-white rounded-lg shadow p-3 text-center min-w-[100px]">
73
+ <div class="text-sm text-gray-500">Total</div>
74
+ <div id="total-count" class="text-xl font-bold text-indigo-600">0</div>
75
+ </div>
76
+ <div class="bg-white rounded-lg shadow p-3 text-center min-w-[100px]">
77
+ <div class="text-sm text-gray-500">Completed</div>
78
+ <div id="completed-count" class="text-xl font-bold text-green-500">0</div>
79
+ </div>
80
+ <div class="bg-white rounded-lg shadow p-3 text-center min-w-[100px]">
81
+ <div class="text-sm text-gray-500">Pending</div>
82
+ <div id="pending-count" class="text-xl font-bold text-yellow-500">0</div>
83
+ </div>
84
+ </div>
85
+ </header>
86
+
87
+ <!-- Main Content -->
88
+ <main class="bg-white rounded-xl shadow-lg overflow-hidden">
89
+ <!-- Input Section -->
90
+ <div class="p-4 border-b border-gray-100">
91
+ <div class="flex gap-2">
92
+ <input
93
+ type="text"
94
+ id="task-input"
95
+ placeholder="What needs to be done?"
96
+ class="flex-1 px-4 py-3 rounded-lg border border-gray-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
97
+ >
98
+ <input
99
+ type="datetime-local"
100
+ id="task-due"
101
+ class="px-4 py-3 rounded-lg border border-gray-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
102
+ >
103
+ <button
104
+ id="add-task-btn"
105
+ class="bg-indigo-600 hover:bg-indigo-700 text-white px-6 py-3 rounded-lg transition-all duration-200 flex items-center gap-2"
106
+ >
107
+ <i class="fas fa-plus"></i>
108
+ <span class="hidden sm:inline">Add Task</span>
109
+ </button>
110
+ </div>
111
+
112
+ <!-- Filter Buttons -->
113
+ <div class="flex justify-center mt-4 gap-2">
114
+ <button data-filter="all" class="filter-btn active px-3 py-1 rounded-full text-sm bg-indigo-100 text-indigo-700">All</button>
115
+ <button data-filter="active" class="filter-btn px-3 py-1 rounded-full text-sm hover:bg-gray-100">Active</button>
116
+ <button data-filter="completed" class="filter-btn px-3 py-1 rounded-full text-sm hover:bg-gray-100">Completed</button>
117
+ </div>
118
+ </div>
119
+
120
+ <!-- Task List -->
121
+ <div id="task-list" class="max-h-[400px] overflow-y-auto custom-scrollbar">
122
+ <!-- Tasks will be added here dynamically -->
123
+ <div class="p-4 text-center text-gray-500" id="empty-state">
124
+ <i class="fas fa-tasks text-4xl mb-2 text-gray-300"></i>
125
+ <p>No tasks yet. Add your first task!</p>
126
+ </div>
127
+ </div>
128
+
129
+ <!-- Clear Completed Button -->
130
+ <div class="p-3 border-t border-gray-100 flex justify-end">
131
+ <button id="clear-completed" class="text-sm text-gray-500 hover:text-red-500 flex items-center gap-1">
132
+ <i class="fas fa-trash-alt"></i>
133
+ <span>Clear Completed</span>
134
+ </button>
135
+ </div>
136
+ </main>
137
+
138
+ <!-- Floating Action Button -->
139
+ <button id="scroll-to-top" class="fixed bottom-6 right-6 bg-indigo-600 text-white w-12 h-12 rounded-full shadow-lg flex items-center justify-center hover:bg-indigo-700 transition-all duration-200 hidden">
140
+ <i class="fas fa-arrow-up"></i>
141
+ </button>
142
+ </div>
143
+
144
+ <script>
145
+ document.addEventListener('DOMContentLoaded', function() {
146
+ // DOM Elements
147
+ const taskInput = document.getElementById('task-input');
148
+ const addTaskBtn = document.getElementById('add-task-btn');
149
+ const taskList = document.getElementById('task-list');
150
+ const emptyState = document.getElementById('empty-state');
151
+ const filterButtons = document.querySelectorAll('.filter-btn');
152
+ const clearCompletedBtn = document.getElementById('clear-completed');
153
+ const scrollToTopBtn = document.getElementById('scroll-to-top');
154
+ const totalCount = document.getElementById('total-count');
155
+ const completedCount = document.getElementById('completed-count');
156
+ const pendingCount = document.getElementById('pending-count');
157
+
158
+ // State
159
+ let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
160
+ let currentFilter = 'all';
161
+
162
+ // Initialize
163
+ renderTasks();
164
+ updateStats();
165
+
166
+ // Event Listeners
167
+ addTaskBtn.addEventListener('click', addTask);
168
+ taskInput.addEventListener('keypress', function(e) {
169
+ if (e.key === 'Enter') addTask();
170
+ });
171
+
172
+ clearCompletedBtn.addEventListener('click', clearCompletedTasks);
173
+ scrollToTopBtn.addEventListener('click', scrollToTop);
174
+
175
+ // Filter buttons
176
+ filterButtons.forEach(btn => {
177
+ btn.addEventListener('click', function() {
178
+ filterButtons.forEach(b => b.classList.remove('active', 'bg-indigo-100', 'text-indigo-700'));
179
+ this.classList.add('active', 'bg-indigo-100', 'text-indigo-700');
180
+ currentFilter = this.dataset.filter;
181
+ renderTasks();
182
+ });
183
+ });
184
+
185
+ // Scroll event for floating button
186
+ window.addEventListener('scroll', function() {
187
+ if (window.pageYOffset > 300) {
188
+ scrollToTopBtn.classList.remove('hidden');
189
+ } else {
190
+ scrollToTopBtn.classList.add('hidden');
191
+ }
192
+ });
193
+
194
+ // Functions
195
+ function addTask() {
196
+ const text = taskInput.value.trim();
197
+ if (text === '') return;
198
+
199
+ const dueDate = document.getElementById('task-due').value;
200
+ const newTask = {
201
+ id: Date.now(),
202
+ text: text,
203
+ completed: false,
204
+ createdAt: new Date().toISOString(),
205
+ dueDate: dueDate || null
206
+ };
207
+
208
+ tasks.unshift(newTask);
209
+ saveTasks();
210
+ renderTasks();
211
+ updateStats();
212
+
213
+ taskInput.value = '';
214
+ taskInput.focus();
215
+
216
+ // Add pulse animation to the add button
217
+ addTaskBtn.classList.add('pulse-animation');
218
+ setTimeout(() => {
219
+ addTaskBtn.classList.remove('pulse-animation');
220
+ }, 2000);
221
+ }
222
+
223
+ function renderTasks() {
224
+ // Filter tasks based on current filter
225
+ let filteredTasks = [];
226
+
227
+ switch (currentFilter) {
228
+ case 'active':
229
+ filteredTasks = tasks.filter(task => !task.completed);
230
+ break;
231
+ case 'completed':
232
+ filteredTasks = tasks.filter(task => task.completed);
233
+ break;
234
+ default:
235
+ filteredTasks = [...tasks];
236
+ }
237
+
238
+ if (filteredTasks.length === 0) {
239
+ emptyState.classList.remove('hidden');
240
+ taskList.innerHTML = '';
241
+ taskList.appendChild(emptyState);
242
+ } else {
243
+ emptyState.classList.add('hidden');
244
+ taskList.innerHTML = '';
245
+
246
+ filteredTasks.forEach(task => {
247
+ const taskElement = createTaskElement(task);
248
+ taskList.appendChild(taskElement);
249
+ });
250
+ }
251
+ }
252
+
253
+ function createTaskElement(task) {
254
+ const taskElement = document.createElement('div');
255
+ taskElement.className = `task-item p-4 border-b border-gray-100 flex items-center gap-3 hover:bg-gray-50 transition-colors duration-150`;
256
+ taskElement.dataset.id = task.id;
257
+
258
+ const checkbox = document.createElement('input');
259
+ checkbox.type = 'checkbox';
260
+ checkbox.className = 'task-checkbox w-5 h-5 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer';
261
+ checkbox.checked = task.completed;
262
+ checkbox.addEventListener('change', function() {
263
+ toggleTaskCompletion(task.id);
264
+ });
265
+
266
+ const taskText = document.createElement('span');
267
+ taskText.className = `task-text flex-1 ${task.completed ? 'line-through text-gray-400' : 'text-gray-700'}`;
268
+ taskText.textContent = task.text;
269
+
270
+ const dateSpan = document.createElement('span');
271
+ dateSpan.className = 'text-xs text-gray-400 ml-2';
272
+ const date = new Date(task.createdAt);
273
+ dateSpan.textContent = date.toLocaleDateString();
274
+
275
+ if (task.dueDate) {
276
+ const dueDate = new Date(task.dueDate);
277
+ const dueSpan = document.createElement('span');
278
+ dueSpan.className = 'text-xs ml-2 ' +
279
+ (new Date() > dueDate && !task.completed ? 'text-red-500' : 'text-gray-500');
280
+ dueSpan.textContent = 'Due: ' + dueDate.toLocaleString();
281
+ taskText.appendChild(dueSpan);
282
+ }
283
+
284
+ const deleteBtn = document.createElement('button');
285
+ deleteBtn.className = 'text-gray-400 hover:text-red-500 transition-colors duration-150';
286
+ deleteBtn.innerHTML = '<i class="fas fa-times"></i>';
287
+ deleteBtn.addEventListener('click', function(e) {
288
+ e.stopPropagation();
289
+ deleteTask(task.id);
290
+ });
291
+
292
+ taskText.appendChild(dateSpan);
293
+ taskElement.appendChild(checkbox);
294
+ taskElement.appendChild(taskText);
295
+ taskElement.appendChild(deleteBtn);
296
+
297
+ return taskElement;
298
+ }
299
+
300
+ function toggleTaskCompletion(taskId) {
301
+ const taskIndex = tasks.findIndex(task => task.id === taskId);
302
+ if (taskIndex !== -1) {
303
+ tasks[taskIndex].completed = !tasks[taskIndex].completed;
304
+ saveTasks();
305
+ renderTasks();
306
+ updateStats();
307
+ }
308
+ }
309
+
310
+ function deleteTask(taskId) {
311
+ tasks = tasks.filter(task => task.id !== taskId);
312
+ saveTasks();
313
+ renderTasks();
314
+ updateStats();
315
+ }
316
+
317
+ function clearCompletedTasks() {
318
+ tasks = tasks.filter(task => !task.completed);
319
+ saveTasks();
320
+ renderTasks();
321
+ updateStats();
322
+
323
+ // Show confirmation animation
324
+ const clearBtn = clearCompletedBtn;
325
+ clearBtn.innerHTML = '<i class="fas fa-check"></i> <span>Cleared!</span>';
326
+ clearBtn.classList.add('text-green-500');
327
+ setTimeout(() => {
328
+ clearBtn.innerHTML = '<i class="fas fa-trash-alt"></i> <span>Clear Completed</span>';
329
+ clearBtn.classList.remove('text-green-500');
330
+ }, 2000);
331
+ }
332
+
333
+ function saveTasks() {
334
+ localStorage.setItem('tasks', JSON.stringify(tasks));
335
+ }
336
+
337
+ function updateStats() {
338
+ totalCount.textContent = tasks.length;
339
+ const completedTasks = tasks.filter(task => task.completed).length;
340
+ completedCount.textContent = completedTasks;
341
+ pendingCount.textContent = tasks.length - completedTasks;
342
+ }
343
+
344
+ function scrollToTop() {
345
+ window.scrollTo({
346
+ top: 0,
347
+ behavior: 'smooth'
348
+ });
349
+ }
350
+ });
351
+ </script>
352
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=jphermans/todo" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
353
+ </html>