Haris-83 commited on
Commit
0f14f93
·
verified ·
1 Parent(s): f222cb4

Upload app.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.js +1540 -0
app.js ADDED
@@ -0,0 +1,1540 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ===== Application State =====
2
+ let tasks = [];
3
+ let categories = [];
4
+ let workspaces = [];
5
+ let currentView = 'all';
6
+ let currentFilter = { priority: 'all', workspace: null, date: null, search: '' };
7
+ let currentSort = 'date-desc';
8
+ let editingTaskId = null;
9
+ let viewMode = 'list';
10
+ let sortableInstance = null;
11
+ let lastDeletedTask = null;
12
+ let focusModeEnabled = false;
13
+ let smartFilter = 'all';
14
+ let calendarMonth = new Date();
15
+ let calendarViewVisible = false;
16
+ let streakCount = 0;
17
+ let myDayTasks = [];
18
+
19
+ // Pomodoro
20
+ let pomodoroRunning = false;
21
+ let pomodoroTimer = null;
22
+ let pomodoroTime = 25 * 60;
23
+ let pomodoroPhase = 'focus';
24
+ let pomodoroSessions = 0;
25
+ let pomodoroTodayMinutes = 0;
26
+
27
+ // ===== Initialize =====
28
+ document.addEventListener('DOMContentLoaded', () => {
29
+ setTimeout(() => {
30
+ document.getElementById('loading-screen').classList.add('hidden');
31
+ loadFromLocalStorage();
32
+ initializeEventListeners();
33
+ setQuickAddDefaults();
34
+ renderTasks();
35
+ renderCategories();
36
+ renderWorkspaces();
37
+ updateWorkspaceSelect();
38
+ updateCounts();
39
+ updateProgress();
40
+ updateInsights();
41
+ applySettings();
42
+ checkDarkMode();
43
+ updateUserStats();
44
+ loadStreak();
45
+ updatePomodoroDisplay();
46
+ initParticles();
47
+ }, 1200);
48
+ });
49
+
50
+ // ===== Local Storage =====
51
+ function saveToLocalStorage() {
52
+ localStorage.setItem('tasks', JSON.stringify(tasks));
53
+ localStorage.setItem('categories', JSON.stringify(categories));
54
+ localStorage.setItem('workspaces', JSON.stringify(workspaces));
55
+ localStorage.setItem('pomodoroSessions', pomodoroSessions.toString());
56
+ localStorage.setItem('pomodoroTodayMinutes', pomodoroTodayMinutes.toString());
57
+ }
58
+
59
+ function loadFromLocalStorage() {
60
+ try {
61
+ tasks = JSON.parse(localStorage.getItem('tasks')) || [];
62
+ categories = JSON.parse(localStorage.getItem('categories')) || [];
63
+ workspaces = JSON.parse(localStorage.getItem('workspaces')) || [];
64
+ pomodoroSessions = parseInt(localStorage.getItem('pomodoroSessions')) || 0;
65
+ pomodoroTodayMinutes = parseInt(localStorage.getItem('pomodoroTodayMinutes')) || 0;
66
+ const savedUserName = localStorage.getItem('userName');
67
+ if (savedUserName) {
68
+ document.getElementById('user-name').textContent = savedUserName;
69
+ document.getElementById('user-name-input').value = savedUserName;
70
+ }
71
+ } catch (e) {
72
+ tasks = [];
73
+ categories = [];
74
+ workspaces = [];
75
+ }
76
+ }
77
+
78
+ // ===== Streak =====
79
+ function loadStreak() {
80
+ const lastDate = localStorage.getItem('lastCompleteDate');
81
+ const today = new Date().toISOString().split('T')[0];
82
+ if (lastDate === today) {
83
+ streakCount = parseInt(localStorage.getItem('streakCount')) || 0;
84
+ } else if (lastDate === getYesterday()) {
85
+ streakCount = parseInt(localStorage.getItem('streakCount')) || 0;
86
+ } else {
87
+ streakCount = 0;
88
+ localStorage.setItem('streakCount', '0');
89
+ }
90
+ updateStreakDisplay();
91
+ }
92
+
93
+ function getYesterday() {
94
+ const d = new Date();
95
+ d.setDate(d.getDate() - 1);
96
+ return d.toISOString().split('T')[0];
97
+ }
98
+
99
+ function updateStreakDisplay() {
100
+ const existing = document.querySelector('.streak-badge');
101
+ if (existing) existing.remove();
102
+ if (streakCount > 0) {
103
+ const badge = document.createElement('span');
104
+ badge.className = 'streak-badge';
105
+ badge.innerHTML = `<i class="fas fa-fire"></i> ${streakCount} day${streakCount > 1 ? 's' : ''}`;
106
+ document.querySelector('.user-info h3').after(badge);
107
+ }
108
+ }
109
+
110
+ function checkStreak() {
111
+ const today = new Date().toISOString().split('T')[0];
112
+ const lastDate = localStorage.getItem('lastCompleteDate');
113
+ if (lastDate === today) return;
114
+ if (lastDate === getYesterday()) {
115
+ streakCount++;
116
+ } else {
117
+ streakCount = 1;
118
+ }
119
+ localStorage.setItem('lastCompleteDate', today);
120
+ localStorage.setItem('streakCount', streakCount.toString());
121
+ updateStreakDisplay();
122
+ }
123
+
124
+ // ===== My Day =====
125
+ function addToMyDay(taskId) {
126
+ const task = tasks.find(t => t.id === taskId);
127
+ if (!task) return;
128
+ if (!myDayTasks.includes(taskId)) {
129
+ myDayTasks.push(taskId);
130
+ localStorage.setItem('myDayTasks', JSON.stringify(myDayTasks));
131
+ showToast('Added to My Day', 'success');
132
+ }
133
+ }
134
+
135
+ function loadMyDay() {
136
+ try {
137
+ myDayTasks = JSON.parse(localStorage.getItem('myDayTasks')) || [];
138
+ } catch { myDayTasks = []; }
139
+ }
140
+
141
+ // ===== Event Listeners =====
142
+ function initializeEventListeners() {
143
+ document.querySelectorAll('.nav-item').forEach(item => {
144
+ item.addEventListener('click', (e) => {
145
+ switchView(e.currentTarget.dataset.view);
146
+ });
147
+ });
148
+
149
+ document.getElementById('add-task-btn').addEventListener('click', () => openTaskModal());
150
+ document.getElementById('close-modal').addEventListener('click', closeTaskModal);
151
+ document.getElementById('cancel-btn').addEventListener('click', closeTaskModal);
152
+ document.getElementById('task-form').addEventListener('submit', saveTask);
153
+
154
+ document.getElementById('task-recurring').addEventListener('change', (e) => {
155
+ document.getElementById('recurring-options').style.display = e.target.checked ? 'block' : 'none';
156
+ });
157
+
158
+ document.getElementById('add-category-btn').addEventListener('click', openCategoryModal);
159
+ document.getElementById('add-workspace-btn').addEventListener('click', openWorkspaceModal);
160
+ document.getElementById('close-workspace-modal').addEventListener('click', closeWorkspaceModal);
161
+ document.getElementById('cancel-workspace-btn').addEventListener('click', closeWorkspaceModal);
162
+ document.getElementById('workspace-form').addEventListener('submit', saveWorkspace);
163
+ document.getElementById('close-category-modal').addEventListener('click', closeCategoryModal);
164
+ document.getElementById('cancel-category-btn').addEventListener('click', closeCategoryModal);
165
+ document.getElementById('category-form').addEventListener('submit', saveCategory);
166
+
167
+ document.querySelectorAll('.color-option').forEach(option => {
168
+ option.addEventListener('click', function() {
169
+ document.querySelectorAll('.color-option').forEach(o => o.classList.remove('active'));
170
+ this.classList.add('active');
171
+ });
172
+ });
173
+
174
+ document.getElementById('settings-btn').addEventListener('click', openSettingsModal);
175
+ document.getElementById('close-settings-modal').addEventListener('click', closeSettingsModal);
176
+
177
+ document.getElementById('pomodoro-toggle-btn').addEventListener('click', togglePomodoroPanel);
178
+ document.getElementById('pomodoro-start').addEventListener('click', togglePomodoro);
179
+ document.getElementById('pomodoro-reset').addEventListener('click', resetPomodoro);
180
+ document.getElementById('pomodoro-skip').addEventListener('click', skipPomodoro);
181
+
182
+ document.getElementById('focus-mode-btn').addEventListener('click', toggleFocusMode);
183
+ document.getElementById('template-btn').addEventListener('click', openTemplateModal);
184
+ document.getElementById('close-template-modal').addEventListener('click', closeTemplateModal);
185
+ document.getElementById('smart-filter-btn').addEventListener('click', cycleSmartFilter);
186
+
187
+ document.getElementById('stats-btn').addEventListener('click', openStatsModal);
188
+ document.getElementById('close-stats-modal').addEventListener('click', closeStatsModal);
189
+
190
+ document.getElementById('shortcuts-btn').addEventListener('click', openShortcutsModal);
191
+ document.getElementById('close-shortcuts-modal').addEventListener('click', closeShortcutsModal);
192
+
193
+ document.getElementById('user-name-input').addEventListener('change', (e) => {
194
+ const name = e.target.value.trim() || 'Welcome Back!';
195
+ document.getElementById('user-name').textContent = name;
196
+ localStorage.setItem('userName', name);
197
+ });
198
+
199
+ document.getElementById('search-input').addEventListener('input', handleSearch);
200
+
201
+ document.getElementById('quick-add-form').addEventListener('submit', handleQuickAddSubmit);
202
+ document.getElementById('complete-visible-btn').addEventListener('click', completeVisibleTasks);
203
+ document.getElementById('clear-completed-btn').addEventListener('click', clearCompletedTasks);
204
+
205
+ document.getElementById('filter-btn').addEventListener('click', toggleFilterPanel);
206
+ document.getElementById('view-toggle').addEventListener('click', toggleViewMode);
207
+ document.getElementById('calendar-toggle').addEventListener('click', toggleCalendarView);
208
+ document.getElementById('calendar-prev').addEventListener('click', () => {
209
+ calendarMonth = new Date(calendarMonth.getFullYear(), calendarMonth.getMonth() - 1, 1);
210
+ renderCalendar();
211
+ });
212
+ document.getElementById('calendar-next').addEventListener('click', () => {
213
+ calendarMonth = new Date(calendarMonth.getFullYear(), calendarMonth.getMonth() + 1, 1);
214
+ renderCalendar();
215
+ });
216
+
217
+ document.querySelectorAll('.filter-option').forEach(option => {
218
+ option.addEventListener('click', handleFilter);
219
+ });
220
+
221
+ document.querySelectorAll('.sort-option').forEach(option => {
222
+ option.addEventListener('click', handleSort);
223
+ });
224
+
225
+ document.getElementById('add-subtask-btn').addEventListener('click', (e) => {
226
+ e.preventDefault();
227
+ addSubtaskInput();
228
+ });
229
+
230
+ document.getElementById('theme-toggle').addEventListener('click', toggleDarkMode);
231
+ document.getElementById('dark-mode-toggle').addEventListener('change', toggleDarkMode);
232
+ document.getElementById('animations-toggle').addEventListener('change', (e) => {
233
+ localStorage.setItem('animationsEnabled', e.target.checked ? 'true' : 'false');
234
+ applySettings();
235
+ });
236
+ document.getElementById('notifications-toggle').addEventListener('change', (e) => {
237
+ localStorage.setItem('notificationsEnabled', e.target.checked ? 'true' : 'false');
238
+ if (e.target.checked) checkNotifications();
239
+ });
240
+ document.getElementById('sound-toggle').addEventListener('change', (e) => {
241
+ localStorage.setItem('soundEnabled', e.target.checked ? 'true' : 'false');
242
+ });
243
+
244
+ document.getElementById('export-data-btn').addEventListener('click', exportData);
245
+ document.getElementById('import-data-btn').addEventListener('click', () => {
246
+ document.getElementById('import-file-input').click();
247
+ });
248
+ document.getElementById('import-file-input').addEventListener('change', importData);
249
+ document.getElementById('clear-data-btn').addEventListener('click', clearAllData);
250
+
251
+ document.getElementById('sidebar-toggle').addEventListener('click', () => {
252
+ document.getElementById('sidebar').classList.toggle('active');
253
+ });
254
+
255
+ document.querySelectorAll('.modal').forEach(modal => {
256
+ modal.addEventListener('click', (e) => {
257
+ if (e.target === modal) modal.classList.remove('active');
258
+ });
259
+ });
260
+ }
261
+
262
+ // ===== Pomodoro =====
263
+ function togglePomodoroPanel() {
264
+ const panel = document.getElementById('pomodoro-container');
265
+ panel.classList.toggle('active');
266
+ }
267
+
268
+ function togglePomodoro() {
269
+ if (pomodoroRunning) {
270
+ clearInterval(pomodoroTimer);
271
+ pomodoroRunning = false;
272
+ document.getElementById('pomodoro-start').innerHTML = '<i class="fas fa-play"></i>';
273
+ document.getElementById('pomodoro-start').classList.remove('active');
274
+ } else {
275
+ pomodoroRunning = true;
276
+ document.getElementById('pomodoro-start').innerHTML = '<i class="fas fa-pause"></i>';
277
+ document.getElementById('pomodoro-start').classList.add('active');
278
+ pomodoroTimer = setInterval(() => {
279
+ pomodoroTime--;
280
+ updatePomodoroDisplay();
281
+ if (pomodoroTime <= 0) {
282
+ clearInterval(pomodoroTimer);
283
+ pomodoroRunning = false;
284
+ document.getElementById('pomodoro-start').innerHTML = '<i class="fas fa-play"></i>';
285
+ document.getElementById('pomodoro-start').classList.remove('active');
286
+ pomodoroComplete();
287
+ }
288
+ }, 1000);
289
+ }
290
+ }
291
+
292
+ function resetPomodoro() {
293
+ clearInterval(pomodoroTimer);
294
+ pomodoroRunning = false;
295
+ pomodoroPhase = 'focus';
296
+ pomodoroTime = 25 * 60;
297
+ document.getElementById('pomodoro-start').innerHTML = '<i class="fas fa-play"></i>';
298
+ document.getElementById('pomodoro-start').classList.remove('active');
299
+ document.getElementById('pomodoro-status').textContent = 'Focus';
300
+ updatePomodoroDisplay();
301
+ }
302
+
303
+ function skipPomodoro() {
304
+ clearInterval(pomodoroTimer);
305
+ pomodoroRunning = false;
306
+ pomodoroComplete();
307
+ }
308
+
309
+ function pomodoroComplete() {
310
+ if (pomodoroPhase === 'focus') {
311
+ pomodoroSessions++;
312
+ pomodoroTodayMinutes += 25;
313
+ saveToLocalStorage();
314
+ document.getElementById('pomodoro-sessions').textContent = pomodoroSessions;
315
+ document.getElementById('pomodoro-today-min').textContent = pomodoroTodayMinutes;
316
+
317
+ if (pomodoroSessions % 4 === 0) {
318
+ pomodoroPhase = 'long-break';
319
+ pomodoroTime = 15 * 60;
320
+ showToast('Long break time! Great work! 🎉', 'success');
321
+ triggerConfetti();
322
+ } else {
323
+ pomodoroPhase = 'short-break';
324
+ pomodoroTime = 5 * 60;
325
+ showToast('Good job! Take a short break ☕', 'success');
326
+ }
327
+ document.getElementById('pomodoro-status').textContent = pomodoroPhase === 'short-break' ? 'Short Break' : 'Long Break';
328
+ } else {
329
+ pomodoroPhase = 'focus';
330
+ pomodoroTime = 25 * 60;
331
+ document.getElementById('pomodoro-status').textContent = 'Focus';
332
+ showToast('Break over! Time to focus 🎯', 'info');
333
+ }
334
+ updatePomodoroDisplay();
335
+ }
336
+
337
+ function updatePomodoroDisplay() {
338
+ const mins = Math.floor(pomodoroTime / 60);
339
+ const secs = pomodoroTime % 60;
340
+ document.getElementById('pomodoro-timer').textContent =
341
+ `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
342
+ document.getElementById('pomodoro-sessions').textContent = pomodoroSessions;
343
+ document.getElementById('pomodoro-today-min').textContent = pomodoroTodayMinutes;
344
+ }
345
+
346
+ // ===== Stats =====
347
+ function openStatsModal() {
348
+ const total = tasks.length;
349
+ const completed = tasks.filter(t => t.completed).length;
350
+ const pending = total - completed;
351
+
352
+ document.getElementById('stats-total').textContent = total;
353
+ document.getElementById('stats-completed').textContent = completed;
354
+ document.getElementById('stats-pending').textContent = pending;
355
+ document.getElementById('stats-streak').textContent = streakCount;
356
+
357
+ renderChart();
358
+ document.getElementById('stats-modal').classList.add('active');
359
+ }
360
+
361
+ function closeStatsModal() {
362
+ document.getElementById('stats-modal').classList.remove('active');
363
+ }
364
+
365
+ function renderChart() {
366
+ const container = document.getElementById('chart-bars');
367
+ container.innerHTML = '';
368
+ const days = [];
369
+ for (let i = 6; i >= 0; i--) {
370
+ const d = new Date();
371
+ d.setDate(d.getDate() - i);
372
+ days.push(d.toISOString().split('T')[0]);
373
+ }
374
+ const maxVal = Math.max(1, ...days.map(day =>
375
+ tasks.filter(t => {
376
+ if (!t.createdAt) return false;
377
+ const taskDay = t.createdAt.split('T')[0];
378
+ return taskDay === day && t.completed;
379
+ }).length
380
+ ));
381
+ days.forEach(day => {
382
+ const count = tasks.filter(t => {
383
+ if (!t.createdAt) return false;
384
+ return t.createdAt.split('T')[0] === day && t.completed;
385
+ }).length;
386
+ const bar = document.createElement('div');
387
+ bar.className = 'chart-bar';
388
+ const pct = (count / maxVal) * 100;
389
+ bar.style.height = `${Math.max(pct, 4)}%`;
390
+ const dateObj = new Date(day + 'T00:00:00');
391
+ const label = dateObj.toLocaleDateString('en-US', { weekday: 'short' });
392
+ bar.innerHTML = `
393
+ <span class="chart-bar-value">${count}</span>
394
+ <span class="chart-bar-label">${label}</span>
395
+ `;
396
+ container.appendChild(bar);
397
+ });
398
+ }
399
+
400
+ // ===== Shortcuts Modal =====
401
+ function openShortcutsModal() {
402
+ document.getElementById('shortcuts-modal').classList.add('active');
403
+ }
404
+
405
+ function closeShortcutsModal() {
406
+ document.getElementById('shortcuts-modal').classList.remove('active');
407
+ }
408
+
409
+ // ===== View Management =====
410
+ function switchView(view) {
411
+ currentView = view;
412
+ currentFilter.category = null;
413
+
414
+ document.querySelectorAll('.nav-item').forEach(item => item.classList.remove('active'));
415
+ document.querySelector(`[data-view="${view}"]`).classList.add('active');
416
+ document.querySelectorAll('.category-item').forEach(item => item.classList.remove('active'));
417
+
418
+ const titles = {
419
+ all: { title: 'All Tasks', subtitle: 'Manage your tasks efficiently' },
420
+ myday: { title: 'My Day', subtitle: 'Tasks you want to focus on today' },
421
+ today: { title: 'Today', subtitle: 'Tasks due today' },
422
+ upcoming: { title: 'Upcoming', subtitle: 'Tasks coming up' },
423
+ overdue: { title: 'Overdue', subtitle: 'Tasks that need attention' },
424
+ completed: { title: 'Completed', subtitle: 'Finished tasks' },
425
+ important: { title: 'Important', subtitle: 'High priority tasks' }
426
+ };
427
+
428
+ document.getElementById('view-title').textContent = titles[view].title;
429
+ document.getElementById('view-subtitle').textContent = titles[view].subtitle;
430
+
431
+ renderTasks();
432
+ updateProgress();
433
+ }
434
+
435
+ function toggleViewMode() {
436
+ viewMode = viewMode === 'list' ? 'grid' : 'list';
437
+ document.querySelector('#view-toggle i').className = viewMode === 'list' ? 'fas fa-th' : 'fas fa-list';
438
+ localStorage.setItem('viewMode', viewMode);
439
+ renderTasks();
440
+ }
441
+
442
+ // ===== Quick Actions =====
443
+ function setQuickAddDefaults() {
444
+ const dateInput = document.getElementById('quick-add-date');
445
+ if (dateInput) dateInput.value = new Date().toISOString().split('T')[0];
446
+ }
447
+
448
+ function handleQuickAddSubmit(e) {
449
+ e.preventDefault();
450
+ const title = document.getElementById('quick-add-title').value.trim();
451
+ if (!title) { showToast('Please enter a task title', 'error'); return; }
452
+
453
+ const parsedTask = parseNaturalLanguageTask(title);
454
+ const newTask = {
455
+ id: Date.now(),
456
+ title: parsedTask.title,
457
+ description: parsedTask.description,
458
+ dueDate: parsedTask.dueDate || document.getElementById('quick-add-date').value,
459
+ dueTime: parsedTask.dueTime || '',
460
+ priority: parsedTask.priority || 'medium',
461
+ category: parsedTask.category || '',
462
+ workspace: '',
463
+ tags: parsedTask.tags || [],
464
+ important: parsedTask.important || false,
465
+ recurring: false,
466
+ recurrence: null,
467
+ subtasks: [],
468
+ completed: false,
469
+ createdAt: new Date().toISOString()
470
+ };
471
+
472
+ tasks.unshift(newTask);
473
+ saveToLocalStorage();
474
+ renderTasks();
475
+ updateCounts();
476
+ updateProgress();
477
+ updateInsights();
478
+ renderCategories();
479
+ updateUserStats();
480
+ showToast('Task added!', 'success');
481
+ playSound('create');
482
+ document.getElementById('quick-add-form').reset();
483
+ setQuickAddDefaults();
484
+ }
485
+
486
+ function parseNaturalLanguageTask(input) {
487
+ const text = input.toLowerCase();
488
+ const result = { title: input.trim(), description: '', dueDate: '', dueTime: '', priority: '', category: '', tags: [], important: false };
489
+
490
+ if (text.includes('urgent') || text.includes('asap')) {
491
+ result.priority = 'high'; result.important = true;
492
+ } else if (text.includes('later')) {
493
+ result.priority = 'low';
494
+ } else if (text.includes('important')) {
495
+ result.priority = 'high'; result.important = true;
496
+ }
497
+
498
+ const today = new Date();
499
+ const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1);
500
+ const nextWeek = new Date(today); nextWeek.setDate(today.getDate() + 7);
501
+
502
+ if (text.includes('today')) result.dueDate = today.toISOString().split('T')[0];
503
+ else if (text.includes('tomorrow')) result.dueDate = tomorrow.toISOString().split('T')[0];
504
+ else if (text.includes('week')) result.dueDate = nextWeek.toISOString().split('T')[0];
505
+
506
+ if (text.includes('work')) { result.category = 'work'; result.tags.push('work'); }
507
+ else if (text.includes('personal')) { result.category = 'personal'; result.tags.push('personal'); }
508
+
509
+ const match = input.match(/@([a-zA-Z0-9-_]+)/);
510
+ if (match) result.tags.push(match[1]);
511
+
512
+ return result;
513
+ }
514
+
515
+ function completeVisibleTasks() {
516
+ const visibleTasks = getFilteredTasks().filter(task => !task.completed);
517
+ if (visibleTasks.length === 0) { showToast('No visible tasks to complete', 'info'); return; }
518
+ visibleTasks.forEach(task => { task.completed = true; });
519
+ saveToLocalStorage();
520
+ renderTasks();
521
+ updateCounts();
522
+ updateProgress();
523
+ updateInsights();
524
+ renderCategories();
525
+ updateUserStats();
526
+ showToast(`Completed ${visibleTasks.length} task${visibleTasks.length !== 1 ? 's' : ''}`, 'success');
527
+ }
528
+
529
+ function toggleFocusMode() {
530
+ focusModeEnabled = !focusModeEnabled;
531
+ document.body.classList.toggle('focus-mode', focusModeEnabled);
532
+ document.getElementById('focus-mode-btn').innerHTML =
533
+ focusModeEnabled ? '<i class="fas fa-bullseye"></i> Focus On' : '<i class="fas fa-bullseye"></i> Focus Mode';
534
+ showToast(focusModeEnabled ? 'Focus mode enabled' : 'Focus mode disabled', 'info');
535
+ }
536
+
537
+ function openTemplateModal() {
538
+ const list = document.getElementById('template-list');
539
+ list.innerHTML = '';
540
+ const templates = [
541
+ { name: 'Deep Work', tasks: ['Block 90 minutes for focused work', 'Close all distractions', 'Review progress'] },
542
+ { name: 'Weekly Review', tasks: ['Review completed tasks', 'Plan next week', 'Update notes'] },
543
+ { name: 'Trip Prep', tasks: ['Pack passport', 'Check travel documents', 'Confirm bookings'] },
544
+ { name: 'Daily Standup', tasks: ['What I did yesterday', 'What I will do today', 'Any blockers'] }
545
+ ];
546
+
547
+ templates.forEach(template => {
548
+ const card = document.createElement('div');
549
+ card.className = 'template-card';
550
+ card.innerHTML = `<strong>${template.name}</strong><div>${template.tasks.join('<br>')}</div><button class="btn btn-primary btn-sm" data-template='${JSON.stringify(template.tasks)}'>Apply</button>`;
551
+ list.appendChild(card);
552
+ });
553
+
554
+ list.querySelectorAll('button').forEach(btn => {
555
+ btn.addEventListener('click', () => {
556
+ const tasksToAdd = JSON.parse(btn.dataset.template);
557
+ tasksToAdd.forEach(taskText => {
558
+ const parsed = parseNaturalLanguageTask(taskText);
559
+ tasks.unshift({
560
+ id: Date.now() + Math.random(),
561
+ title: parsed.title,
562
+ description: parsed.description,
563
+ dueDate: parsed.dueDate || '',
564
+ dueTime: parsed.dueTime || '',
565
+ priority: parsed.priority || 'medium',
566
+ category: parsed.category || '',
567
+ workspace: '',
568
+ reminder: '',
569
+ tags: parsed.tags || [],
570
+ important: parsed.important || false,
571
+ recurring: false,
572
+ recurrence: null,
573
+ subtasks: [],
574
+ completed: false,
575
+ createdAt: new Date().toISOString()
576
+ });
577
+ });
578
+ saveToLocalStorage();
579
+ renderTasks();
580
+ updateCounts();
581
+ updateProgress();
582
+ updateInsights();
583
+ renderCategories();
584
+ updateUserStats();
585
+ closeTemplateModal();
586
+ showToast('Template applied!', 'success');
587
+ });
588
+ });
589
+ document.getElementById('template-modal').classList.add('active');
590
+ }
591
+
592
+ function closeTemplateModal() {
593
+ document.getElementById('template-modal').classList.remove('active');
594
+ }
595
+
596
+ function cycleSmartFilter() {
597
+ const filters = ['all', 'high', 'today', 'important'];
598
+ const index = filters.indexOf(smartFilter);
599
+ smartFilter = filters[(index + 1) % filters.length];
600
+ showToast(`Smart filter: ${smartFilter}`, 'info');
601
+ renderTasks();
602
+ }
603
+
604
+ function clearCompletedTasks() {
605
+ const completedCount = tasks.filter(task => task.completed).length;
606
+ if (completedCount === 0) { showToast('No completed tasks to clear', 'info'); return; }
607
+ tasks = tasks.filter(task => !task.completed);
608
+ saveToLocalStorage();
609
+ renderTasks();
610
+ updateCounts();
611
+ updateProgress();
612
+ updateInsights();
613
+ renderCategories();
614
+ updateUserStats();
615
+ showToast('Completed tasks cleared', 'success');
616
+ }
617
+
618
+ function updateInsights() {
619
+ const today = new Date().toISOString().split('T')[0];
620
+ const activeTasks = tasks.filter(task => !task.completed).length;
621
+ const overdueTasks = tasks.filter(task => task.dueDate && task.dueDate < today && !task.completed).length;
622
+ const dueSoonTasks = tasks.filter(task => {
623
+ if (!task.dueDate || task.completed) return false;
624
+ const diffDays = Math.round((new Date(`${task.dueDate}T00:00:00`) - new Date(`${today}T00:00:00`)) / (86400000));
625
+ return diffDays >= 0 && diffDays <= 2;
626
+ }).length;
627
+
628
+ document.getElementById('insight-active').textContent = `${activeTasks} task${activeTasks !== 1 ? 's' : ''}`;
629
+ document.getElementById('insight-overdue').textContent = `${overdueTasks} task${overdueTasks !== 1 ? 's' : ''}`;
630
+ document.getElementById('insight-soon').textContent = `${dueSoonTasks} task${dueSoonTasks !== 1 ? 's' : ''}`;
631
+ }
632
+
633
+ function getTaskDueState(task) {
634
+ if (task.completed || !task.dueDate) return { className: '', label: '' };
635
+ const today = new Date();
636
+ const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate());
637
+ const dueDate = new Date(`${task.dueDate}T00:00:00`);
638
+ const diffDays = Math.round((dueDate - todayStart) / 86400000);
639
+ if (diffDays < 0) return { className: 'overdue', label: 'Overdue' };
640
+ if (diffDays <= 2) return { className: 'due-soon', label: diffDays === 0 ? 'Due today' : `${diffDays} day${diffDays === 1 ? '' : 's'} left` };
641
+ return { className: '', label: '' };
642
+ }
643
+
644
+ // ===== Task Management =====
645
+ function openTaskModal(taskId = null) {
646
+ const modal = document.getElementById('task-modal');
647
+ const form = document.getElementById('task-form');
648
+ const modalTitle = document.getElementById('modal-title');
649
+
650
+ form.reset();
651
+ document.getElementById('subtasks-container').innerHTML = '';
652
+ document.getElementById('recurring-options').style.display = 'none';
653
+
654
+ if (taskId) {
655
+ editingTaskId = taskId;
656
+ const task = tasks.find(t => t.id === taskId);
657
+ if (!task) return;
658
+ modalTitle.textContent = 'Edit Task';
659
+ document.getElementById('task-title').value = task.title;
660
+ document.getElementById('task-description').value = task.description || '';
661
+ document.getElementById('task-date').value = task.dueDate || '';
662
+ document.getElementById('task-time').value = task.dueTime || '';
663
+ document.getElementById('task-priority').value = task.priority;
664
+ document.getElementById('task-category').value = task.category || '';
665
+ document.getElementById('task-workspace').value = task.workspace || '';
666
+ document.getElementById('task-reminder').value = task.reminder || '';
667
+ document.getElementById('task-tags').value = task.tags ? task.tags.join(', ') : '';
668
+ document.getElementById('task-important').checked = task.important || false;
669
+ document.getElementById('task-recurring').checked = task.recurring || false;
670
+ if (task.recurring) {
671
+ document.getElementById('recurring-options').style.display = 'block';
672
+ document.getElementById('task-recurrence').value = task.recurrence || 'daily';
673
+ }
674
+ if (task.subtasks && task.subtasks.length > 0) {
675
+ task.subtasks.forEach(subtask => addSubtaskInput(subtask.text));
676
+ }
677
+ } else {
678
+ editingTaskId = null;
679
+ modalTitle.textContent = 'Add New Task';
680
+ }
681
+
682
+ updateCategorySelect();
683
+ updateWorkspaceSelect();
684
+ modal.classList.add('active');
685
+ }
686
+
687
+ function closeTaskModal() {
688
+ document.getElementById('task-modal').classList.remove('active');
689
+ editingTaskId = null;
690
+ }
691
+
692
+ function saveTask(e) {
693
+ e.preventDefault();
694
+ const title = document.getElementById('task-title').value.trim();
695
+ if (!title) { showToast('Please enter a task title', 'error'); return; }
696
+
697
+ const taskData = {
698
+ title,
699
+ description: document.getElementById('task-description').value.trim(),
700
+ dueDate: document.getElementById('task-date').value,
701
+ dueTime: document.getElementById('task-time').value,
702
+ priority: document.getElementById('task-priority').value,
703
+ category: document.getElementById('task-category').value,
704
+ workspace: document.getElementById('task-workspace').value,
705
+ reminder: document.getElementById('task-reminder').value,
706
+ tags: document.getElementById('task-tags').value.trim().split(',').map(t => t.trim()).filter(Boolean),
707
+ important: document.getElementById('task-important').checked,
708
+ recurring: document.getElementById('task-recurring').checked,
709
+ recurrence: document.getElementById('task-recurring').checked ? document.getElementById('task-recurrence').value : null,
710
+ subtasks: [],
711
+ completed: false
712
+ };
713
+
714
+ document.querySelectorAll('.subtask-item input[type="text"]').forEach(input => {
715
+ if (input.value.trim()) {
716
+ taskData.subtasks.push({ id: Date.now() + Math.random(), text: input.value.trim(), completed: false });
717
+ }
718
+ });
719
+
720
+ if (editingTaskId) {
721
+ const taskIndex = tasks.findIndex(t => t.id === editingTaskId);
722
+ if (taskIndex !== -1) {
723
+ tasks[taskIndex] = { ...tasks[taskIndex], ...taskData };
724
+ showToast('Task updated!', 'success');
725
+ }
726
+ } else {
727
+ tasks.unshift({ id: Date.now(), createdAt: new Date().toISOString(), ...taskData });
728
+ showToast('Task created!', 'success');
729
+ playSound('create');
730
+ }
731
+
732
+ saveToLocalStorage();
733
+ renderTasks();
734
+ updateCounts();
735
+ updateProgress();
736
+ updateInsights();
737
+ renderCategories();
738
+ updateUserStats();
739
+ closeTaskModal();
740
+ }
741
+
742
+ function deleteTask(taskId) {
743
+ const task = tasks.find(t => t.id === taskId);
744
+ if (!task) return;
745
+ lastDeletedTask = { task: { ...task }, index: tasks.findIndex(t => t.id === taskId) };
746
+ tasks = tasks.filter(t => t.id !== taskId);
747
+ saveToLocalStorage();
748
+ renderTasks();
749
+ updateCounts();
750
+ updateProgress();
751
+ updateInsights();
752
+ renderCategories();
753
+ updateUserStats();
754
+ showToast('Task deleted!', 'success', 'Undo', restoreLastDeletedTask);
755
+ playSound('delete');
756
+ }
757
+
758
+ function restoreLastDeletedTask() {
759
+ if (!lastDeletedTask) return;
760
+ const { task, index } = lastDeletedTask;
761
+ if (index >= 0) tasks.splice(index, 0, task);
762
+ else tasks.unshift(task);
763
+ lastDeletedTask = null;
764
+ saveToLocalStorage();
765
+ renderTasks();
766
+ updateCounts();
767
+ updateProgress();
768
+ updateInsights();
769
+ renderCategories();
770
+ updateUserStats();
771
+ showToast('Task restored!', 'info');
772
+ }
773
+
774
+ function toggleTaskComplete(taskId) {
775
+ const task = tasks.find(t => t.id === taskId);
776
+ if (!task) return;
777
+ task.completed = !task.completed;
778
+
779
+ if (task.completed) {
780
+ showToast('Task completed! 🎉', 'success');
781
+ playSound('complete');
782
+ triggerConfetti();
783
+ checkStreak();
784
+ if (task.recurring && task.recurrence) createRecurringTask(task);
785
+ } else {
786
+ showToast('Task reopened!', 'info');
787
+ }
788
+
789
+ saveToLocalStorage();
790
+ renderTasks();
791
+ updateCounts();
792
+ updateProgress();
793
+ updateInsights();
794
+ renderCategories();
795
+ updateUserStats();
796
+ }
797
+
798
+ function createRecurringTask(originalTask) {
799
+ const newTask = { ...originalTask };
800
+ newTask.id = Date.now();
801
+ newTask.completed = false;
802
+ newTask.createdAt = new Date().toISOString();
803
+ if (newTask.dueDate) {
804
+ const currentDate = new Date(newTask.dueDate);
805
+ if (newTask.recurrence === 'daily') currentDate.setDate(currentDate.getDate() + 1);
806
+ else if (newTask.recurrence === 'weekly') currentDate.setDate(currentDate.getDate() + 7);
807
+ else if (newTask.recurrence === 'monthly') currentDate.setMonth(currentDate.getMonth() + 1);
808
+ newTask.dueDate = currentDate.toISOString().split('T')[0];
809
+ }
810
+ tasks.unshift(newTask);
811
+ saveToLocalStorage();
812
+ }
813
+
814
+ function toggleTaskImportant(taskId) {
815
+ const task = tasks.find(t => t.id === taskId);
816
+ if (!task) return;
817
+ task.important = !task.important;
818
+ saveToLocalStorage();
819
+ renderTasks();
820
+ updateCounts();
821
+ updateInsights();
822
+ showToast(task.important ? 'Marked as important!' : 'Removed from important!', 'info');
823
+ }
824
+
825
+ function toggleSubtask(taskId, subtaskId) {
826
+ const task = tasks.find(t => t.id === taskId);
827
+ if (!task || !task.subtasks) return;
828
+ const subtask = task.subtasks.find(s => s.id === subtaskId);
829
+ if (!subtask) return;
830
+ subtask.completed = !subtask.completed;
831
+ saveToLocalStorage();
832
+ renderTasks();
833
+ }
834
+
835
+ // ===== Render =====
836
+ function renderTasks() {
837
+ const container = document.getElementById('tasks-container');
838
+ const emptyState = document.getElementById('empty-state');
839
+ if (!container || !emptyState) return;
840
+
841
+ container.classList.remove('list-view', 'grid-view');
842
+ container.classList.add(viewMode === 'grid' ? 'grid-view' : 'list-view');
843
+
844
+ let filteredTasks = getFilteredTasks();
845
+
846
+ if (filteredTasks.length === 0) {
847
+ emptyState.style.display = 'flex';
848
+ container.querySelectorAll('.task-card').forEach(card => card.remove());
849
+ if (sortableInstance) { sortableInstance.destroy(); sortableInstance = null; }
850
+ return;
851
+ }
852
+
853
+ emptyState.style.display = 'none';
854
+ container.innerHTML = '';
855
+
856
+ filteredTasks.forEach(task => {
857
+ container.appendChild(createTaskCard(task));
858
+ });
859
+
860
+ if (currentView === 'all' && !sortableInstance) {
861
+ sortableInstance = Sortable.create(container, {
862
+ animation: 200,
863
+ ghostClass: 'dragging',
864
+ onEnd: function(evt) {
865
+ const movedTask = filteredTasks[evt.oldIndex];
866
+ filteredTasks.splice(evt.oldIndex, 1);
867
+ filteredTasks.splice(evt.newIndex, 0, movedTask);
868
+ const allTaskIds = filteredTasks.map(t => t.id);
869
+ tasks.sort((a, b) => {
870
+ const ai = allTaskIds.indexOf(a.id);
871
+ const bi = allTaskIds.indexOf(b.id);
872
+ if (ai === -1) return 1;
873
+ if (bi === -1) return -1;
874
+ return ai - bi;
875
+ });
876
+ saveToLocalStorage();
877
+ }
878
+ });
879
+ } else if (currentView !== 'all' && sortableInstance) {
880
+ sortableInstance.destroy();
881
+ sortableInstance = null;
882
+ }
883
+ }
884
+
885
+ function escapeHtml(text) {
886
+ const div = document.createElement('div');
887
+ div.textContent = text;
888
+ return div.innerHTML;
889
+ }
890
+
891
+ function createTaskCard(task) {
892
+ const card = document.createElement('div');
893
+ const dueState = getTaskDueState(task);
894
+ card.className = `task-card ${task.completed ? 'completed' : ''} ${dueState.className}`;
895
+
896
+ const category = categories.find(c => c.id === task.category);
897
+ const workspace = workspaces.find(w => w.id === task.workspace);
898
+ const categoryBadge = category ?
899
+ `<span class="task-category-badge" style="background:${category.color}15;color:${category.color}"><i class="fas ${category.icon}"></i>${escapeHtml(category.name)}</span>` : '';
900
+
901
+ const tagsHtml = task.tags && task.tags.length > 0 ?
902
+ `<div class="task-tags">${task.tags.map(tag => `<span class="task-tag">${escapeHtml(tag)}</span>`).join('')}</div>` : '';
903
+
904
+ const dueDateHtml = task.dueDate ?
905
+ `<div class="task-meta-item"><i class="fas fa-calendar"></i><span>${formatDate(task.dueDate)}</span></div>` : '';
906
+
907
+ const dueTimeHtml = task.dueTime ?
908
+ `<div class="task-meta-item"><i class="fas fa-clock"></i><span>${task.dueTime}</span></div>` : '';
909
+
910
+ const workspaceBadge = workspace ?
911
+ `<div class="task-meta-item"><i class="fas fa-folder"></i><span>${escapeHtml(workspace.name)}</span></div>` : '';
912
+
913
+ const recurringBadge = task.recurring ?
914
+ `<div class="task-meta-item"><i class="fas fa-redo"></i><span>${task.recurrence}</span></div>` : '';
915
+
916
+ const dueStateBadge = dueState.label ?
917
+ `<div class="task-meta-item due-state ${dueState.className}"><i class="fas fa-bolt"></i><span>${escapeHtml(dueState.label)}</span></div>` : '';
918
+
919
+ let subtasksHtml = '';
920
+ if (task.subtasks && task.subtasks.length > 0) {
921
+ const completedSubtasks = task.subtasks.filter(s => s.completed).length;
922
+ const subtasksItems = task.subtasks.map(subtask => {
923
+ const checked = subtask.completed ? 'checked' : '';
924
+ return `<div class="task-subtask"><input type="checkbox" ${checked} data-task-id="${task.id}" data-subtask-id="${subtask.id}" class="subtask-checkbox"><span style="${subtask.completed ? 'text-decoration:line-through' : ''}">${escapeHtml(subtask.text)}</span></div>`;
925
+ }).join('');
926
+ subtasksHtml = `<div class="task-subtasks"><div style="font-size:12px;color:var(--text-tertiary);margin-bottom:8px">${completedSubtasks}/${task.subtasks.length} subtasks</div>${subtasksItems}</div>`;
927
+ }
928
+
929
+ const isMyDay = myDayTasks.includes(task.id);
930
+
931
+ card.innerHTML = `
932
+ <div class="task-header">
933
+ <div class="task-checkbox ${task.completed ? 'checked' : ''}" data-task-id="${task.id}"></div>
934
+ <div class="task-content">
935
+ <h3 class="task-title">${escapeHtml(task.title)}</h3>
936
+ ${task.description ? `<p class="task-description">${escapeHtml(task.description)}</p>` : ''}
937
+ <div class="task-meta">
938
+ <span class="task-priority ${task.priority}">${task.priority}</span>
939
+ ${categoryBadge}
940
+ ${dueDateHtml}
941
+ ${dueTimeHtml}
942
+ ${workspaceBadge}
943
+ ${recurringBadge}
944
+ ${dueStateBadge}
945
+ ${isMyDay ? '<span class="task-priority" style="background:rgba(245,158,11,0.1);color:var(--warning);font-size:10px"><i class="fas fa-sun"></i> My Day</span>' : ''}
946
+ </div>
947
+ ${tagsHtml}
948
+ ${subtasksHtml}
949
+ </div>
950
+ <div class="task-actions">
951
+ <button class="task-action-btn" data-task-id="${task.id}" data-action="myday" title="Add to My Day"><i class="fas fa-sun"></i></button>
952
+ <button class="task-action-btn ${task.important ? 'important' : ''}" data-task-id="${task.id}" data-action="important" title="Important"><i class="fas fa-star"></i></button>
953
+ <button class="task-action-btn" data-task-id="${task.id}" data-action="edit" title="Edit"><i class="fas fa-edit"></i></button>
954
+ <button class="task-action-btn delete" data-task-id="${task.id}" data-action="delete" title="Delete"><i class="fas fa-trash"></i></button>
955
+ </div>
956
+ </div>
957
+ `;
958
+
959
+ card.querySelector('.task-checkbox').addEventListener('click', function() {
960
+ toggleTaskComplete(parseInt(this.dataset.taskId));
961
+ });
962
+
963
+ card.querySelectorAll('.task-action-btn').forEach(btn => {
964
+ btn.addEventListener('click', (e) => {
965
+ e.stopPropagation();
966
+ const action = btn.dataset.action;
967
+ const taskId = parseInt(btn.dataset.taskId);
968
+ if (action === 'important') toggleTaskImportant(taskId);
969
+ else if (action === 'edit') openTaskModal(taskId);
970
+ else if (action === 'delete') deleteTask(taskId);
971
+ else if (action === 'myday') addToMyDay(taskId);
972
+ });
973
+ });
974
+
975
+ card.querySelectorAll('.subtask-checkbox').forEach(cb => {
976
+ cb.addEventListener('change', (e) => {
977
+ toggleSubtask(parseInt(e.target.dataset.taskId), parseFloat(e.target.dataset.subtaskId));
978
+ });
979
+ });
980
+
981
+ return card;
982
+ }
983
+
984
+ function getFilteredTasks() {
985
+ let filtered = [...tasks];
986
+ const today = new Date().toISOString().split('T')[0];
987
+
988
+ if (smartFilter === 'high') filtered = filtered.filter(task => task.priority === 'high' || task.important);
989
+ else if (smartFilter === 'today') filtered = filtered.filter(task => task.dueDate === today || !task.dueDate);
990
+ else if (smartFilter === 'important') filtered = filtered.filter(task => task.important);
991
+
992
+ loadMyDay();
993
+ switch (currentView) {
994
+ case 'myday':
995
+ filtered = filtered.filter(t => myDayTasks.includes(t.id));
996
+ break;
997
+ case 'today':
998
+ filtered = filtered.filter(t => t.dueDate === today && !t.completed);
999
+ break;
1000
+ case 'upcoming':
1001
+ filtered = filtered.filter(t => t.dueDate && t.dueDate > today && !t.completed);
1002
+ break;
1003
+ case 'overdue':
1004
+ filtered = filtered.filter(t => t.dueDate && t.dueDate < today && !t.completed);
1005
+ break;
1006
+ case 'completed':
1007
+ filtered = filtered.filter(t => t.completed);
1008
+ break;
1009
+ case 'important':
1010
+ filtered = filtered.filter(t => t.important && !t.completed);
1011
+ break;
1012
+ }
1013
+
1014
+ if (currentFilter.priority !== 'all') filtered = filtered.filter(t => t.priority === currentFilter.priority);
1015
+ if (currentFilter.category) filtered = filtered.filter(t => t.category === currentFilter.category);
1016
+ if (currentFilter.workspace) filtered = filtered.filter(t => t.workspace === currentFilter.workspace);
1017
+ if (currentFilter.date) filtered = filtered.filter(t => t.dueDate === currentFilter.date);
1018
+
1019
+ if (currentFilter.search) {
1020
+ const search = currentFilter.search.toLowerCase();
1021
+ filtered = filtered.filter(t =>
1022
+ t.title.toLowerCase().includes(search) ||
1023
+ (t.description && t.description.toLowerCase().includes(search)) ||
1024
+ (t.tags && t.tags.some(tag => tag.toLowerCase().includes(search)))
1025
+ );
1026
+ }
1027
+
1028
+ filtered.sort((a, b) => {
1029
+ switch (currentSort) {
1030
+ case 'date-desc': return new Date(b.createdAt) - new Date(a.createdAt);
1031
+ case 'date-asc': return new Date(a.createdAt) - new Date(b.createdAt);
1032
+ case 'priority': return ({ high: 3, medium: 2, low: 1 })[b.priority] - ({ high: 3, medium: 2, low: 1 })[a.priority];
1033
+ case 'alphabetical': return a.title.localeCompare(b.title);
1034
+ case 'duedate':
1035
+ if (!a.dueDate) return 1;
1036
+ if (!b.dueDate) return -1;
1037
+ return new Date(a.dueDate) - new Date(b.dueDate);
1038
+ default: return 0;
1039
+ }
1040
+ });
1041
+
1042
+ return filtered;
1043
+ }
1044
+
1045
+ // ===== Categories =====
1046
+ function openCategoryModal() {
1047
+ document.getElementById('category-modal').classList.add('active');
1048
+ }
1049
+
1050
+ function closeCategoryModal() {
1051
+ document.getElementById('category-modal').classList.remove('active');
1052
+ document.getElementById('category-form').reset();
1053
+ document.querySelectorAll('.color-option').forEach(o => o.classList.remove('active'));
1054
+ document.querySelector('.color-option').classList.add('active');
1055
+ }
1056
+
1057
+ function saveCategory(e) {
1058
+ e.preventDefault();
1059
+ const name = document.getElementById('category-name').value.trim();
1060
+ if (!name) { showToast('Please enter a category name', 'error'); return; }
1061
+ categories.push({
1062
+ id: Date.now().toString(),
1063
+ name,
1064
+ icon: document.getElementById('category-icon').value,
1065
+ color: document.querySelector('.color-option.active').dataset.color
1066
+ });
1067
+ saveToLocalStorage();
1068
+ renderCategories();
1069
+ updateCategorySelect();
1070
+ closeCategoryModal();
1071
+ showToast('Category created!', 'success');
1072
+ }
1073
+
1074
+ function renderCategories() {
1075
+ const container = document.getElementById('categories-list');
1076
+ container.innerHTML = '';
1077
+ categories.forEach(category => {
1078
+ const item = document.createElement('button');
1079
+ item.className = 'category-item';
1080
+ item.dataset.categoryId = category.id;
1081
+ item.addEventListener('click', function() { filterByCategory(category.id, this); });
1082
+ const count = tasks.filter(t => t.category === category.id && !t.completed).length;
1083
+ item.innerHTML = `<div class="category-icon" style="background:${category.color}15;color:${category.color}"><i class="fas ${category.icon}"></i></div><span>${escapeHtml(category.name)}</span><span class="count">${count}</span>`;
1084
+ container.appendChild(item);
1085
+ });
1086
+ }
1087
+
1088
+ function filterByCategory(categoryId, element) {
1089
+ currentFilter.category = categoryId;
1090
+ document.querySelectorAll('.category-item').forEach(item => item.classList.remove('active'));
1091
+ if (element) element.classList.add('active');
1092
+ renderTasks();
1093
+ }
1094
+
1095
+ function renderWorkspaces() {
1096
+ const container = document.getElementById('workspaces-list');
1097
+ container.innerHTML = '';
1098
+ workspaces.forEach(workspace => {
1099
+ const item = document.createElement('button');
1100
+ item.className = 'category-item';
1101
+ item.innerHTML = `<span>${escapeHtml(workspace.name)}</span><span class="count">${tasks.filter(t => t.workspace === workspace.id).length}</span>`;
1102
+ item.addEventListener('click', () => {
1103
+ currentFilter.workspace = workspace.id;
1104
+ renderTasks();
1105
+ });
1106
+ container.appendChild(item);
1107
+ });
1108
+ }
1109
+
1110
+ function openWorkspaceModal() { document.getElementById('workspace-modal').classList.add('active'); }
1111
+ function closeWorkspaceModal() { document.getElementById('workspace-modal').classList.remove('active'); document.getElementById('workspace-form').reset(); }
1112
+
1113
+ function saveWorkspace(e) {
1114
+ e.preventDefault();
1115
+ const name = document.getElementById('workspace-name').value.trim();
1116
+ if (!name) return;
1117
+ workspaces.push({ id: Date.now().toString(), name });
1118
+ saveToLocalStorage();
1119
+ renderWorkspaces();
1120
+ updateWorkspaceSelect();
1121
+ closeWorkspaceModal();
1122
+ showToast('Workspace created!', 'success');
1123
+ }
1124
+
1125
+ function updateWorkspaceSelect() {
1126
+ const select = document.getElementById('task-workspace');
1127
+ const currentValue = select.value;
1128
+ select.innerHTML = '<option value="">No Workspace</option>';
1129
+ workspaces.forEach(w => {
1130
+ const o = document.createElement('option');
1131
+ o.value = w.id; o.textContent = w.name; select.appendChild(o);
1132
+ });
1133
+ if (currentValue) select.value = currentValue;
1134
+ }
1135
+
1136
+ function updateCategorySelect() {
1137
+ const select = document.getElementById('task-category');
1138
+ const currentValue = select.value;
1139
+ select.innerHTML = '<option value="">No Category</option>';
1140
+ categories.forEach(c => {
1141
+ const o = document.createElement('option');
1142
+ o.value = c.id; o.textContent = c.name; select.appendChild(o);
1143
+ });
1144
+ if (currentValue) select.value = currentValue;
1145
+ }
1146
+
1147
+ // ===== Filter & Sort =====
1148
+ function toggleFilterPanel() { document.getElementById('filter-panel').classList.toggle('active'); }
1149
+
1150
+ function handleFilter(e) {
1151
+ const filterType = e.currentTarget.dataset.filter;
1152
+ const filterValue = e.currentTarget.dataset.value;
1153
+ currentFilter[filterType] = filterValue;
1154
+ e.currentTarget.parentElement.querySelectorAll('.filter-option').forEach(o => o.classList.remove('active'));
1155
+ e.currentTarget.classList.add('active');
1156
+ renderTasks();
1157
+ }
1158
+
1159
+ function handleSort(e) {
1160
+ currentSort = e.currentTarget.dataset.sort;
1161
+ document.querySelectorAll('.sort-option').forEach(o => o.classList.remove('active'));
1162
+ e.currentTarget.classList.add('active');
1163
+ renderTasks();
1164
+ }
1165
+
1166
+ function handleSearch(e) {
1167
+ currentFilter.search = e.target.value.trim();
1168
+ renderTasks();
1169
+ }
1170
+
1171
+ // ===== Subtasks =====
1172
+ function addSubtaskInput(subtaskText = '') {
1173
+ const container = document.getElementById('subtasks-container');
1174
+ const item = document.createElement('div');
1175
+ item.className = 'subtask-item';
1176
+ const input = document.createElement('input');
1177
+ input.type = 'text';
1178
+ input.placeholder = 'Enter subtask';
1179
+ input.value = subtaskText;
1180
+ const removeBtn = document.createElement('button');
1181
+ removeBtn.type = 'button';
1182
+ removeBtn.innerHTML = '<i class="fas fa-times"></i>';
1183
+ removeBtn.addEventListener('click', () => item.remove());
1184
+ item.appendChild(input);
1185
+ item.appendChild(removeBtn);
1186
+ container.appendChild(item);
1187
+ }
1188
+
1189
+ // ===== Settings =====
1190
+ function openSettingsModal() { document.getElementById('settings-modal').classList.add('active'); }
1191
+ function closeSettingsModal() { document.getElementById('settings-modal').classList.remove('active'); }
1192
+
1193
+ function toggleDarkMode() {
1194
+ document.body.classList.toggle('dark-mode');
1195
+ const isDark = document.body.classList.contains('dark-mode');
1196
+ localStorage.setItem('darkMode', isDark ? 'enabled' : 'disabled');
1197
+ document.getElementById('dark-mode-toggle').checked = isDark;
1198
+ document.querySelector('#theme-toggle i').className = isDark ? 'fas fa-sun' : 'fas fa-moon';
1199
+ }
1200
+
1201
+ function applySettings() {
1202
+ const animationsEnabled = localStorage.getItem('animationsEnabled') !== 'false';
1203
+ const notificationsEnabled = localStorage.getItem('notificationsEnabled') !== 'false';
1204
+ const soundEnabled = localStorage.getItem('soundEnabled') !== 'false';
1205
+ const savedViewMode = localStorage.getItem('viewMode');
1206
+ document.body.classList.toggle('no-animations', !animationsEnabled);
1207
+ document.getElementById('animations-toggle').checked = animationsEnabled;
1208
+ document.getElementById('notifications-toggle').checked = notificationsEnabled;
1209
+ document.getElementById('sound-toggle').checked = soundEnabled;
1210
+ if (savedViewMode === 'grid' || savedViewMode === 'list') {
1211
+ viewMode = savedViewMode;
1212
+ const icon = document.querySelector('#view-toggle i');
1213
+ if (icon) icon.className = viewMode === 'list' ? 'fas fa-th' : 'fas fa-list';
1214
+ }
1215
+ }
1216
+
1217
+ function checkDarkMode() {
1218
+ if (localStorage.getItem('darkMode') === 'enabled') {
1219
+ document.body.classList.add('dark-mode');
1220
+ document.getElementById('dark-mode-toggle').checked = true;
1221
+ document.querySelector('#theme-toggle i').className = 'fas fa-sun';
1222
+ }
1223
+ }
1224
+
1225
+ function exportData() {
1226
+ const data = { tasks, categories, workspaces, exportDate: new Date().toISOString(), version: '3.0' };
1227
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
1228
+ const url = URL.createObjectURL(blob);
1229
+ const link = document.createElement('a');
1230
+ link.href = url;
1231
+ link.download = `taskmaster-backup-${Date.now()}.json`;
1232
+ link.click();
1233
+ URL.revokeObjectURL(url);
1234
+ showToast('Data exported!', 'success');
1235
+ }
1236
+
1237
+ function importData(e) {
1238
+ const file = e.target.files[0];
1239
+ if (!file) return;
1240
+ const reader = new FileReader();
1241
+ reader.onload = (event) => {
1242
+ try {
1243
+ const data = JSON.parse(event.target.result);
1244
+ if (data.tasks && data.categories) {
1245
+ tasks = data.tasks;
1246
+ categories = data.categories;
1247
+ workspaces = data.workspaces || [];
1248
+ saveToLocalStorage();
1249
+ renderTasks();
1250
+ renderCategories();
1251
+ renderWorkspaces();
1252
+ updateWorkspaceSelect();
1253
+ updateCounts();
1254
+ updateProgress();
1255
+ updateInsights();
1256
+ updateUserStats();
1257
+ showToast('Data imported!', 'success');
1258
+ } else {
1259
+ showToast('Invalid data format!', 'error');
1260
+ }
1261
+ } catch (err) {
1262
+ showToast('Error: ' + err.message, 'error');
1263
+ }
1264
+ };
1265
+ reader.readAsText(file);
1266
+ e.target.value = '';
1267
+ }
1268
+
1269
+ function clearAllData() {
1270
+ if (confirm('Are you sure you want to clear all data? This cannot be undone!')) {
1271
+ tasks = [];
1272
+ categories = [];
1273
+ workspaces = [];
1274
+ saveToLocalStorage();
1275
+ renderTasks();
1276
+ renderCategories();
1277
+ updateCounts();
1278
+ updateProgress();
1279
+ updateInsights();
1280
+ updateUserStats();
1281
+ showToast('All data cleared!', 'success');
1282
+ }
1283
+ }
1284
+
1285
+ // ===== Utility =====
1286
+ function toggleCalendarView() {
1287
+ calendarViewVisible = !calendarViewVisible;
1288
+ document.getElementById('calendar-view-panel').hidden = !calendarViewVisible;
1289
+ document.getElementById('tasks-container').hidden = calendarViewVisible;
1290
+ if (calendarViewVisible) renderCalendar();
1291
+ }
1292
+
1293
+ function renderCalendar() {
1294
+ const grid = document.getElementById('calendar-grid');
1295
+ const label = document.getElementById('calendar-label');
1296
+ label.textContent = calendarMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
1297
+ const year = calendarMonth.getFullYear();
1298
+ const month = calendarMonth.getMonth();
1299
+ const firstDay = new Date(year, month, 1).getDay();
1300
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
1301
+ const cells = [];
1302
+ for (let i = 0; i < firstDay; i++) cells.push('<div class="calendar-cell muted"></div>');
1303
+ for (let day = 1; day <= daysInMonth; day++) {
1304
+ const dateKey = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
1305
+ const dayTasks = tasks.filter(task => task.dueDate === dateKey && !task.completed);
1306
+ const isSelected = currentFilter.date === dateKey;
1307
+ cells.push(`<div class="calendar-cell ${isSelected ? 'active' : ''}" data-date="${dateKey}"><strong>${day}</strong><div class="calendar-task-count">${dayTasks.length} task${dayTasks.length === 1 ? '' : 's'}</div></div>`);
1308
+ }
1309
+ grid.innerHTML = cells.join('');
1310
+ grid.querySelectorAll('.calendar-cell[data-date]').forEach(cell => {
1311
+ cell.addEventListener('click', () => {
1312
+ currentFilter.date = cell.dataset.date;
1313
+ renderCalendar();
1314
+ renderTasks();
1315
+ });
1316
+ });
1317
+ }
1318
+
1319
+ function updateCounts() {
1320
+ const today = new Date().toISOString().split('T')[0];
1321
+ loadMyDay();
1322
+ document.getElementById('all-count').textContent = tasks.filter(t => !t.completed).length;
1323
+ document.getElementById('myday-count').textContent = myDayTasks.length;
1324
+ document.getElementById('today-count').textContent = tasks.filter(t => t.dueDate === today && !t.completed).length;
1325
+ document.getElementById('upcoming-count').textContent = tasks.filter(t => t.dueDate && t.dueDate > today && !t.completed).length;
1326
+ document.getElementById('overdue-count').textContent = tasks.filter(t => t.dueDate && t.dueDate < today && !t.completed).length;
1327
+ document.getElementById('completed-count').textContent = tasks.filter(t => t.completed).length;
1328
+ document.getElementById('important-count').textContent = tasks.filter(t => t.important && !t.completed).length;
1329
+ document.getElementById('stat-completed').textContent = tasks.filter(t => t.completed).length;
1330
+ document.getElementById('stat-pending').textContent = tasks.filter(t => !t.completed).length;
1331
+ }
1332
+
1333
+ function updateProgress() {
1334
+ const today = new Date().toISOString().split('T')[0];
1335
+ const todayTasks = tasks.filter(t => t.dueDate === today);
1336
+ const completedToday = todayTasks.filter(t => t.completed).length;
1337
+ const totalToday = todayTasks.length;
1338
+ const percentage = totalToday > 0 ? Math.round((completedToday / totalToday) * 100) : 0;
1339
+ document.getElementById('progress-text').textContent = `${percentage}%`;
1340
+ document.getElementById('progress-fill').style.width = `${percentage}%`;
1341
+ }
1342
+
1343
+ function updateUserStats() {
1344
+ const today = new Date().toISOString().split('T')[0];
1345
+ const todayTasks = tasks.filter(t => t.dueDate === today && !t.completed).length;
1346
+ document.getElementById('user-stats').textContent = `${todayTasks} task${todayTasks !== 1 ? 's' : ''} today`;
1347
+ }
1348
+
1349
+ function formatDate(dateString) {
1350
+ const date = new Date(dateString);
1351
+ const today = new Date();
1352
+ const tomorrow = new Date(today);
1353
+ tomorrow.setDate(tomorrow.getDate() + 1);
1354
+ const todayStr = today.toISOString().split('T')[0];
1355
+ const tomorrowStr = tomorrow.toISOString().split('T')[0];
1356
+ if (dateString === todayStr) return 'Today';
1357
+ else if (dateString === tomorrowStr) return 'Tomorrow';
1358
+ else if (dateString < todayStr) return '⚠️ Overdue';
1359
+ else return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
1360
+ }
1361
+
1362
+ function showToast(message, type = 'success', actionLabel = '', actionHandler = null) {
1363
+ const toast = document.getElementById('toast');
1364
+ const toastMessage = document.getElementById('toast-message');
1365
+ const toastIcon = toast.querySelector('i');
1366
+ const toastAction = document.getElementById('toast-action');
1367
+
1368
+ toastMessage.textContent = message;
1369
+ if (actionLabel && actionHandler) {
1370
+ toastAction.textContent = actionLabel;
1371
+ toastAction.style.display = 'inline-flex';
1372
+ toastAction.onclick = actionHandler;
1373
+ } else {
1374
+ toastAction.textContent = '';
1375
+ toastAction.style.display = 'none';
1376
+ toastAction.onclick = null;
1377
+ }
1378
+
1379
+ toastIcon.className = type === 'success' ? 'fas fa-check-circle' : type === 'error' ? 'fas fa-exclamation-circle' : 'fas fa-info-circle';
1380
+ toast.style.background = type === 'success' ? 'var(--success)' : type === 'error' ? 'var(--danger)' : 'var(--info)';
1381
+ toast.classList.add('show');
1382
+ setTimeout(() => toast.classList.remove('show'), 3500);
1383
+ }
1384
+
1385
+ function scheduleReminders() {
1386
+ const now = new Date();
1387
+ tasks.filter(task => task.reminder && !task.completed).forEach(task => {
1388
+ const reminderTime = new Date(task.reminder);
1389
+ if (reminderTime > now) {
1390
+ setTimeout(() => {
1391
+ if (Notification.permission === 'granted') {
1392
+ new Notification('Task Reminder', { body: task.title });
1393
+ }
1394
+ }, reminderTime - now);
1395
+ }
1396
+ });
1397
+ }
1398
+
1399
+ function playSound(type) {
1400
+ if (localStorage.getItem('soundEnabled') === 'false') return;
1401
+ try {
1402
+ const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
1403
+ const osc = audioCtx.createOscillator();
1404
+ const gain = audioCtx.createGain();
1405
+ osc.connect(gain);
1406
+ gain.connect(audioCtx.destination);
1407
+ if (type === 'complete') { osc.frequency.value = 800; gain.gain.value = 0.1; osc.start(); osc.stop(audioCtx.currentTime + 0.1); }
1408
+ else if (type === 'create') { osc.frequency.value = 600; gain.gain.value = 0.05; osc.start(); osc.stop(audioCtx.currentTime + 0.05); }
1409
+ else if (type === 'delete') { osc.frequency.value = 400; gain.gain.value = 0.05; osc.start(); osc.stop(audioCtx.currentTime + 0.05); }
1410
+ } catch (e) {}
1411
+ }
1412
+
1413
+ function triggerConfetti() {
1414
+ const canvas = document.getElementById('confetti-canvas');
1415
+ const ctx = canvas.getContext('2d');
1416
+ canvas.width = window.innerWidth;
1417
+ canvas.height = window.innerHeight;
1418
+
1419
+ const confetti = [];
1420
+ const count = 60;
1421
+ const colors = ['#6366f1', '#a855f7', '#ec4899', '#f59e0b', '#22c55e', '#06b6d4'];
1422
+
1423
+ for (let i = 0; i < count; i++) {
1424
+ confetti.push({
1425
+ x: Math.random() * canvas.width,
1426
+ y: Math.random() * canvas.height - canvas.height,
1427
+ r: Math.random() * 6 + 3,
1428
+ d: Math.random() * count,
1429
+ color: colors[Math.floor(Math.random() * colors.length)],
1430
+ tilt: Math.random() * 10 - 10,
1431
+ tiltAngleIncremental: Math.random() * 0.07 + 0.05,
1432
+ tiltAngle: 0
1433
+ });
1434
+ }
1435
+
1436
+ let frameCount = 0;
1437
+ function draw() {
1438
+ frameCount++;
1439
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1440
+ confetti.forEach((c, i) => {
1441
+ ctx.beginPath();
1442
+ ctx.lineWidth = c.r / 2;
1443
+ ctx.strokeStyle = c.color;
1444
+ c.tiltAngle += c.tiltAngleIncremental;
1445
+ c.y += (Math.cos(c.d) + 3 + c.r / 2) / 2;
1446
+ c.tilt = Math.sin(c.tiltAngle - i / 3) * 15;
1447
+ ctx.moveTo(c.x + c.tilt + c.r, c.y);
1448
+ ctx.lineTo(c.x + c.tilt, c.y + c.tilt + c.r);
1449
+ ctx.stroke();
1450
+ if (c.y > canvas.height) confetti.splice(i, 1);
1451
+ });
1452
+ if (confetti.length > 0 && frameCount < 150) {
1453
+ requestAnimationFrame(draw);
1454
+ } else {
1455
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1456
+ }
1457
+ }
1458
+ draw();
1459
+ }
1460
+
1461
+ function initParticles() {
1462
+ const canvas = document.getElementById('particles-canvas');
1463
+ const ctx = canvas.getContext('2d');
1464
+ canvas.width = window.innerWidth;
1465
+ canvas.height = window.innerHeight;
1466
+
1467
+ const particles = [];
1468
+ const count = 40;
1469
+
1470
+ for (let i = 0; i < count; i++) {
1471
+ particles.push({
1472
+ x: Math.random() * canvas.width,
1473
+ y: Math.random() * canvas.height,
1474
+ vx: (Math.random() - 0.5) * 0.5,
1475
+ vy: (Math.random() - 0.5) * 0.5,
1476
+ r: Math.random() * 2 + 1,
1477
+ opacity: Math.random() * 0.5 + 0.1
1478
+ });
1479
+ }
1480
+
1481
+ function animate() {
1482
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1483
+ const isDark = document.body.classList.contains('dark-mode');
1484
+ particles.forEach(p => {
1485
+ p.x += p.vx;
1486
+ p.y += p.vy;
1487
+ if (p.x < 0) p.x = canvas.width;
1488
+ if (p.x > canvas.width) p.x = 0;
1489
+ if (p.y < 0) p.y = canvas.height;
1490
+ if (p.y > canvas.height) p.y = 0;
1491
+
1492
+ ctx.beginPath();
1493
+ ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
1494
+ ctx.fillStyle = isDark ? `rgba(255,255,255,${p.opacity * 0.3})` : `rgba(99,102,241,${p.opacity * 0.3})`;
1495
+ ctx.fill();
1496
+ });
1497
+ requestAnimationFrame(animate);
1498
+ }
1499
+ animate();
1500
+
1501
+ window.addEventListener('resize', () => {
1502
+ canvas.width = window.innerWidth;
1503
+ canvas.height = window.innerHeight;
1504
+ });
1505
+ }
1506
+
1507
+ // ===== Keyboard =====
1508
+ document.addEventListener('keydown', (e) => {
1509
+ if ((e.ctrlKey || e.metaKey) && e.key === 'n') { e.preventDefault(); openTaskModal(); }
1510
+ if ((e.ctrlKey || e.metaKey) && e.key === 'd') { e.preventDefault(); toggleDarkMode(); }
1511
+ if ((e.ctrlKey || e.metaKey) && e.key === 'f') { e.preventDefault(); document.getElementById('search-input').focus(); }
1512
+ if ((e.ctrlKey || e.metaKey) && e.key === 'p') { e.preventDefault(); togglePomodoroPanel(); }
1513
+ if ((e.ctrlKey || e.metaKey) && e.key === 'Shift' && e.key === 'F') { e.preventDefault(); toggleFocusMode(); }
1514
+ if (e.key === 'Escape') {
1515
+ document.querySelectorAll('.modal.active').forEach(modal => modal.classList.remove('active'));
1516
+ }
1517
+ });
1518
+
1519
+ // ===== Notifications =====
1520
+ function checkNotifications() {
1521
+ if (localStorage.getItem('notificationsEnabled') === 'false') return;
1522
+ if (!('Notification' in window)) return;
1523
+ if (Notification.permission === 'granted') {
1524
+ const today = new Date().toISOString().split('T')[0];
1525
+ const todayTasks = tasks.filter(t => t.dueDate === today && !t.completed);
1526
+ if (todayTasks.length > 0) {
1527
+ new Notification('TaskMaster Pro', {
1528
+ body: `You have ${todayTasks.length} task${todayTasks.length !== 1 ? 's' : ''} due today!`
1529
+ });
1530
+ }
1531
+ } else if (Notification.permission !== 'denied') {
1532
+ Notification.requestPermission();
1533
+ }
1534
+ }
1535
+
1536
+ setTimeout(() => {
1537
+ checkNotifications();
1538
+ scheduleReminders();
1539
+ loadMyDay();
1540
+ }, 2000);