class TodoApp { constructor() { this.tasks = []; this.currentFilter = 'all'; this.initElements(); this.initEventListeners(); this.loadTasks(); this.initFireworks(); } initElements() { this.taskInput = document.getElementById('taskInput'); this.addBtn = document.getElementById('addBtn'); this.taskList = document.getElementById('taskList'); this.emptyState = document.getElementById('emptyState'); this.clearCompletedBtn = document.getElementById('clearCompleted'); this.totalTasksEl = document.getElementById('totalTasks'); this.completedTasksEl = document.getElementById('completedTasks'); this.filterBtns = document.querySelectorAll('.filter-btn'); } initEventListeners() { this.addBtn.addEventListener('click', () => this.addTask()); this.taskInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') this.addTask(); }); this.clearCompletedBtn.addEventListener('click', () => this.clearCompleted()); this.filterBtns.forEach(btn => { btn.addEventListener('click', () => { this.filterBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); this.currentFilter = btn.dataset.filter; this.render(); }); }); } initFireworks() { const canvas = document.getElementById('fireworks'); this.fireworks = new Fireworks(canvas); } loadTasks() { const stored = localStorage.getItem('tasks'); if (stored) { this.tasks = JSON.parse(stored); } this.render(); } saveTasks() { localStorage.setItem('tasks', JSON.stringify(this.tasks)); } addTask() { const text = this.taskInput.value.trim(); if (!text) return; const task = { id: Date.now(), text: text, createdAt: new Date().toISOString(), completedAt: null, completed: false }; this.tasks.unshift(task); this.taskInput.value = ''; this.saveTasks(); this.render(); } toggleTask(id) { const task = this.tasks.find(t => t.id === id); if (!task) return; task.completed = !task.completed; task.completedAt = task.completed ? new Date().toISOString() : null; if (task.completed) { this.fireworks.start(); } this.saveTasks(); this.render(); } deleteTask(id) { this.tasks = this.tasks.filter(t => t.id !== id); this.saveTasks(); this.render(); } clearCompleted() { this.tasks = this.tasks.filter(t => !t.completed); this.saveTasks(); this.render(); } formatDate(dateString) { const date = new Date(dateString); const now = new Date(); const diff = now - date; const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); if (minutes < 1) return 'just now'; if (minutes < 60) return `${minutes}m ago`; if (hours < 24) return `${hours}h ago`; if (days < 7) return `${days}d ago`; return date.toLocaleDateString(); } getFilteredTasks() { switch (this.currentFilter) { case 'active': return this.tasks.filter(t => !t.completed); case 'completed': return this.tasks.filter(t => t.completed); default: return this.tasks; } } updateStats() { const total = this.tasks.length; const completed = this.tasks.filter(t => t.completed).length; this.totalTasksEl.textContent = `${total} ${total === 1 ? 'task' : 'tasks'}`; this.completedTasksEl.textContent = `${completed} completed`; } render() { const filteredTasks = this.getFilteredTasks(); this.taskList.innerHTML = ''; this.emptyState.classList.toggle('show', filteredTasks.length === 0); filteredTasks.forEach(task => { const taskEl = document.createElement('div'); taskEl.className = `task-item ${task.completed ? 'completed' : ''}`; taskEl.innerHTML = `
${this.escapeHtml(task.text)}
📅 Created: ${this.formatDate(task.createdAt)}
${task.completedAt ? `
✅ Completed: ${this.formatDate(task.completedAt)}
` : ''}
`; this.taskList.appendChild(taskEl); }); this.updateStats(); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } } // Initialize app const app = new TodoApp();