"use strict"; (function () { var KEY = "focusdesk.tasks.v1"; var tasks = load(); var editingId = null; var $ = function (id) { return document.getElementById(id); }; var form = $("task-form"), titleIn = $("task-title"), notesIn = $("task-notes"), prioIn = $("task-priority"), dueIn = $("task-due"), titleErr = $("title-error"), searchIn = $("search"), statusSel = $("status-filter"), prioSel = $("priority-filter"), listEl = $("task-list"), emptyEl = $("empty-state"); function load() { try { var raw = localStorage.getItem(KEY); if (!raw) return []; var data = JSON.parse(raw); if (!Array.isArray(data)) return []; return data.filter(function (t) { return t && typeof t.id === "string" && typeof t.title === "string"; }).map(function (t) { return { id: t.id, title: t.title, notes: typeof t.notes === "string" ? t.notes : "", priority: ["low", "medium", "high"].indexOf(t.priority) >= 0 ? t.priority : "medium", due: typeof t.due === "string" ? t.due : "", completed: !!t.completed }; }); } catch (e) { return []; } } function save() { try { localStorage.setItem(KEY, JSON.stringify(tasks)); } catch (e) {} } function uid() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 8); } function el(tag, cls, text) { var n = document.createElement(tag); if (cls) n.className = cls; if (text != null) n.textContent = text; return n; } form.addEventListener("submit", function (e) { e.preventDefault(); var title = titleIn.value.trim(); if (!title) { titleErr.hidden = false; titleIn.setAttribute("aria-invalid", "true"); titleIn.focus(); return; } titleErr.hidden = true; titleIn.removeAttribute("aria-invalid"); tasks.unshift({ id: uid(), title: title, notes: notesIn.value.trim(), priority: prioIn.value, due: dueIn.value, completed: false }); save(); form.reset(); prioIn.value = "medium"; titleIn.focus(); render(); }); [searchIn, statusSel, prioSel].forEach(function (c) { c.addEventListener("input", render); c.addEventListener("change", render); }); function matches(t) { var q = searchIn.value.trim().toLowerCase(); if (q && t.title.toLowerCase().indexOf(q) < 0 && t.notes.toLowerCase().indexOf(q) < 0) return false; var s = statusSel.value; if (s === "active" && t.completed) return false; if (s === "completed" && !t.completed) return false; var p = prioSel.value; if (p !== "all" && t.priority !== p) return false; return true; } function todayStr() { var d = new Date(); return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0"); } function render() { listEl.textContent = ""; var shown = tasks.filter(matches); shown.forEach(function (t) { listEl.appendChild(editingId === t.id ? editItem(t) : viewItem(t)); }); emptyEl.hidden = shown.length > 0; $("count-total").textContent = tasks.length; $("count-active").textContent = tasks.filter(function (t) { return !t.completed; }).length; $("count-completed").textContent = tasks.filter(function (t) { return t.completed; }).length; } function viewItem(t) { var li = el("li", "task-item" + (t.completed ? " done" : "")); li.setAttribute("data-testid", "task-item"); var cb = document.createElement("input"); cb.type = "checkbox"; cb.className = "task-check"; cb.checked = t.completed; cb.setAttribute("data-testid", "task-toggle"); cb.setAttribute("aria-label", "Mark “" + t.title + "” as " + (t.completed ? "active" : "completed")); cb.addEventListener("change", function () { t.completed = cb.checked; save(); render(); }); var body = el("div", "task-body"); body.appendChild(el("div", "task-title-text", t.title)); if (t.notes) body.appendChild(el("p", "task-notes-text", t.notes)); var meta = el("div", "task-meta"); meta.appendChild(el("span", "badge " + t.priority, t.priority)); if (t.due) { var overdue = !t.completed && t.due < todayStr(); meta.appendChild(el("span", "due" + (overdue ? " overdue" : ""), "Due " + t.due + (overdue ? " (overdue)" : ""))); } body.appendChild(meta); var actions = el("div", "task-actions"); var editBtn = el("button", "btn ghost", "Edit"); editBtn.type = "button"; editBtn.setAttribute("data-testid", "task-edit"); editBtn.setAttribute("aria-label", "Edit “" + t.title + "”"); editBtn.addEventListener("click", function () { editingId = t.id; render(); }); var delBtn = el("button", "btn danger", "Delete"); delBtn.type = "button"; delBtn.setAttribute("data-testid", "task-delete"); delBtn.setAttribute("aria-label", "Delete “" + t.title + "”"); delBtn.addEventListener("click", function () { tasks = tasks.filter(function (x) { return x.id !== t.id; }); if (editingId === t.id) editingId = null; save(); render(); }); actions.appendChild(editBtn); actions.appendChild(delBtn); li.appendChild(cb); li.appendChild(body); li.appendChild(actions); return li; } function editItem(t) { var li = el("li", "task-item"); li.setAttribute("data-testid", "task-item"); var f = el("form", "edit-form"); var titleField = el("div", "field"); var tl = el("label", null, "Title"); tl.setAttribute("for", "edit-title-" + t.id); var ti = document.createElement("input"); ti.id = "edit-title-" + t.id; ti.type = "text"; ti.value = t.title; ti.required = true; ti.maxLength = 200; ti.setAttribute("data-testid", "edit-title"); titleField.appendChild(tl); titleField.appendChild(ti); var notesField = el("div", "field"); var nl = el("label", null, "Notes"); nl.setAttribute("for", "edit-notes-" + t.id); var ni = document.createElement("textarea"); ni.id = "edit-notes-" + t.id; ni.rows = 2; ni.value = t.notes; ni.maxLength = 1000; notesField.appendChild(nl); notesField.appendChild(ni); var row = el("div", "edit-row"); var pf = el("div", "field"); var pl = el("label", null, "Priority"); pl.setAttribute("for", "edit-prio-" + t.id); var ps = document.createElement("select"); ps.id = "edit-prio-" + t.id; ["low", "medium", "high"].forEach(function (v) { var o = document.createElement("option"); o.value = v; o.textContent = v[0].toUpperCase() + v.slice(1); if (t.priority === v) o.selected = true; ps.appendChild(o); }); pf.appendChild(pl); pf.appendChild(ps); var df = el("div", "field"); var dl = el("label", null, "Due date"); dl.setAttribute("for", "edit-due-" + t.id); var di = document.createElement("input"); di.id = "edit-due-" + t.id; di.type = "date"; di.value = t.due; df.appendChild(dl); df.appendChild(di); row.appendChild(pf); row.appendChild(df); var acts = el("div", "edit-actions"); var saveBtn = el("button", "btn primary", "Save"); saveBtn.type = "submit"; saveBtn.style.width = "auto"; saveBtn.setAttribute("data-testid", "save-edit"); var cancelBtn = el("button", "btn ghost", "Cancel"); cancelBtn.type = "button"; cancelBtn.addEventListener("click", function () { editingId = null; render(); }); acts.appendChild(saveBtn); acts.appendChild(cancelBtn); f.appendChild(titleField); f.appendChild(notesField); f.appendChild(row); f.appendChild(acts); f.addEventListener("submit", function (e) { e.preventDefault(); var v = ti.value.trim(); if (!v) { ti.focus(); return; } t.title = v; t.notes = ni.value.trim(); t.priority = ps.value; t.due = di.value; editingId = null; save(); render(); }); li.appendChild(f); return li; } render(); })();