let globalBlur = false; let currentDateFilter = "all"; // 'all' | 'today' let currentStatusFilter = "all"; // 'all' | 'pending' | 'completed' | 'delayed' | 'pend_delay' let editingId = null; async function fetchTodos() { const res = await fetch("/api/todos"); return res.json(); } let searchTitle = ""; let searchDescription = ""; let searchBoth = ""; function autoResize(el) { el.style.height = "auto"; el.style.height = el.scrollHeight + "px"; } document.getElementById("todoText").addEventListener("input", (e) => { autoResize(e.target); }); document.getElementById("searchTitle").addEventListener("input", (e) => { searchTitle = e.target.value.toLowerCase(); renderTodos(); }); document.getElementById("searchDescription").addEventListener("input", (e) => { searchDescription = e.target.value.toLowerCase(); renderTodos(); }); document.getElementById("searchBoth").addEventListener("input", (e) => { searchBoth = e.target.value.toLowerCase(); renderTodos(); }); function toggleSidebar() { const sidebar = document.getElementById("sidebar"); sidebar.classList.toggle("collapsed"); const icon = document.getElementById("collapseIcon"); icon.textContent = sidebar.classList.contains("collapsed") ? "▶" : "◀"; } function toggleDarkMode() { document.body.classList.toggle("dark"); } // Sidebar "Today / All" buttons function filterTodos(type) { currentDateFilter = type; renderTodos(); } // Status dropdown function filterByStatus(status) { currentStatusFilter = status; renderTodos(); } async function toggleGlobalBlur() { globalBlur = !globalBlur; const todos = await fetchTodos(); // Wait for ALL updates to actually finish before re-rendering await Promise.all( todos.map((todo) => updateTodo(todo.id, { private: globalBlur }, { render: false }), ), ); renderTodos(); } async function deleteTodo(id) { await fetch(`/api/todos/${id}`, { method: "DELETE" }); renderTodos(); } async function updateTodo(id, data, { render = true } = {}) { await fetch(`/api/todos/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); if (render) await renderTodos(); } document.getElementById("todoForm").addEventListener("submit", async (e) => { e.preventDefault(); const today = new Date().toISOString().split("T")[0]; const newTodo = { title: document.getElementById("todoTitle").value || "", text: document.getElementById("todoText").value, date: today, status: "pending", private: globalBlur, }; await fetch("/api/todos", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newTodo), }); e.target.reset(); document.getElementById("todoText").style.height = "auto"; // 👈 add this renderTodos(); document.getElementById("todoText").focus(); }); function startEdit(id) { editingId = id; renderTodos(); } function cancelEdit() { editingId = null; renderTodos(); } async function saveEdit(id) { const title = document.getElementById(`editTitle-${id}`).value; const text = document.getElementById(`editText-${id}`).value; editingId = null; await updateTodo(id, { title, text }); } // Prevent broken HTML if the todo text/title contains quotes, <, etc. function escapeHtml(str) { return str.replace(/&/g, "&").replace(//g, ">"); } function escapeAttr(str) { return escapeHtml(str).replace(/"/g, """); } function renderTodoCard(todo) { if (editingId === todo.id) { return `
${escapeHtml(todo.text || "")}
${todo.date} - ${todo.status}Today
Total Today: ${todayTodos.length}
Completed Today: ${completedToday}
pending+delayed Today: ${pend_delay}
Pending Today: ${pendingToday}
Delayed Today: ${delayedToday}
Overall
All Total: ${todos.length}
All Completed: ${allCompleted}
All pending+delayed: ${all_pend_delay}
All Pending: ${allPending}
All Delayed: ${allDelayed}
`; } renderTodos(); window.addEventListener("DOMContentLoaded", async () => { // ✅ add async document.getElementById("sidebar").classList.add("collapsed"); document.body.classList.add("dark"); document.getElementById("todoText").focus(); const todos = await fetchTodos(); globalBlur = todos.length > 0 && todos.every((t) => t.private); renderTodos(); // also make sure the UI actually reflects restored state });