XOHDY commited on
Commit
21f2bde
·
verified ·
1 Parent(s): dd060cf

need you to create new app to do list as Kanban board, save all things localy so the user not lost the data - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +595 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Kanban
3
- emoji: 🏃
4
  colorFrom: purple
5
- colorTo: gray
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: kanban
3
+ emoji: 🐳
4
  colorFrom: purple
5
+ colorTo: yellow
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,595 @@
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>Kanban To-Do Board</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
+ .kanban-column {
11
+ min-height: 300px;
12
+ transition: all 0.3s ease;
13
+ }
14
+ .task-card {
15
+ transition: all 0.2s ease;
16
+ cursor: grab;
17
+ }
18
+ .task-card:active {
19
+ cursor: grabbing;
20
+ }
21
+ .task-card:hover {
22
+ transform: translateY(-2px);
23
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
24
+ }
25
+ .dragging {
26
+ opacity: 0.5;
27
+ transform: scale(1.05);
28
+ }
29
+ .column-hover {
30
+ background-color: rgba(226, 232, 240, 0.5);
31
+ }
32
+ </style>
33
+ </head>
34
+ <body class="bg-gray-100 min-h-screen">
35
+ <div class="container mx-auto px-4 py-8">
36
+ <!-- Header -->
37
+ <header class="flex flex-col md:flex-row justify-between items-center mb-8">
38
+ <h1 class="text-3xl font-bold text-indigo-700 mb-4 md:mb-0">
39
+ <i class="fas fa-tasks mr-2"></i> Kanban To-Do Board
40
+ </h1>
41
+ <div class="flex space-x-2">
42
+ <button id="addTaskBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center">
43
+ <i class="fas fa-plus mr-2"></i> Add Task
44
+ </button>
45
+ <button id="clearAllBtn" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg flex items-center">
46
+ <i class="fas fa-trash-alt mr-2"></i> Clear All
47
+ </button>
48
+ </div>
49
+ </header>
50
+
51
+ <!-- Kanban Board -->
52
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
53
+ <!-- Backlog Column -->
54
+ <div class="kanban-column bg-white rounded-lg shadow-md p-4" data-status="backlog">
55
+ <div class="flex justify-between items-center mb-4">
56
+ <h2 class="text-lg font-semibold text-gray-700">
57
+ <i class="fas fa-inbox mr-2"></i> Backlog
58
+ </h2>
59
+ <span id="backlog-count" class="bg-gray-200 text-gray-700 text-xs font-medium px-2.5 py-0.5 rounded-full">0</span>
60
+ </div>
61
+ <div class="tasks-container space-y-3 min-h-[200px]" id="backlog-tasks"></div>
62
+ <button class="add-task-btn mt-3 w-full text-gray-400 hover:text-indigo-600 py-1 rounded" data-status="backlog">
63
+ <i class="fas fa-plus mr-1"></i> Add task
64
+ </button>
65
+ </div>
66
+
67
+ <!-- To Do Column -->
68
+ <div class="kanban-column bg-white rounded-lg shadow-md p-4" data-status="todo">
69
+ <div class="flex justify-between items-center mb-4">
70
+ <h2 class="text-lg font-semibold text-gray-700">
71
+ <i class="fas fa-clipboard-list mr-2"></i> To Do
72
+ </h2>
73
+ <span id="todo-count" class="bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-0.5 rounded-full">0</span>
74
+ </div>
75
+ <div class="tasks-container space-y-3 min-h-[200px]" id="todo-tasks"></div>
76
+ <button class="add-task-btn mt-3 w-full text-gray-400 hover:text-indigo-600 py-1 rounded" data-status="todo">
77
+ <i class="fas fa-plus mr-1"></i> Add task
78
+ </button>
79
+ </div>
80
+
81
+ <!-- In Progress Column -->
82
+ <div class="kanban-column bg-white rounded-lg shadow-md p-4" data-status="in-progress">
83
+ <div class="flex justify-between items-center mb-4">
84
+ <h2 class="text-lg font-semibold text-gray-700">
85
+ <i class="fas fa-spinner mr-2"></i> In Progress
86
+ </h2>
87
+ <span id="in-progress-count" class="bg-yellow-100 text-yellow-800 text-xs font-medium px-2.5 py-0.5 rounded-full">0</span>
88
+ </div>
89
+ <div class="tasks-container space-y-3 min-h-[200px]" id="in-progress-tasks"></div>
90
+ <button class="add-task-btn mt-3 w-full text-gray-400 hover:text-indigo-600 py-1 rounded" data-status="in-progress">
91
+ <i class="fas fa-plus mr-1"></i> Add task
92
+ </button>
93
+ </div>
94
+
95
+ <!-- Done Column -->
96
+ <div class="kanban-column bg-white rounded-lg shadow-md p-4" data-status="done">
97
+ <div class="flex justify-between items-center mb-4">
98
+ <h2 class="text-lg font-semibold text-gray-700">
99
+ <i class="fas fa-check-circle mr-2"></i> Done
100
+ </h2>
101
+ <span id="done-count" class="bg-green-100 text-green-800 text-xs font-medium px-2.5 py-0.5 rounded-full">0</span>
102
+ </div>
103
+ <div class="tasks-container space-y-3 min-h-[200px]" id="done-tasks"></div>
104
+ <button class="add-task-btn mt-3 w-full text-gray-400 hover:text-indigo-600 py-1 rounded" data-status="done">
105
+ <i class="fas fa-plus mr-1"></i> Add task
106
+ </button>
107
+ </div>
108
+ </div>
109
+ </div>
110
+
111
+ <!-- Add/Edit Task Modal -->
112
+ <div id="taskModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
113
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
114
+ <div class="flex justify-between items-center border-b px-6 py-4">
115
+ <h3 class="text-lg font-semibold text-gray-800" id="modalTitle">Add New Task</h3>
116
+ <button id="closeModalBtn" class="text-gray-400 hover:text-gray-600">
117
+ <i class="fas fa-times"></i>
118
+ </button>
119
+ </div>
120
+ <div class="p-6">
121
+ <form id="taskForm">
122
+ <input type="hidden" id="taskId">
123
+ <div class="mb-4">
124
+ <label for="taskTitle" class="block text-sm font-medium text-gray-700 mb-1">Title</label>
125
+ <input type="text" id="taskTitle" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" required>
126
+ </div>
127
+ <div class="mb-4">
128
+ <label for="taskDescription" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
129
+ <textarea id="taskDescription" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"></textarea>
130
+ </div>
131
+ <div class="mb-4">
132
+ <label for="taskStatus" class="block text-sm font-medium text-gray-700 mb-1">Status</label>
133
+ <select id="taskStatus" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
134
+ <option value="backlog">Backlog</option>
135
+ <option value="todo">To Do</option>
136
+ <option value="in-progress">In Progress</option>
137
+ <option value="done">Done</option>
138
+ </select>
139
+ </div>
140
+ <div class="mb-4">
141
+ <label for="taskPriority" class="block text-sm font-medium text-gray-700 mb-1">Priority</label>
142
+ <select id="taskPriority" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
143
+ <option value="low">Low</option>
144
+ <option value="medium" selected>Medium</option>
145
+ <option value="high">High</option>
146
+ </select>
147
+ </div>
148
+ <div class="mb-4">
149
+ <label for="taskDueDate" class="block text-sm font-medium text-gray-700 mb-1">Due Date</label>
150
+ <input type="date" id="taskDueDate" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
151
+ </div>
152
+ </form>
153
+ </div>
154
+ <div class="flex justify-end space-x-3 border-t px-6 py-4">
155
+ <button id="cancelTaskBtn" class="px-4 py-2 text-gray-600 hover:text-gray-800">Cancel</button>
156
+ <button id="saveTaskBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Save Task</button>
157
+ </div>
158
+ </div>
159
+ </div>
160
+
161
+ <!-- Confirmation Modal -->
162
+ <div id="confirmModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
163
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
164
+ <div class="flex justify-between items-center border-b px-6 py-4">
165
+ <h3 class="text-lg font-semibold text-gray-800">Confirm Action</h3>
166
+ <button id="closeConfirmModalBtn" class="text-gray-400 hover:text-gray-600">
167
+ <i class="fas fa-times"></i>
168
+ </button>
169
+ </div>
170
+ <div class="p-6">
171
+ <p id="confirmMessage" class="text-gray-700">Are you sure you want to perform this action?</p>
172
+ </div>
173
+ <div class="flex justify-end space-x-3 border-t px-6 py-4">
174
+ <button id="cancelConfirmBtn" class="px-4 py-2 text-gray-600 hover:text-gray-800">Cancel</button>
175
+ <button id="confirmActionBtn" class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700">Confirm</button>
176
+ </div>
177
+ </div>
178
+ </div>
179
+
180
+ <script>
181
+ document.addEventListener('DOMContentLoaded', function() {
182
+ // DOM Elements
183
+ const kanbanBoard = document.querySelector('.grid');
184
+ const columns = document.querySelectorAll('.kanban-column');
185
+ const taskContainers = {
186
+ 'backlog': document.getElementById('backlog-tasks'),
187
+ 'todo': document.getElementById('todo-tasks'),
188
+ 'in-progress': document.getElementById('in-progress-tasks'),
189
+ 'done': document.getElementById('done-tasks')
190
+ };
191
+ const countElements = {
192
+ 'backlog': document.getElementById('backlog-count'),
193
+ 'todo': document.getElementById('todo-count'),
194
+ 'in-progress': document.getElementById('in-progress-count'),
195
+ 'done': document.getElementById('done-count')
196
+ };
197
+
198
+ // Modal Elements
199
+ const taskModal = document.getElementById('taskModal');
200
+ const confirmModal = document.getElementById('confirmModal');
201
+ const taskForm = document.getElementById('taskForm');
202
+ const modalTitle = document.getElementById('modalTitle');
203
+ const taskIdInput = document.getElementById('taskId');
204
+ const taskTitleInput = document.getElementById('taskTitle');
205
+ const taskDescriptionInput = document.getElementById('taskDescription');
206
+ const taskStatusInput = document.getElementById('taskStatus');
207
+ const taskPriorityInput = document.getElementById('taskPriority');
208
+ const taskDueDateInput = document.getElementById('taskDueDate');
209
+ const confirmMessage = document.getElementById('confirmMessage');
210
+
211
+ // State
212
+ let tasks = JSON.parse(localStorage.getItem('kanban-tasks')) || [];
213
+ let currentTaskId = null;
214
+ let confirmAction = null;
215
+
216
+ // Initialize the app
217
+ function init() {
218
+ renderTasks();
219
+ setupEventListeners();
220
+ setupDragAndDrop();
221
+ }
222
+
223
+ // Render all tasks to the board
224
+ function renderTasks() {
225
+ // Clear all task containers first
226
+ Object.values(taskContainers).forEach(container => {
227
+ container.innerHTML = '';
228
+ });
229
+
230
+ // Reset all counters
231
+ Object.values(countElements).forEach(element => {
232
+ element.textContent = '0';
233
+ });
234
+
235
+ // Render each task to its appropriate column
236
+ tasks.forEach(task => {
237
+ addTaskToDOM(task);
238
+ updateCounter(task.status);
239
+ });
240
+ }
241
+
242
+ // Add a single task to the DOM
243
+ function addTaskToDOM(task) {
244
+ const taskElement = createTaskElement(task);
245
+ taskContainers[task.status].appendChild(taskElement);
246
+ }
247
+
248
+ // Create a task element
249
+ function createTaskElement(task) {
250
+ const priorityColors = {
251
+ 'low': 'bg-blue-100 text-blue-800',
252
+ 'medium': 'bg-yellow-100 text-yellow-800',
253
+ 'high': 'bg-red-100 text-red-800'
254
+ };
255
+
256
+ const priorityText = {
257
+ 'low': 'Low',
258
+ 'medium': 'Medium',
259
+ 'high': 'High'
260
+ };
261
+
262
+ const taskElement = document.createElement('div');
263
+ taskElement.className = 'task-card bg-white p-3 rounded-lg shadow-sm border border-gray-200';
264
+ taskElement.dataset.taskId = task.id;
265
+ taskElement.draggable = true;
266
+
267
+ const dueDate = task.dueDate ? new Date(task.dueDate) : null;
268
+ const today = new Date();
269
+ today.setHours(0, 0, 0, 0);
270
+
271
+ let dueDateBadge = '';
272
+ if (dueDate) {
273
+ const dueDateStr = dueDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
274
+ const isOverdue = dueDate < today && task.status !== 'done';
275
+
276
+ if (isOverdue) {
277
+ dueDateBadge = `<span class="text-xs font-medium px-2 py-0.5 rounded-full bg-red-100 text-red-800">
278
+ <i class="fas fa-exclamation-circle mr-1"></i>Overdue: ${dueDateStr}
279
+ </span>`;
280
+ } else {
281
+ dueDateBadge = `<span class="text-xs font-medium px-2 py-0.5 rounded-full bg-gray-100 text-gray-800">
282
+ <i class="far fa-calendar-alt mr-1"></i>${dueDateStr}
283
+ </span>`;
284
+ }
285
+ }
286
+
287
+ taskElement.innerHTML = `
288
+ <div class="flex justify-between items-start mb-2">
289
+ <h3 class="font-medium text-gray-800">${task.title}</h3>
290
+ <div class="flex space-x-1">
291
+ <button class="edit-task-btn text-gray-400 hover:text-indigo-600" data-task-id="${task.id}">
292
+ <i class="fas fa-edit text-sm"></i>
293
+ </button>
294
+ <button class="delete-task-btn text-gray-400 hover:text-red-600" data-task-id="${task.id}">
295
+ <i class="fas fa-trash-alt text-sm"></i>
296
+ </button>
297
+ </div>
298
+ </div>
299
+ ${task.description ? `<p class="text-sm text-gray-600 mb-2">${task.description}</p>` : ''}
300
+ <div class="flex justify-between items-center">
301
+ <span class="text-xs font-medium px-2 py-0.5 rounded-full ${priorityColors[task.priority]}">
302
+ ${priorityText[task.priority]}
303
+ </span>
304
+ ${dueDateBadge}
305
+ </div>
306
+ `;
307
+
308
+ return taskElement;
309
+ }
310
+
311
+ // Update counter for a specific status
312
+ function updateCounter(status) {
313
+ const count = tasks.filter(task => task.status === status).length;
314
+ countElements[status].textContent = count;
315
+ }
316
+
317
+ // Save tasks to localStorage
318
+ function saveTasks() {
319
+ localStorage.setItem('kanban-tasks', JSON.stringify(tasks));
320
+ }
321
+
322
+ // Generate a unique ID for new tasks
323
+ function generateId() {
324
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
325
+ }
326
+
327
+ // Open the task modal for adding/editing
328
+ function openTaskModal(task = null) {
329
+ if (task) {
330
+ // Edit mode
331
+ modalTitle.textContent = 'Edit Task';
332
+ taskIdInput.value = task.id;
333
+ taskTitleInput.value = task.title;
334
+ taskDescriptionInput.value = task.description || '';
335
+ taskStatusInput.value = task.status;
336
+ taskPriorityInput.value = task.priority || 'medium';
337
+ taskDueDateInput.value = task.dueDate || '';
338
+ currentTaskId = task.id;
339
+ } else {
340
+ // Add mode
341
+ modalTitle.textContent = 'Add New Task';
342
+ taskForm.reset();
343
+ taskIdInput.value = '';
344
+ currentTaskId = null;
345
+ }
346
+ taskModal.classList.remove('hidden');
347
+ }
348
+
349
+ // Close the task modal
350
+ function closeTaskModal() {
351
+ taskModal.classList.add('hidden');
352
+ }
353
+
354
+ // Open confirmation modal
355
+ function openConfirmModal(message, action) {
356
+ confirmMessage.textContent = message;
357
+ confirmAction = action;
358
+ confirmModal.classList.remove('hidden');
359
+ }
360
+
361
+ // Close confirmation modal
362
+ function closeConfirmModal() {
363
+ confirmModal.classList.add('hidden');
364
+ confirmAction = null;
365
+ }
366
+
367
+ // Add a new task
368
+ function addTask(taskData) {
369
+ const newTask = {
370
+ id: generateId(),
371
+ title: taskData.title,
372
+ description: taskData.description,
373
+ status: taskData.status,
374
+ priority: taskData.priority,
375
+ dueDate: taskData.dueDate,
376
+ createdAt: new Date().toISOString()
377
+ };
378
+
379
+ tasks.push(newTask);
380
+ saveTasks();
381
+ addTaskToDOM(newTask);
382
+ updateCounter(newTask.status);
383
+ }
384
+
385
+ // Update an existing task
386
+ function updateTask(taskId, updatedData) {
387
+ const taskIndex = tasks.findIndex(task => task.id === taskId);
388
+ if (taskIndex !== -1) {
389
+ const originalStatus = tasks[taskIndex].status;
390
+
391
+ // Update task data
392
+ tasks[taskIndex] = {
393
+ ...tasks[taskIndex],
394
+ ...updatedData
395
+ };
396
+
397
+ saveTasks();
398
+
399
+ // If status changed, we need to move the task
400
+ if (originalStatus !== updatedData.status) {
401
+ renderTasks(); // Full re-render is simpler for status change
402
+ } else {
403
+ // Just update the existing task element
404
+ const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
405
+ if (taskElement) {
406
+ const newTaskElement = createTaskElement(tasks[taskIndex]);
407
+ taskElement.replaceWith(newTaskElement);
408
+ }
409
+ }
410
+ }
411
+ }
412
+
413
+ // Delete a task
414
+ function deleteTask(taskId) {
415
+ const taskIndex = tasks.findIndex(task => task.id === taskId);
416
+ if (taskIndex !== -1) {
417
+ const status = tasks[taskIndex].status;
418
+ tasks.splice(taskIndex, 1);
419
+ saveTasks();
420
+
421
+ // Remove from DOM
422
+ const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
423
+ if (taskElement) {
424
+ taskElement.remove();
425
+ }
426
+
427
+ updateCounter(status);
428
+ }
429
+ }
430
+
431
+ // Clear all tasks
432
+ function clearAllTasks() {
433
+ tasks = [];
434
+ saveTasks();
435
+ renderTasks();
436
+ }
437
+
438
+ // Setup drag and drop functionality
439
+ function setupDragAndDrop() {
440
+ let draggedTask = null;
441
+
442
+ // Drag start event
443
+ document.addEventListener('dragstart', function(e) {
444
+ if (e.target.classList.contains('task-card')) {
445
+ draggedTask = e.target;
446
+ e.target.classList.add('dragging');
447
+
448
+ // Set the drag image to be the task card itself
449
+ setTimeout(() => {
450
+ e.target.classList.add('hidden');
451
+ }, 0);
452
+ }
453
+ });
454
+
455
+ // Drag end event
456
+ document.addEventListener('dragend', function(e) {
457
+ if (e.target.classList.contains('task-card')) {
458
+ e.target.classList.remove('dragging', 'hidden');
459
+ draggedTask = null;
460
+
461
+ // Remove hover styles from all columns
462
+ columns.forEach(column => {
463
+ column.classList.remove('column-hover');
464
+ });
465
+ }
466
+ });
467
+
468
+ // Drag over event for columns
469
+ columns.forEach(column => {
470
+ column.addEventListener('dragover', function(e) {
471
+ e.preventDefault();
472
+ column.classList.add('column-hover');
473
+ });
474
+
475
+ column.addEventListener('dragleave', function() {
476
+ column.classList.remove('column-hover');
477
+ });
478
+
479
+ column.addEventListener('drop', function(e) {
480
+ e.preventDefault();
481
+ column.classList.remove('column-hover');
482
+
483
+ if (draggedTask) {
484
+ const taskId = draggedTask.dataset.taskId;
485
+ const newStatus = column.dataset.status;
486
+
487
+ // Update task status
488
+ const taskIndex = tasks.findIndex(task => task.id === taskId);
489
+ if (taskIndex !== -1 && tasks[taskIndex].status !== newStatus) {
490
+ tasks[taskIndex].status = newStatus;
491
+ saveTasks();
492
+
493
+ // Move task to new column
494
+ const tasksContainer = column.querySelector('.tasks-container');
495
+ tasksContainer.appendChild(draggedTask);
496
+
497
+ // Update counters
498
+ updateCounter(newStatus);
499
+ updateCounter(tasks[taskIndex].status); // Original status
500
+ }
501
+ }
502
+ });
503
+ });
504
+ }
505
+
506
+ // Setup event listeners
507
+ function setupEventListeners() {
508
+ // Add task button
509
+ document.getElementById('addTaskBtn').addEventListener('click', () => openTaskModal());
510
+
511
+ // Column add task buttons
512
+ document.querySelectorAll('.add-task-btn').forEach(button => {
513
+ button.addEventListener('click', function() {
514
+ openTaskModal({
515
+ status: this.dataset.status
516
+ });
517
+ });
518
+ });
519
+
520
+ // Modal buttons
521
+ document.getElementById('closeModalBtn').addEventListener('click', closeTaskModal);
522
+ document.getElementById('cancelTaskBtn').addEventListener('click', closeTaskModal);
523
+ document.getElementById('saveTaskBtn').addEventListener('click', function() {
524
+ if (taskTitleInput.value.trim() === '') {
525
+ alert('Task title is required');
526
+ return;
527
+ }
528
+
529
+ const taskData = {
530
+ title: taskTitleInput.value.trim(),
531
+ description: taskDescriptionInput.value.trim(),
532
+ status: taskStatusInput.value,
533
+ priority: taskPriorityInput.value,
534
+ dueDate: taskDueDateInput.value
535
+ };
536
+
537
+ if (currentTaskId) {
538
+ updateTask(currentTaskId, taskData);
539
+ } else {
540
+ addTask(taskData);
541
+ }
542
+
543
+ closeTaskModal();
544
+ });
545
+
546
+ // Confirmation modal buttons
547
+ document.getElementById('closeConfirmModalBtn').addEventListener('click', closeConfirmModal);
548
+ document.getElementById('cancelConfirmBtn').addEventListener('click', closeConfirmModal);
549
+ document.getElementById('confirmActionBtn').addEventListener('click', function() {
550
+ if (confirmAction) {
551
+ confirmAction();
552
+ }
553
+ closeConfirmModal();
554
+ });
555
+
556
+ // Clear all button
557
+ document.getElementById('clearAllBtn').addEventListener('click', function() {
558
+ if (tasks.length > 0) {
559
+ openConfirmModal('Are you sure you want to delete all tasks? This action cannot be undone.', clearAllTasks);
560
+ } else {
561
+ alert('There are no tasks to clear.');
562
+ }
563
+ });
564
+
565
+ // Edit and delete buttons (delegated events since tasks are dynamic)
566
+ kanbanBoard.addEventListener('click', function(e) {
567
+ // Edit task
568
+ if (e.target.closest('.edit-task-btn')) {
569
+ const taskId = e.target.closest('.edit-task-btn').dataset.taskId;
570
+ const task = tasks.find(task => task.id === taskId);
571
+ if (task) {
572
+ openTaskModal(task);
573
+ }
574
+ }
575
+
576
+ // Delete task
577
+ if (e.target.closest('.delete-task-btn')) {
578
+ const taskId = e.target.closest('.delete-task-btn').dataset.taskId;
579
+ const task = tasks.find(task => task.id === taskId);
580
+
581
+ if (task) {
582
+ openConfirmModal('Are you sure you want to delete this task?', () => {
583
+ deleteTask(taskId);
584
+ });
585
+ }
586
+ }
587
+ });
588
+ }
589
+
590
+ // Initialize the app
591
+ init();
592
+ });
593
+ </script>
594
+ <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=XOHDY/kanban" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
595
+ </html>