(function () { 'use strict'; var STORAGE_KEY = 'focus-desk.tasks.v1'; var PRIORITIES = ['low', 'medium', 'high']; var $ = function (sel, root) { return (root || document).querySelector(sel); }; var form = $('#task-form'); var titleInput = $('#task-title'); var notesInput = $('#task-notes'); var priorityInput = $('#task-priority'); var dueInput = $('#task-due'); var titleError = $('#title-error'); var searchInput = $('#search'); var statusFilter = $('#status-filter'); var priorityFilter = $('#priority-filter'); var list = $('#task-list'); var emptyState = $('#empty-state'); var emptyTitle = $('#empty-title'); var emptyText = $('#empty-text'); var template = $('#task-template'); var tasks = load(); var editingId = null; function load() { try { var raw = localStorage.getItem(STORAGE_KEY); if (!raw) return []; var data = JSON.parse(raw); if (!Array.isArray(data)) return []; return data.filter(function (t) { return t && typeof t === 'object' && typeof t.title === 'string'; }).map(function (t) { return { id: String(t.id || uid()), title: t.title, notes: typeof t.notes === 'string' ? t.notes : '', priority: PRIORITIES.indexOf(t.priority) >= 0 ? t.priority : 'medium', due: /^\d{4}-\d{2}-\d{2}$/.test(t.due || '') ? t.due : '', completed: !!t.completed, createdAt: Number(t.createdAt) || Date.now() }; }); } catch (e) { console.warn('Focus Desk: could not read saved tasks, starting empty.', e); return []; } } function save() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks)); } catch (e) { console.warn('Focus Desk: could not save tasks.', e); } } function uid() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 8); } function findTask(id) { for (var i = 0; i < tasks.length; i++) if (tasks[i].id === id) return tasks[i]; return null; } function todayISO() { var d = new Date(); var m = String(d.getMonth() + 1).padStart(2, '0'); var day = String(d.getDate()).padStart(2, '0'); return d.getFullYear() + '-' + m + '-' + day; } function formatDue(due) { var parts = due.split('-'); var d = new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2])); if (isNaN(d.getTime())) return due; return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); } function visibleTasks() { var q = searchInput.value.trim().toLowerCase(); var status = statusFilter.value; var prio = priorityFilter.value; return tasks.filter(function (t) { if (status === 'active' && t.completed) return false; if (status === 'completed' && !t.completed) return false; if (prio !== 'all' && t.priority !== prio) return false; if (q && t.title.toLowerCase().indexOf(q) < 0 && t.notes.toLowerCase().indexOf(q) < 0) return false; return true; }); } function updateCounts() { var done = tasks.filter(function (t) { return t.completed; }).length; $('#count-total').textContent = tasks.length; $('#count-active').textContent = tasks.length - done; $('#count-completed').textContent = done; } function render() { var shown = visibleTasks(); var today = todayISO(); list.textContent = ''; shown.forEach(function (t) { var node = template.content.firstElementChild.cloneNode(true); node.dataset.id = t.id; node.classList.toggle('done', t.completed); var toggle = $('.toggle', node); toggle.checked = t.completed; toggle.setAttribute('aria-label', (t.completed ? 'Mark incomplete: ' : 'Mark complete: ') + t.title); $('.task-title', node).textContent = t.title; $('.task-notes', node).textContent = t.notes; var badge = $('.priority', node); badge.textContent = t.priority; badge.classList.add(t.priority); badge.setAttribute('aria-label', 'Priority: ' + t.priority); var due = $('.due', node); if (t.due) { due.textContent = 'Due ' + formatDue(t.due); if (!t.completed && t.due < today) { due.classList.add('overdue'); due.textContent += ' (overdue)'; } } $('.edit', node).setAttribute('aria-label', 'Edit task: ' + t.title); $('.delete', node).setAttribute('aria-label', 'Delete task: ' + t.title); if (editingId === t.id) { $('.task-view', node).hidden = true; var ef = $('.task-edit', node); ef.hidden = false; $('.edit-title', ef).value = t.title; $('.edit-notes', ef).value = t.notes; $('.edit-priority', ef).value = t.priority; $('.edit-due', ef).value = t.due; } list.appendChild(node); }); var isEmpty = shown.length === 0; emptyState.hidden = !isEmpty; if (isEmpty) { if (tasks.length === 0) { emptyTitle.textContent = 'No tasks yet'; emptyText.textContent = 'Add your first task using the form.'; } else { emptyTitle.textContent = 'No matching tasks'; emptyText.textContent = 'Try a different search or clear the status and priority filters.'; } } updateCounts(); } form.addEventListener('submit', function (e) { e.preventDefault(); var title = titleInput.value.trim(); if (!title) { titleError.hidden = false; titleInput.setAttribute('aria-invalid', 'true'); titleInput.focus(); return; } titleError.hidden = true; titleInput.removeAttribute('aria-invalid'); tasks.unshift({ id: uid(), title: title, notes: notesInput.value.trim(), priority: PRIORITIES.indexOf(priorityInput.value) >= 0 ? priorityInput.value : 'medium', due: dueInput.value || '', completed: false, createdAt: Date.now() }); save(); form.reset(); priorityInput.value = 'medium'; render(); titleInput.focus(); }); titleInput.addEventListener('input', function () { if (titleInput.value.trim()) { titleError.hidden = true; titleInput.removeAttribute('aria-invalid'); } }); list.addEventListener('change', function (e) { if (!e.target.classList.contains('toggle')) return; var task = findTask(e.target.closest('.task-item').dataset.id); if (!task) return; task.completed = e.target.checked; save(); render(); }); list.addEventListener('click', function (e) { var btn = e.target.closest('button'); if (!btn) return; var item = btn.closest('.task-item'); var id = item.dataset.id; if (btn.classList.contains('edit')) { editingId = id; render(); var input = $('.task-item[data-id="' + id + '"] .edit-title', list); if (input) { input.focus(); input.select(); } } else if (btn.classList.contains('delete')) { tasks = tasks.filter(function (t) { return t.id !== id; }); if (editingId === id) editingId = null; save(); render(); } else if (btn.classList.contains('cancel')) { editingId = null; render(); focusEditButton(id); } }); list.addEventListener('submit', function (e) { e.preventDefault(); var ef = e.target; if (!ef.classList.contains('task-edit')) return; var item = ef.closest('.task-item'); var task = findTask(item.dataset.id); if (!task) return; var titleField = $('.edit-title', ef); var title = titleField.value.trim(); if (!title) { titleField.setAttribute('aria-invalid', 'true'); titleField.focus(); return; } task.title = title; task.notes = $('.edit-notes', ef).value.trim(); var p = $('.edit-priority', ef).value; task.priority = PRIORITIES.indexOf(p) >= 0 ? p : task.priority; task.due = $('.edit-due', ef).value || ''; editingId = null; save(); render(); focusEditButton(task.id); }); list.addEventListener('keydown', function (e) { if (e.key === 'Escape' && editingId && e.target.closest('.task-edit')) { var id = editingId; editingId = null; render(); focusEditButton(id); } }); function focusEditButton(id) { var btn = $('.task-item[data-id="' + id + '"] .edit', list); if (btn) btn.focus(); } [searchInput, statusFilter, priorityFilter].forEach(function (el) { el.addEventListener('input', render); el.addEventListener('change', render); }); render(); })();