diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js
index f416823..293f51a 100644
--- a/src/main/resources/static/app.js
+++ b/src/main/resources/static/app.js
@@ -3,11 +3,25 @@ const API_BASE = '/api/tasks';
const taskInput = document.getElementById('taskInput');
const addBtn = document.getElementById('addBtn');
const taskList = document.getElementById('taskList');
+const totalCount = document.getElementById('totalCount');
+const pendingCount = document.getElementById('pendingCount');
+const doneCount = document.getElementById('doneCount');
+
+function updateStats(tasks) {
+ const total = tasks.length;
+ const done = tasks.filter(t => t.completed).length;
+ const pending = total - done;
+
+ totalCount.textContent = total;
+ pendingCount.textContent = pending;
+ doneCount.textContent = done;
+}
async function fetchTasks() {
try {
const response = await fetch(API_BASE);
const tasks = await response.json();
+ updateStats(tasks);
renderTasks(tasks);
} catch (error) {
console.error('Error fetching tasks:', error);
@@ -18,29 +32,64 @@ function renderTasks(tasks) {
taskList.innerHTML = '';
if (tasks.length === 0) {
- taskList.innerHTML = '
No tasks yet. Add one above!
';
+ taskList.innerHTML = `
+
+
/
+ no tasks yet
+
type above to add your first task
+
+ `;
return;
}
- tasks.forEach(task => {
+ // Show pending tasks first, then completed
+ const sorted = [...tasks].sort((a, b) => {
+ if (a.completed !== b.completed) return a.completed ? 1 : -1;
+ return 0;
+ });
+
+ sorted.forEach((task, index) => {
const li = document.createElement('li');
li.className = `task-item ${task.completed ? 'completed' : ''}`;
+ li.style.animationDelay = `${index * 0.04}s`;
+
+ const indexSpan = document.createElement('span');
+ indexSpan.className = 'task-index';
+ indexSpan.textContent = String(index + 1).padStart(2, '0');
+
+ const checkboxWrapper = document.createElement('label');
+ checkboxWrapper.className = 'checkbox-wrapper';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = task.completed;
checkbox.addEventListener('change', () => toggleTask(task));
- const span = document.createElement('span');
- span.textContent = task.name;
+ const checkboxCustom = document.createElement('span');
+ checkboxCustom.className = 'checkbox-custom';
+ checkboxCustom.innerHTML = ``;
+
+ checkboxWrapper.appendChild(checkbox);
+ checkboxWrapper.appendChild(checkboxCustom);
+
+ const nameSpan = document.createElement('span');
+ nameSpan.className = 'task-name';
+ nameSpan.textContent = task.name;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
- deleteBtn.textContent = 'Delete';
- deleteBtn.addEventListener('click', () => deleteTask(task.id));
+ deleteBtn.textContent = 'del';
+ deleteBtn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ li.style.transition = 'all 0.3s ease';
+ li.style.opacity = '0';
+ li.style.transform = 'translateX(20px)';
+ setTimeout(() => deleteTask(task.id), 250);
+ });
- li.appendChild(checkbox);
- li.appendChild(span);
+ li.appendChild(indexSpan);
+ li.appendChild(checkboxWrapper);
+ li.appendChild(nameSpan);
li.appendChild(deleteBtn);
taskList.appendChild(li);
});
@@ -48,7 +97,13 @@ function renderTasks(tasks) {
async function addTask() {
const name = taskInput.value.trim();
- if (!name) return;
+ if (!name) {
+ // Subtle shake on empty submit
+ taskInput.parentElement.style.animation = 'none';
+ taskInput.parentElement.offsetHeight; // reflow
+ taskInput.parentElement.style.animation = 'shake 0.4s ease';
+ return;
+ }
try {
const response = await fetch(API_BASE, {
@@ -103,6 +158,19 @@ async function deleteTask(id) {
}
}
+// Add shake animation dynamically
+const styleEl = document.createElement('style');
+styleEl.textContent = `
+ @keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 20% { transform: translateX(-4px); }
+ 40% { transform: translateX(4px); }
+ 60% { transform: translateX(-3px); }
+ 80% { transform: translateX(2px); }
+ }
+`;
+document.head.appendChild(styleEl);
+
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
@@ -110,4 +178,9 @@ taskInput.addEventListener('keypress', (e) => {
}
});
+// Focus input on page load
+window.addEventListener('load', () => {
+ taskInput.focus();
+});
+
fetchTasks();
diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html
index 4bee4ff..b268439 100644
--- a/src/main/resources/static/index.html
+++ b/src/main/resources/static/index.html
@@ -3,8 +3,28 @@
- Todo App
+ todo.
+
+
+
-