const API_BASE = 'http://localhost:8080/api/v1'; let token = ''; const sessionState = document.getElementById('sessionState'); const loginBtn = document.getElementById('loginBtn'); const createForm = document.getElementById('createForm'); const taskList = document.getElementById('taskList'); const refreshBtn = document.getElementById('refreshBtn'); const clearBtn = document.getElementById('clearBtn'); const template = document.getElementById('taskTemplate'); function setSessionState(text) { sessionState.textContent = text; } function getHeaders() { return { 'Content-Type': 'application/json', Authorization: token ? `Bearer ${token}` : '', }; } async function login() { const response = await fetch(`${API_BASE}/auth/login`, { method: 'POST', }); if (!response.ok) { setSessionState('Login failed'); return; } const data = await response.json(); token = data.token || 'demo-token'; setSessionState('Connected'); await loadTasks(); } function buildMeta(task) { const parts = []; if (task.status) parts.push(task.status.toUpperCase()); if (task.due_at) parts.push(`Due: ${task.due_at}`); if (task.priority) parts.push(`P${task.priority}`); if (task.tags && task.tags.length > 0) parts.push(task.tags.join(', ')); return parts.join(' • '); } function renderTasks(tasks) { taskList.innerHTML = ''; if (!tasks || tasks.length === 0) { taskList.innerHTML = '
No tasks yet.
'; return; } tasks.forEach((task) => { const node = template.content.cloneNode(true); node.querySelector('h3').textContent = task.title || 'Untitled task'; node.querySelector('.meta').textContent = buildMeta(task); node.querySelector('.desc').textContent = task.description || ''; node.querySelector('.badge').textContent = task.status || 'todo'; node.querySelector('.toggle').addEventListener('click', () => toggleStatus(task)); node.querySelector('.delete').addEventListener('click', () => deleteTask(task)); taskList.appendChild(node); }); } async function loadTasks() { const response = await fetch(`${API_BASE}/tasks`, { headers: getHeaders(), }); if (!response.ok) { setSessionState('Auth required'); renderTasks([]); return; } const data = await response.json(); renderTasks(data); } async function createTask(event) { event.preventDefault(); const form = new FormData(createForm); const payload = { title: form.get('title'), description: form.get('description'), due_at: toISO(form.get('due_at')), priority: Number(form.get('priority') || 0), tags: String(form.get('tags') || '') .split(',') .map((tag) => tag.trim()) .filter(Boolean), }; const response = await fetch(`${API_BASE}/tasks`, { method: 'POST', headers: getHeaders(), body: JSON.stringify(payload), }); if (!response.ok) { setSessionState('Failed to create task'); return; } createForm.reset(); await loadTasks(); } async function toggleStatus(task) { const nextStatus = task.status === 'done' ? 'todo' : 'done'; await fetch(`${API_BASE}/tasks/${task.id}`, { method: 'PUT', headers: getHeaders(), body: JSON.stringify({ status: nextStatus }), }); await loadTasks(); } async function deleteTask(task) { await fetch(`${API_BASE}/tasks/${task.id}`, { method: 'DELETE', headers: getHeaders(), }); await loadTasks(); } async function clearCompleted() { const response = await fetch(`${API_BASE}/api/tasks`, { headers: getHeaders(), }); if (!response.ok) { return; } const tasks = await response.json(); const completed = tasks.filter((task) => task.status === 'done'); for (const task of completed) { await deleteTask(task); } } loginBtn.addEventListener('click', login); createForm.addEventListener('submit', createTask); refreshBtn.addEventListener('click', loadTasks); clearBtn.addEventListener('click', clearCompleted); setSessionState('Not connected'); function toISO(value) { if (!value) return ''; const date = new Date(value); if (Number.isNaN(date.getTime())) return ''; return date.toISOString(); }