aboutsummaryrefslogtreecommitdiffstats
path: root/public/app.js
diff options
context:
space:
mode:
authorpack <pack@packgekko.xyz>2026-08-12 17:19:51 +0000
committerpack <pack@packgekko.xyz>2026-08-12 17:19:51 +0000
commit647f2c663aaf756bee8474e75dff72422ba240c5 (patch)
tree7cd5b15024fb7c64cd8dd0bbcc9c7e96466105e1 /public/app.js
parent5611527e313c4add16c432e5cf9d55a987558c02 (diff)
downloadcrud-king.tar.gz
final commit IIking
forgot to add 'public/' as well.
Diffstat (limited to '')
-rw-r--r--public/app.js167
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">&times;</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();
+ }
+})();