todo-list/public/index.html
2026-06-11 18:49:05 +00:00

216 lines
5.1 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo List</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f0f2f5;
color: #1a1a2e;
display: flex;
justify-content: center;
padding: 40px 16px;
min-height: 100vh;
}
.app {
width: 100%;
max-width: 520px;
}
h1 {
font-size: 2rem;
font-weight: 700;
margin-bottom: 24px;
text-align: center;
}
.input-row {
display: flex;
gap: 8px;
margin-bottom: 24px;
}
.input-row input {
flex: 1;
padding: 12px 16px;
font-size: 1rem;
border: 2px solid #d1d5db;
border-radius: 8px;
outline: none;
transition: border-color 0.2s;
}
.input-row input:focus {
border-color: #6366f1;
}
.input-row button {
padding: 12px 20px;
font-size: 1rem;
font-weight: 600;
background: #6366f1;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background 0.2s;
}
.input-row button:hover { background: #4f46e5; }
.todo-list {
list-style: none;
display: flex;
flex-direction: column;
gap: 8px;
}
.todo-item {
display: flex;
align-items: center;
gap: 12px;
background: white;
padding: 14px 16px;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
transition: opacity 0.2s;
}
.todo-item.done .todo-text {
text-decoration: line-through;
color: #9ca3af;
}
.todo-item input[type="checkbox"] {
width: 20px;
height: 20px;
accent-color: #6366f1;
cursor: pointer;
flex-shrink: 0;
}
.todo-text {
flex: 1;
font-size: 1rem;
word-break: break-word;
}
.todo-date {
font-size: 0.75rem;
color: #9ca3af;
white-space: nowrap;
}
.todo-item .delete-btn {
background: none;
border: none;
color: #ef4444;
font-size: 1.2rem;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
transition: background 0.2s;
flex-shrink: 0;
}
.todo-item .delete-btn:hover {
background: #fef2f2;
}
.empty {
text-align: center;
color: #9ca3af;
padding: 40px 0;
font-size: 1rem;
}
</style>
</head>
<body>
<div class="app">
<h1>✅ Todo List</h1>
<form class="input-row" id="addForm">
<input type="text" id="todoInput" placeholder="What needs to be done?" autocomplete="off" required>
<button type="submit">Add</button>
</form>
<ul class="todo-list" id="todoList"></ul>
<p class="empty" id="emptyMsg" style="display:none">No todos yet — add one above!</p>
</div>
<script>
const API = '/api/todos';
const todoList = document.getElementById('todoList');
const addForm = document.getElementById('addForm');
const todoInput = document.getElementById('todoInput');
const emptyMsg = document.getElementById('emptyMsg');
async function loadTodos() {
const res = await fetch(API);
const todos = await res.json();
todoList.innerHTML = '';
if (todos.length === 0) {
emptyMsg.style.display = 'block';
} else {
emptyMsg.style.display = 'none';
todos.forEach(t => {
const li = document.createElement('li');
li.className = 'todo-item' + (t.done ? ' done' : '');
li.dataset.id = t.id;
const date = new Date(t.created_at).toLocaleDateString(undefined, {
month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'
});
li.innerHTML = `
<input type="checkbox" ${t.done ? 'checked' : ''} />
<span class="todo-text"></span>
<span class="todo-date">${date}</span>
<button class="delete-btn" title="Delete">✕</button>
`;
li.querySelector('.todo-text').textContent = t.text;
li.querySelector('input[type="checkbox"]').addEventListener('change', () => toggleTodo(t.id));
li.querySelector('.delete-btn').addEventListener('click', () => deleteTodo(t.id));
todoList.appendChild(li);
});
}
}
async function addTodo(text) {
await fetch(API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
loadTodos();
}
async function toggleTodo(id) {
await fetch(`${API}/${id}/toggle`, { method: 'PUT' });
loadTodos();
}
async function deleteTodo(id) {
await fetch(`${API}/${id}`, { method: 'DELETE' });
loadTodos();
}
addForm.addEventListener('submit', (e) => {
e.preventDefault();
const text = todoInput.value.trim();
if (text) {
addTodo(text);
todoInput.value = '';
todoInput.focus();
}
});
loadTodos();
</script>
</body>
</html>