151 lines
4.1 KiB
JavaScript
151 lines
4.1 KiB
JavaScript
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 = '<div class="empty">No tasks yet.</div>';
|
|
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();
|
|
}
|