/** * api.js * * Powers the Dashboard page. Talks to the Express / Mongoose backend * at http://localhost:3001 to create, read, update and delete tasks. * * Core CRUD (original): * - displayTasks() GET /api/tasks * - createNewTask() POST /api/tasks/todo * - completeTask() PATCH /api/tasks/complete/:id * - taskNotCompleted() PATCH /api/tasks/notComplete/:id * - deleteTask() DELETE /api/tasks/delete/:id * - editTask() PUT /api/tasks/update/:id * * Added interactivity: * - Stats cards: renders live counts of Active, Completed, Overdue * and Due-Today tasks (see updateStats()). * - Search filter: an input box filters the rendered task lists by * title/description on every keystroke, without re-hitting the * API (see renderTasks()). * - Quick-pick date buttons: "Today", "Tomorrow" and "Next week" * populate the due-date field of the new-task form. * * The in-memory `allTasks` array holds the latest fetched list so * search filtering can be recomputed purely on the client side. */ const taskForm = document.getElementById('taskForm'); const toDoList = document.getElementById('toDoList'); const completedList = document.getElementById('completedList'); const toDoEmpty = document.getElementById('toDoEmpty'); const completedEmpty = document.getElementById('completedEmpty'); const searchInput = document.getElementById('searchInput'); const dueDateInput = document.getElementById('dueDate'); const url = "http://localhost:3001"; // Cache of the most recent /api/tasks response so the search filter // can re-render instantly without refetching. let allTasks = []; function resetForm() { taskForm.reset(); } const sortButton = document.getElementById("sortSelect"); window.addEventListener("DOMContentLoaded", () => { sortButton.value = "default"; }); sortButton.addEventListener('change', () => { displayTasks(); }); // Search filter: re-renders the two lists on every keystroke using // the cached `allTasks` array. No API round-trip required. if (searchInput) { searchInput.addEventListener('input', () => { renderTasks(); }); } // Quick-pick date buttons: set the due-date input to today, tomorrow // or a week from now. Saves the user from having to open the date // picker for the most common options. document.querySelectorAll('.quick-date-btn').forEach(btn => { btn.addEventListener('click', () => { const offsetDays = { today: 0, tomorrow: 1, nextWeek: 7 }[btn.dataset.quick] ?? 0; const date = new Date(); date.setDate(date.getDate() + offsetDays); dueDateInput.value = date.toISOString().split('T')[0]; }); }); window.addEventListener("DOMContentLoaded", () => { displayTasks(); }); taskForm.addEventListener("submit", (event) => { event.preventDefault(); createNewTask(); }); [toDoList, completedList].forEach(list => { list.addEventListener('click', (event) => { if (event.target.classList.contains("done")) { const taskId = event.target.getAttribute("data-id"); completeTask(taskId); } else if (event.target.classList.contains("notDone")) { const taskId = event.target.getAttribute("data-id"); taskNotCompleted(taskId); } else if (event.target.classList.contains("delete")) { const taskId = event.target.getAttribute("data-id"); deleteTask(taskId); } else if (event.target.classList.contains('edit')) { const task = { id: event.target.getAttribute('data-id'), title: event.target.getAttribute('data-title'), description: event.target.getAttribute('data-description'), dueDate: new Date(event.target.getAttribute('data-due-date')) }; const modal = { titleInput: document.getElementById('editTaskName'), descriptionInput: document.getElementById('editTaskDescription'), dueDateInput: document.getElementById('editDueDate'), saveButton: document.getElementById('saveButton') }; modal.titleInput.value = task.title; modal.descriptionInput.value = task.description; modal.dueDateInput.value = task.dueDate.toISOString().split('T')[0]; modal.saveButton.addEventListener('click', async () => { await editTask(task.id); bootstrap.Modal.getInstance(document.getElementById('editModal')).hide(); }, { once: true }); } }); }); async function displayTasks() { const sortSelect = document.getElementById('sortSelect'); const sortBy = sortSelect.value; let query = ''; if (sortBy !== 'default') { query = `?sortBy=${sortBy}`; } try { const response = await fetch(`${url}/api/tasks${query}`); if (!response.ok) { throw new Error(`Failed to get tasks: ${response.status}`); } allTasks = await response.json(); updateStats(allTasks); renderTasks(); resetForm(); } catch (error) { console.error("Error: ", error); } } function formatTask(task) { const li = document.createElement("li"); li.classList.add("card", "p-3", "shadow-sm", "mt-2"); const done = task.completed ? "text-decoration-line-through opacity-50" : ""; li.innerHTML = `
${task.description}
Due: ${new Date(task.dueDate).toLocaleDateString()}
Created on: ${new Date(task.dateCreated).toLocaleDateString()}