class TaskManager { constructor() { this.tasks = []; this.load(); this.render(); this.attachEventListeners(); } load() { try { const stored = localStorage.getItem('focusdeskTasks'); this.tasks = stored ? JSON.parse(stored) : []; } catch { this.tasks = []; localStorage.removeItem('focusdeskTasks'); } } save() { localStorage.setItem('focusdeskTasks', JSON.stringify(this.tasks)); } generateId() { return Date.now() + Math.random().toString(36).substr(2, 9); } addTask(title, notes, priority, due) { const trimmed = title.trim(); if (!trimmed) return false; this.tasks.unshift({ id: this.generateId(), title: trimmed, notes: notes.trim(), priority, due, completed: false, created: new Date().toISOString() }); this.save(); return true; } deleteTask(id) { this.tasks = this.tasks.filter(t => t.id !== id); this.save(); } toggleTask(id) { const task = this.tasks.find(t => t.id === id); if (task) { task.completed = !task.completed; this.save(); } } updateTask(id, updates) { const task = this.tasks.find(t => t.id === id); if (task) { Object.assign(task, updates); this.save(); } } getFilteredTasks() { const search = document.getElementById('searchInput').value.toLowerCase(); const statusFilter = document.getElementById('statusFilter').value; const priorityFilter = document.getElementById('priorityFilter').value; return this.tasks.filter(task => { const matchesSearch = task.title.toLowerCase().includes(search) || task.notes.toLowerCase().includes(search); const matchesStatus = statusFilter === 'all' || (statusFilter === 'active' && !task.completed) || (statusFilter === 'completed' && task.completed); const matchesPriority = priorityFilter === 'all' || task.priority === priorityFilter; return matchesSearch && matchesStatus && matchesPriority; }); } updateCounts() { const total = this.tasks.length; const active = this.tasks.filter(t => !t.completed).length; const completed = this.tasks.filter(t => t.completed).length; document.querySelector('[data-testid="count-total"]').textContent = total; document.querySelector('[data-testid="count-active"]').textContent = active; document.querySelector('[data-testid="count-completed"]').textContent = completed; } formatDate(dateStr) { if (!dateStr) return ''; const d = new Date(dateStr); return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } renderTaskItem(task) { const div = document.createElement('div'); div.className = `task-item ${task.completed ? 'completed' : ''}`; div.data-testid = 'task-item'; div.dataset.taskId = task.id; const dueHtml = task.due ? `
📅 ${this.formatDate(task.due)}
` : ''; const notesHtml = task.notes ? `
${this.escapeHtml(task.notes)}
` : ''; div.innerHTML = `
${this.escapeHtml(task.title)} ${task.priority}
${notesHtml}
${dueHtml}
`; const checkbox = div.querySelector('[data-testid="task-toggle"]'); checkbox.addEventListener('change', () => { this.toggleTask(task.id); this.render(); }); const editBtn = div.querySelector('[data-testid="task-edit"]'); editBtn.addEventListener('click', () => this.showEditForm(task)); const deleteBtn = div.querySelector('[data-testid="task-delete"]'); deleteBtn.addEventListener('click', () => { this.deleteTask(task.id); this.render(); }); return div; } showEditForm(task) { const backdrop = document.createElement('div'); backdrop.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; z-index: 1000; `; const modal = document.createElement('div'); modal.style.cssText = ` background: white; padding: 24px; border-radius: 8px; max-width: 90%; width: 100%; max-height: 90vh; overflow-y: auto; box-shadow: 0 10px 25px rgba(0,0,0,0.2); `; modal.innerHTML = `

Edit Task

`; backdrop.appendChild(modal); document.body.appendChild(backdrop); const titleInput = modal.querySelector('#editTitle'); titleInput.focus(); modal.querySelector('#saveBtn').addEventListener('click', () => { const newTitle = titleInput.value.trim(); if (!newTitle) { titleInput.focus(); return; } this.updateTask(task.id, { title: newTitle, notes: modal.querySelector('#editNotes').value.trim(), priority: modal.querySelector('#editPriority').value, due: modal.querySelector('#editDue').value }); document.body.removeChild(backdrop); this.render(); }); modal.querySelector('#cancelBtn').addEventListener('click', () => { document.body.removeChild(backdrop); }); backdrop.addEventListener('click', (e) => { if (e.target === backdrop) { document.body.removeChild(backdrop); } }); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } render() { this.updateCounts(); const filtered = this.getFilteredTasks(); const taskList = document.querySelector('[data-testid="task-list"]'); const emptyState = document.querySelector('[data-testid="empty-state"]'); taskList.innerHTML = ''; if (filtered.length === 0) { emptyState.hidden = false; } else { emptyState.hidden = true; filtered.forEach(task => { taskList.appendChild(this.renderTaskItem(task)); }); } } attachEventListeners() { document.getElementById('createForm').addEventListener('submit', (e) => { e.preventDefault(); const title = document.getElementById('taskTitle').value; const notes = document.getElementById('taskNotes').value; const priority = document.getElementById('taskPriority').value; const due = document.getElementById('taskDue').value; if (this.addTask(title, notes, priority, due)) { document.getElementById('taskTitle').value = ''; document.getElementById('taskNotes').value = ''; document.getElementById('taskPriority').value = 'medium'; document.getElementById('taskDue').value = ''; this.render(); } }); document.getElementById('searchInput').addEventListener('input', () => this.render()); document.getElementById('statusFilter').addEventListener('change', () => this.render()); document.getElementById('priorityFilter').addEventListener('change', () => this.render()); } } document.addEventListener('DOMContentLoaded', () => { new TaskManager(); });