diff --git a/Sprint-3/todo-list/index.html b/Sprint-3/todo-list/index.html
index 4d12c4654..97fac1970 100644
--- a/Sprint-3/todo-list/index.html
+++ b/Sprint-3/todo-list/index.html
@@ -14,12 +14,16 @@
+
+
diff --git a/Sprint-3/todo-list/package.json b/Sprint-3/todo-list/package.json
index ce181158a..5389baf9e 100644
--- a/Sprint-3/todo-list/package.json
+++ b/Sprint-3/todo-list/package.json
@@ -18,6 +18,6 @@
"homepage": "https://github.com/CodeYourFuture/CYF-Coursework-Template#readme",
"devDependencies": {
"http-server": "^14.1.1",
- "jest": "^30.0.4"
+ "jest": "^30.4.2"
}
}
diff --git a/Sprint-3/todo-list/script.mjs b/Sprint-3/todo-list/script.mjs
index ba0b2ceae..bd28dabe5 100644
--- a/Sprint-3/todo-list/script.mjs
+++ b/Sprint-3/todo-list/script.mjs
@@ -7,10 +7,14 @@ 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", deleteCompletedTodos);
+
// Populate sample data
- Todos.addTask(todos, "Wash the dishes", false);
- Todos.addTask(todos, "Do the shopping", true);
+ Todos.addTask(todos, "Wash the dishes", false, null);
+ Todos.addTask(todos, "Do the shopping", true, "2026-07-30");
+
render();
});
@@ -20,15 +24,31 @@ window.addEventListener("load", () => {
// append a new task to the todo list.
function addNewTodo() {
const taskInput = document.getElementById("new-task-input");
+ const deadlineInput = document.getElementById("new-task-deadline");
+
const task = taskInput.value.trim();
+ const deadline = deadlineInput.value || null;
+
if (task) {
- Todos.addTask(todos, task, false);
+ Todos.addTask(todos, task, false, deadline);
render();
}
taskInput.value = "";
+ deadlineInput.value = "";
+}
+
+function deleteCompletedTodos() {
+ const newTodos = Todos.deleteCompleted(todos);
+
+ // Replace the old array contents
+ todos.length = 0;
+ todos.push(...newTodos);
+
+ render();
}
+
// Note:
// - Store the reference to the
element with id "todo-list" here
// to avoid querying the DOM repeatedly inside render().
@@ -52,6 +72,28 @@ function render() {
// - This variable is declared here to be close to the only function that uses it.
const todoListItemTemplate =
document.getElementById("todo-item-template").content.firstElementChild;
+ function getDaysRemainingText(deadline) {
+ if (!deadline) return "";
+
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+
+ const deadlineDate = new Date(deadline);
+ deadlineDate.setHours(0, 0, 0, 0);
+
+ const msPerDay = 1000 * 60 * 60 * 24;
+ const diffDays = Math.round((deadlineDate - today) / msPerDay);
+
+ if (diffDays === 0) {
+ return "Due today";
+ } else if (diffDays > 0) {
+ return `${diffDays} day${diffDays === 1 ? "" : "s"} left`;
+ } else {
+ const overdueDays = Math.abs(diffDays);
+ return `Overdue by ${overdueDays} day${overdueDays === 1 ? "" : "s"}`;
+ }
+ }
+
// Create a - element for the given todo task
function createListItem(todo, index) {
@@ -61,6 +103,13 @@ function createListItem(todo, index) {
if (todo.completed) {
li.classList.add("completed");
}
+ const deadlineEl = li.querySelector(".deadline");
+ if (todo.deadline) {
+ deadlineEl.textContent = getDaysRemainingText(todo.deadline);
+ } else {
+ deadlineEl.textContent = "";
+ }
+
li.querySelector('.complete-btn').addEventListener("click", () => {
Todos.toggleCompletedOnTask(todos, index);
diff --git a/Sprint-3/todo-list/todos.mjs b/Sprint-3/todo-list/todos.mjs
index f17ab6a25..f1619c86c 100644
--- a/Sprint-3/todo-list/todos.mjs
+++ b/Sprint-3/todo-list/todos.mjs
@@ -10,10 +10,11 @@
*/
// 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
export function deleteTask(todos, taskIndex) {
if (todos[taskIndex]) {
@@ -26,4 +27,7 @@ export function toggleCompletedOnTask(todos, taskIndex) {
if (todos[taskIndex]) {
todos[taskIndex].completed = !todos[taskIndex].completed;
}
-}
\ No newline at end of file
+}
+export function deleteCompleted(todos) {
+ return todos.filter((todo) => !todo.completed);
+}
diff --git a/Sprint-3/todo-list/todos.test.mjs b/Sprint-3/todo-list/todos.test.mjs
index bae7ae491..d09e3446b 100644
--- a/Sprint-3/todo-list/todos.test.mjs
+++ b/Sprint-3/todo-list/todos.test.mjs
@@ -18,14 +18,13 @@ function createMockTodos() {
}
// 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", () => {
let todos = [];
- Todos.addTask(todos, theTask.task, theTask.completed);
- expect(todos).toHaveLength(1);
- expect(todos[0]).toEqual(theTask);
+ Todos.addTask(todos, theTask.task, theTask.completed);
+ expect(todos[todos.length - 1]).toEqual(theTask);
});
test("Should append a new task to the end of a ToDo list", () => {
@@ -130,3 +129,16 @@ describe("toggleCompletedOnTask()", () => {
});
});
+
+
+test("deleteCompleted removes all completed tasks", () => {
+ const todos = [
+ { task: "A", completed: true },
+ { task: "B", completed: false },
+ { task: "C", completed: true },
+ ];
+
+ const result = Todos.deleteCompleted(todos);
+
+ expect(result).toEqual([{ task: "B", completed: false }]);
+});