From 8887c0a71a3faff3739163230e0bf40c0b30f296 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Tue, 11 Aug 2026 11:16:12 +0100 Subject: [PATCH 1/2] feat: implement mass delete, deadlines, and days remaining for todo list --- Sprint-3/todo-list/index.html | 8 ++++-- Sprint-3/todo-list/script.mjs | 25 ++++++++++++++-- Sprint-3/todo-list/style.css | 32 +++++++++++++++++++-- Sprint-3/todo-list/todos.mjs | 11 ++++++-- Sprint-3/todo-list/todos.test.mjs | 47 +++++++++++++++++++++++++------ 5 files changed, 106 insertions(+), 17 deletions(-) diff --git a/Sprint-3/todo-list/index.html b/Sprint-3/todo-list/index.html index 4d12c4654..fefd05855 100644 --- a/Sprint-3/todo-list/index.html +++ b/Sprint-3/todo-list/index.html @@ -15,12 +15,15 @@

My ToDo List

+
+ + Task description +
- - + +
diff --git a/Sprint-3/todo-list/script.mjs b/Sprint-3/todo-list/script.mjs index ba0b2ceae..6e773ef35 100644 --- a/Sprint-3/todo-list/script.mjs +++ b/Sprint-3/todo-list/script.mjs @@ -7,6 +7,10 @@ const todos = []; // Set up tasks to be performed once on page load window.addEventListener("load", () => { document.getElementById("add-task-btn").addEventListener("click", addNewTodo); + document.getElementById("delete-completed-btn").addEventListener("click", () => { + Todos.deleteCompleted(todos); + render(); + }); // Populate sample data Todos.addTask(todos, "Wash the dishes", false); @@ -20,13 +24,14 @@ window.addEventListener("load", () => { // append a new task to the todo list. function addNewTodo() { const taskInput = document.getElementById("new-task-input"); + const deadlineInput = document.getElementById("deadline-input"); const task = taskInput.value.trim(); if (task) { - Todos.addTask(todos, task, false); + Todos.addTask(todos, task, false, deadlineInput.value || null); render(); } - taskInput.value = ""; + deadlineInput.value = ""; } // Note: @@ -58,6 +63,22 @@ function createListItem(todo, index) { const li = todoListItemTemplate.cloneNode(true); // true => Do a deep copy of the node li.querySelector(".description").textContent = todo.task; + + const deadlineEl = li.querySelector(".deadline"); + if (todo.deadline) { + const today = new Date(); + today.setHours(0, 0, 0, 0); + const due = new Date(todo.deadline + "T00:00:00"); + const daysLeft = Math.round((due - today) / (1000 * 60 * 60 * 24)); + if (daysLeft < 0) { + deadlineEl.textContent = `Overdue by ${Math.abs(daysLeft)} day(s)`; + deadlineEl.classList.add("overdue"); + } else if (daysLeft === 0) { + deadlineEl.textContent = "Due today"; + } else { + deadlineEl.textContent = `${daysLeft} day(s) left`; + } + } if (todo.completed) { li.classList.add("completed"); } diff --git a/Sprint-3/todo-list/style.css b/Sprint-3/todo-list/style.css index 535e91227..7435dff94 100644 --- a/Sprint-3/todo-list/style.css +++ b/Sprint-3/todo-list/style.css @@ -93,11 +93,11 @@ h1 { height: 32px; } -.complete-btn i { +.complete-btn span { color: green; } -.delete-btn i { +.delete-btn span { color: red; } @@ -105,3 +105,31 @@ h1 { text-decoration: line-through; color: gray; } + +#delete-completed-btn { + display: block; + width: 100%; + margin-top: 16px; + padding: 10px; + font-size: 16px; + background-color: #e53935; + color: white; + border: none; + border-radius: 6px; + cursor: pointer; +} + +#delete-completed-btn:hover { + background-color: #c62828; +} + +.deadline { + font-size: 12px; + color: #888; + margin-right: 10px; + white-space: nowrap; +} + +.deadline.overdue { + color: #e53935; +} diff --git a/Sprint-3/todo-list/todos.mjs b/Sprint-3/todo-list/todos.mjs index f17ab6a25..343cfa913 100644 --- a/Sprint-3/todo-list/todos.mjs +++ b/Sprint-3/todo-list/todos.mjs @@ -10,8 +10,8 @@ */ // Append a new task to todos[] -export function addTask(todos, task, completed = false) { - todos.push({ task, completed }); +export function addTask(todos, task, completed = false, deadline = null) { + todos.push({ task, completed, deadline }); } // Delete todos[taskIndex] if it exists @@ -26,4 +26,11 @@ export function toggleCompletedOnTask(todos, taskIndex) { if (todos[taskIndex]) { todos[taskIndex].completed = !todos[taskIndex].completed; } +} + +// Remove all completed tasks from todos[] +export function deleteCompleted(todos) { + for (let i = todos.length - 1; i >= 0; i--) { + if (todos[i].completed) todos.splice(i, 1); + } } \ No newline at end of file diff --git a/Sprint-3/todo-list/todos.test.mjs b/Sprint-3/todo-list/todos.test.mjs index bae7ae491..393e7007e 100644 --- a/Sprint-3/todo-list/todos.test.mjs +++ b/Sprint-3/todo-list/todos.test.mjs @@ -10,15 +10,15 @@ import * as Todos from "./todos.mjs"; // Return a mock ToDo List data with exactly 4 elements. function createMockTodos() { return [ - { task: "Task 1 description", completed: true }, - { task: "Task 2 description", completed: false }, - { task: "Task 3 description", completed: true }, - { task: "Task 4 description", completed: false }, + { task: "Task 1 description", completed: true, deadline: null }, + { task: "Task 2 description", completed: false, deadline: null }, + { task: "Task 3 description", completed: true, deadline: null }, + { task: "Task 4 description", completed: false, deadline: null }, ]; } // A mock task to simulate user input -const theTask = { task: "The Task", completed: false }; +const theTask = { task: "The Task", completed: false, deadline: null }; describe("addTask()", () => { test("Add a task to an empty ToDo list", () => { @@ -29,16 +29,24 @@ describe("addTask()", () => { }); test("Should append a new task to the end of a ToDo list", () => { - const todos = createMockTodos(); const lengthBeforeAddition = todos.length; Todos.addTask(todos, theTask.task, theTask.completed); - // todos should now have one more task expect(todos).toHaveLength(lengthBeforeAddition + 1); - - // New task should be appended to the todos expect(todos[todos.length - 1]).toEqual(theTask); }); + + test("Should store a deadline when provided", () => { + const todos = []; + Todos.addTask(todos, "Task with deadline", false, "2030-01-01"); + expect(todos[0].deadline).toBe("2030-01-01"); + }); + + test("Should default deadline to null when not provided", () => { + const todos = []; + Todos.addTask(todos, "Task without deadline", false); + expect(todos[0].deadline).toBeNull(); + }); }); describe("deleteTask()", () => { @@ -130,3 +138,24 @@ describe("toggleCompletedOnTask()", () => { }); }); +describe("deleteCompleted()", () => { + test("Removes all completed tasks", () => { + const todos = createMockTodos(); // tasks 0 and 2 are completed + Todos.deleteCompleted(todos); + expect(todos).toHaveLength(2); + todos.forEach(t => expect(t.completed).toBe(false)); + }); + + test("Does nothing when no tasks are completed", () => { + const todos = [{ task: "A", completed: false }, { task: "B", completed: false }]; + Todos.deleteCompleted(todos); + expect(todos).toHaveLength(2); + }); + + test("Empties the list when all tasks are completed", () => { + const todos = [{ task: "A", completed: true }, { task: "B", completed: true }]; + Todos.deleteCompleted(todos); + expect(todos).toHaveLength(0); + }); +}); + From c0a3f8fc737693c0c6824475a4b2eab17d156903 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Wed, 12 Aug 2026 10:58:14 +0100 Subject: [PATCH 2/2] refactor: use named function for delete-completed event listener --- Sprint-3/todo-list/script.mjs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Sprint-3/todo-list/script.mjs b/Sprint-3/todo-list/script.mjs index 6e773ef35..e07dc6858 100644 --- a/Sprint-3/todo-list/script.mjs +++ b/Sprint-3/todo-list/script.mjs @@ -7,10 +7,7 @@ const todos = []; // Set up tasks to be performed once on page load window.addEventListener("load", () => { document.getElementById("add-task-btn").addEventListener("click", addNewTodo); - document.getElementById("delete-completed-btn").addEventListener("click", () => { - Todos.deleteCompleted(todos); - render(); - }); + document.getElementById("delete-completed-btn").addEventListener("click", deleteCompleted); // Populate sample data Todos.addTask(todos, "Wash the dishes", false); @@ -20,6 +17,11 @@ window.addEventListener("load", () => { }); +function deleteCompleted() { + Todos.deleteCompleted(todos); + render(); +} + // A callback that reads the task description from an input field and // append a new task to the todo list. function addNewTodo() {