diff options
| author | pack <pack@packgekko.xyz> | 2026-08-12 17:19:51 +0000 |
|---|---|---|
| committer | pack <pack@packgekko.xyz> | 2026-08-12 17:19:51 +0000 |
| commit | 647f2c663aaf756bee8474e75dff72422ba240c5 (patch) | |
| tree | 7cd5b15024fb7c64cd8dd0bbcc9c7e96466105e1 /public/app.js | |
| parent | 5611527e313c4add16c432e5cf9d55a987558c02 (diff) | |
| download | crud-647f2c663aaf756bee8474e75dff72422ba240c5.tar.gz | |
final commit IIking
forgot to add 'public/' as well.
Diffstat (limited to 'public/app.js')
| -rw-r--r-- | public/app.js | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/public/app.js b/public/app.js new file mode 100644 index 0000000..a943f85 --- /dev/null +++ b/public/app.js @@ -0,0 +1,167 @@ +/* Auth. */ +function switchTab(tab) +{ + const loginForm = document.getElementById('login-form'); + const registerForm = document.getElementById('register-form'); + const loginTab = document.getElementById('login-tab'); + const registerTab = document.getElementById('register-tab'); + if (tab === 'login') + { + loginForm.style.display = 'flex'; + registerForm.style.display = 'none'; + loginTab.classList.add('active'); + registerTab.classList.remove('active'); + } + else + { + loginForm.style.display = 'none'; + registerForm.style.display = 'flex'; + loginTab.classList.remove('active'); + registerTab.classList.add('active'); + } + document.getElementById('auth-error').textContent = ''; +} + +async function register(e) +{ + e.preventDefault(); + const username = document.getElementById('reg-username').value; + const password = document.getElementById('reg-password').value; + const res = await fetch('/auth/register', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, password }) + }); + if (res.ok) + { + document.getElementById('auth-error').textContent = ''; + showTasks(); + } + else + { + const data = await res.json(); + document.getElementById('auth-error').textContent = data.error || 'Registration failed'; + } +} + +async function login(e) +{ + e.preventDefault(); + const username = document.getElementById('login-username').value; + const password = document.getElementById('login-password').value; + const res = await fetch('/auth/login', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, password }) + }); + if (res.ok) + { + document.getElementById('auth-error').textContent = ''; + showTasks(); + } + else + { + const data = await res.json(); + document.getElementById('auth-error').textContent = data.error || 'Login failed'; + } +} + +async function logout() +{ + await fetch('/auth/logout', { method: 'POST' }); + document.getElementById('tasks-section').style.display = 'none'; + document.getElementById('auth-section').style.display = 'block'; +} + +/* Tasks. */ +async function showTasks() +{ + document.getElementById('auth-section').style.display = 'none'; + document.getElementById('tasks-section').style.display = 'block'; + await loadTasks(); +} + +async function loadTasks() +{ + const res = await fetch('/tasks'); + const tasks = await res.json(); + const list = document.getElementById('task-list'); + if (tasks.length === 0) + { + list.innerHTML = '<p class="empty-msg">No tasks yet. Add one above!</p>'; + return; + } + list.innerHTML = tasks.map(task => ` + <li class="task-item"> + <input type="checkbox" ${task.done ? 'checked' : ''} onchange="toggleDone(${task.id}, this.checked)"> + <div class="task-info"> + <div class="task-title ${task.done ? 'done' : ''}">${escapeHtml(task.title)}</div> + ${task.description ? `<div class="task-desc">${escapeHtml(task.description)}</div>` : ''} + </div> + <button class="delete-btn" onclick="deleteTask(${task.id})" title="Delete">×</button> + </li> + `).join(''); +} + +async function createTask(e) +{ + e.preventDefault(); + const title = document.getElementById('task-title').value; + const description = document.getElementById('task-desc').value; + const res = await fetch('/tasks', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ title, description }) + }); + if (res.ok) + { + document.getElementById('task-title').value = ''; + document.getElementById('task-desc').value = ''; + await loadTasks(); + } +} + +async function toggleDone(id, done) +{ + const res = await fetch(`/tasks/${id}`, + { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ done: done ? 1 : 0 }) + }); + if (res.ok) + { + await loadTasks(); + } +} + +async function deleteTask(id) +{ + const res = await fetch(`/tasks/${id}`, { method: 'DELETE' }); + if (res.ok) + { + await loadTasks(); + } +} + +/* Helpers. */ +function escapeHtml(str) +{ + const div = document.createElement('div'); + div.textContent = str; + return div.innerHTML; +} + +/* Init. */ +/* Check if already logged in on page load. */ +(async function init() +{ + const res = await fetch('/auth/check'); + if (res.ok) + { + showTasks(); + } +})(); |