Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions backend/controllers/tasksController.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ function writeTasks(tasks) {
fs.writeFileSync(filePath, JSON.stringify(tasks, null, 2));
}

const VALID_CATEGORIES = ['Low', 'Medium', 'High'];

exports.getAllTasks = (req, res) => {
res.json(readTasks());
};

exports.createTask = (req, res) => {
const tasks = readTasks();
const { title, completed = false } = req.body;
const newTask = { id: uuidv4(), title, completed };
const { title, completed = false, category = 'Medium' } = req.body;
if (!VALID_CATEGORIES.includes(category)) {
return res.status(400).json({ message: `Invalid category. Must be one of: ${VALID_CATEGORIES.join(', ')}` });
}
const newTask = { id: uuidv4(), title, completed, category };
tasks.push(newTask);
writeTasks(tasks);
res.status(201).json(newTask);
Expand All @@ -32,6 +37,12 @@ exports.updateTask = (req, res) => {

task.title = req.body.title ?? task.title;
task.completed = req.body.completed ?? task.completed;
if (req.body.category !== undefined) {
if (!VALID_CATEGORIES.includes(req.body.category)) {
return res.status(400).json({ message: `Invalid category. Must be one of: ${VALID_CATEGORIES.join(', ')}` });
}
task.category = req.body.category;
}
writeTasks(tasks);
res.json(task);
};
Expand Down
5 changes: 5 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ <h1>Task List</h1>
</div>
<form id="taskForm">
<input type="text" id="taskInput" placeholder="New task" required />
<select id="categorySelect">
<option value="Low">Low</option>
<option value="Medium" selected>Medium</option>
<option value="High">High</option>
</select>
Comment on lines +17 to +21

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new <select id="categorySelect"> has no accessible name (no <label> and no aria-label/aria-labelledby). Add an explicit label or an ARIA label so screen readers can announce what this control does.

Copilot uses AI. Check for mistakes.
<button type="submit" title="Click to add a new task to the list">Add Task</button>
</form>
<ul id="taskList"></ul>
Expand Down
34 changes: 25 additions & 9 deletions frontend/script.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const API_URL = 'http://localhost:3000/api/tasks';
const CATEGORIES = ['Low', 'Medium', 'High'];
const DEFAULT_CATEGORY = 'Medium';
Comment on lines 1 to +3

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CATEGORIES is declared but never used. This adds dead code and can cause lint/build failures in stricter setups; either remove it or use it to generate/validate category values in the UI (e.g., populate the <select> or validate categorySelect.value before sending).

Copilot uses AI. Check for mistakes.
const taskList = document.getElementById('taskList');
const taskForm = document.getElementById('taskForm');
const taskInput = document.getElementById('taskInput');
const categorySelect = document.getElementById('categorySelect');
const themeToggle = document.getElementById('themeToggle');

// Theme functionality
function initTheme() {
const savedTheme = localStorage.getItem('theme') || 'light';
Expand Down Expand Up @@ -35,28 +37,42 @@ async function fetchTasks() {
}

function addTaskToDOM(task) {
const category = task.category || DEFAULT_CATEGORY;
const li = document.createElement('li');
li.innerHTML = `
<span class="${task.completed ? 'completed' : ''}">${task.title}</span>
<div>
<button onclick="toggleComplete('${task.id}', ${!task.completed})">✓</button>
<button onclick="deleteTask('${task.id}')">✕</button>
</div>
`;
const titleSpan = document.createElement('span');
titleSpan.className = task.completed ? 'completed' : '';
titleSpan.textContent = task.title;
const categoryBadge = document.createElement('span');
categoryBadge.className = `category-badge category-${category.toLowerCase()}`;
categoryBadge.textContent = category;
const actions = document.createElement('div');
const completeBtn = document.createElement('button');
completeBtn.textContent = '✓';
completeBtn.onclick = () => toggleComplete(task.id, !task.completed);
const deleteBtn = document.createElement('button');
deleteBtn.textContent = '✕';
deleteBtn.onclick = () => deleteTask(task.id);
actions.appendChild(completeBtn);
actions.appendChild(deleteBtn);
li.appendChild(titleSpan);
li.appendChild(categoryBadge);
li.appendChild(actions);
taskList.appendChild(li);
}

taskForm.addEventListener('submit', async e => {
e.preventDefault();
const title = taskInput.value;
const category = categorySelect.value;
const res = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title })
body: JSON.stringify({ title, category })
});
const task = await res.json();
addTaskToDOM(task);
taskInput.value = '';
categorySelect.value = DEFAULT_CATEGORY;
});

async function toggleComplete(id, completed) {
Expand Down
58 changes: 58 additions & 0 deletions frontend/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,61 @@ li button:active {
padding: 0.75em 0.5em;
}
}

#categorySelect {
padding: 0.75em 0.5em;
border: none;
border-radius: 4px;
background: var(--input-bg);
color: var(--text-color);
box-shadow: 0 1px 2px var(--input-shadow);
font-size: 1em;
cursor: pointer;
outline: none;
transition: box-shadow 0.2s;
}

#categorySelect:focus {
box-shadow: 0 2px 8px var(--input-focus-shadow);
}

.category-badge {
font-size: 0.75em;
font-weight: 500;
padding: 0.2em 0.65em;
border-radius: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0 0.5em;

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new category badge is a <span> inside <li>, but the existing global rule li span { flex: 1; } will also apply to .category-badge, causing it to grow/stretch and potentially break the layout. Consider overriding in .category-badge (e.g., ensure it does not flex-grow) or tightening the li span selector to target only the title span.

Suggested change
margin: 0 0.5em;
margin: 0 0.5em;
flex: 0 0 auto;

Copilot uses AI. Check for mistakes.
flex-shrink: 0;
}

.category-low {
background-color: #e8f5e9;
color: #2e7d32;
}

.category-medium {
background-color: #fff3e0;
color: #e65100;
}

.category-high {
background-color: #fce4ec;
color: #c62828;
}

[data-theme="dark"] .category-low {
background-color: #1b5e20;
color: #a5d6a7;
}

[data-theme="dark"] .category-medium {
background-color: #bf360c;
color: #ffcc80;
}

[data-theme="dark"] .category-high {
background-color: #880e4f;
color: #f48fb1;
}
Loading