diff --git a/public/js/app.js b/public/js/app.js index e76b1042dcc3e03f55e57e2bf9d7d4f958a95566..f3ea0875e95bdab5d388e2cf53c19b52f9a03275 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -14,4 +14,127 @@ document.getElementById('logout-btn').addEventListener('click', async () => { } catch (err) { console.error('Error during logout:', err); } -}); \ No newline at end of file +}); + +// Load every task when the page is initialzed +document.addEventListener('DOMContentLoaded', async () => { + try { + const res = await fetch('/api/todos'); + const todos = await res.json(); + renderTasks(todos); + } catch (err) { + console.error('Error loading tasks:', err); + } +}); + +// Function to render tasks in the DOM +function renderTasks(todos) { + taskList.innerHTML = ''; + todos.forEach(todo => { + const taskItem = createTaskElement(todo); + taskList.appendChild(taskItem); + }); +} + +// Create an element in the DOM +function createTaskElement(todo) { + const li = document.createElement('li'); + li.className = 'task'; + li.dataset.id = todo._id; + if (todo.isDone) { + li.classList.add('completed'); + } + li.innerHTML = ` + <p>${todo.description}</p> + <div class="task-actions"> + <button class="hide-btn" data-id="${todo._id}">Done</button> + <button class="edit-btn" data-id="${todo._id}">Edit</button> + <button class="delete-btn" data-id="${todo._id}">Delete</button> + </div> + `; + + // Button Button assigns + li.querySelector('.hide-btn').addEventListener('click', () => markAsDone(todo._id, todo.isDone)); + li.querySelector('.delete-btn').addEventListener('click', () => deleteTask(todo._id)); + li.querySelector('.edit-btn').addEventListener('click', () => editTask(todo)); + + return li; +} + +// Add new task +newTaskForm.addEventListener('submit', async (e) => { + e.preventDefault(); + const input = newTaskForm.querySelector('input[name="task"]'); + const description = input.value.trim(); + + if (description === '') return; + + try { + const res = await fetch('/api/todos', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ description }) + }); + + const newTodo = await res.json(); + taskList.appendChild(createTaskElement(newTodo)); + input.value = ''; // Limpiar el campo de entrada + } catch (err) { + console.error('Error adding task:', err); + } +}); + +// Mark task as completed +async function markAsDone(taskId, isDone) { + try { + const res = await fetch(`/api/todos/${taskId}/done`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ isDone: !isDone }) + }); + + if (res.ok) { + const taskElement = document.querySelector(`li[data-id="${taskId}"]`); + taskElement.classList.toggle('completed'); + } + } catch (err) { + console.error('Error marking task as done:', err); + } +} + +// Erase task +async function deleteTask(taskId) { + try { + const res = await fetch(`/api/todos/${taskId}`, { + method: 'DELETE' + }); + + if (res.ok) { + const taskElement = document.querySelector(`li[data-id="${taskId}"]`); + taskElement.remove(); + } else { + console.error('Failed to delete task from server'); + } + } catch (err) { + console.error('Error deleting task:', err); + } +} + +// Edit task +async function updateTask(taskId, newDescription) { + try { + const res = await fetch(`/api/todos/${taskId}/description`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ description: newDescription }) + }); + + if (res.ok) { + const updatedTask = await res.json(); + const taskElement = document.querySelector(`li[data-id="${taskId}"]`); + taskElement.querySelector('p').textContent = updatedTask.todo.description; + } + } catch (err) { + console.error('Error updating task:', err); + } +} diff --git a/public/js/index.js b/public/js/index.js index 5d8064574ffe7b926ddbcceae836114e138a021f..4fc446d33a5b9b7866c10cf1e9a5e0a8d668ce08 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1,17 +1,184 @@ document.addEventListener('DOMContentLoaded', async () => { - const logoutBtn = document.getElementById('logout-btn'); + const form = document.getElementById("new-task-form"); + const input = form.querySelector('input[name="task"]'); + const taskList = document.getElementById("task-list"); + const logoutBtn = document.getElementById('logout-btn'); + + // Load the tasks when page is initiated + async function loadTasks() { + try { + const res = await fetch("/api/todos"); + const todos = await res.json(); + + // Verificar if the response has tasks + if (todos.length > 0) { + renderTasks(todos); + } else { + console.log('No hay tareas para mostrar.'); + } + } catch (err) { + console.error("Error loading tasks:", err); + } + } + + // Function to render tasks in the list + function renderTasks(todos) { + taskList.innerHTML = ''; + todos.forEach(todo => { + const taskItem = createTaskElement(todo); + taskList.appendChild(taskItem); + }); + } + + // Function to create an element in DOM + function createTaskElement(todo) { + const li = document.createElement('li'); + li.dataset.id = todo._id; + if (todo.isDone) { + li.classList.add('completed'); + } + + li.innerHTML = ` + <span class="task-text">${todo.description}</span> + <input type="text" class="edit-input" value="${todo.description}" style="display:none;"> + <div class="task-actions"> + <button class="complete-btn" title="Mark as complete">✓</button> + <button class="edit-btn" title="Edit task">✎</button> + <button class="save-btn" title="Save changes" style="display:none;">💾</button> + <button class="delete-btn" title="Delete task">×</button> + </div> + `; + + // Button assigns + const completeBtn = li.querySelector(".complete-btn"); + completeBtn.addEventListener("click", async () => { + await toggleComplete(todo._id, !todo.isDone); + li.classList.toggle("completed"); + }); + + const deleteBtn = li.querySelector(".delete-btn"); + deleteBtn.addEventListener("click", async () => { + await deleteTask(todo._id); + li.remove(); + }); + + const editBtn = li.querySelector(".edit-btn"); + const saveBtn = li.querySelector(".save-btn"); + const taskText = li.querySelector(".task-text"); + const editInput = li.querySelector(".edit-input"); + + editBtn.addEventListener("click", () => { + taskText.style.display = "none"; + editInput.style.display = "block"; + editInput.focus(); + saveBtn.style.display = "inline" + editBtn.style.display = "none"; + }); + + saveBtn.addEventListener("click", async () => { + const newDescription = editInput.value.trim(); + if (newDescription && newDescription !== taskText.textContent) { + await updateTask(todo._id, newDescription); + taskText.textContent = newDescription; + } + taskText.style.display = "block"; + editInput.style.display = "none"; + saveBtn.style.display = "none"; + editBtn.style.display = "inline"; + }); + + return li; + } + + // Function to alternate to the completed state + async function toggleComplete(taskId, isDone) { + try { + const res = await fetch(`/api/todos/${taskId}/done`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ isDone }) + }); + if (!res.ok) { + console.error('Error marcando la tarea como completada.'); + } + } catch (err) { + console.error('Error actualizando la tarea:', err); + } + } + + // Function to eliminate a task + async function deleteTask(taskId) { + try { + const res = await fetch(`/api/todos/${taskId}`, { + method: 'DELETE' + }); + if (!res.ok) { + console.error('Error eliminando la tarea.'); + } + } catch (err) { + console.error('Error eliminando la tarea:', err); + } + } + + // Function to update the description of a task + async function updateTask(taskId, newDescription) { + try { + const res = await fetch(`/api/todos/${taskId}/description`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ description: newDescription }) + }); + if (!res.ok) { + console.error('Error actualizando la tarea.'); + } + } catch (err) { + console.error('Error actualizando la tarea:', err); + } + } + + // Load the tasks in the beginning + loadTasks(); - // Event for the logout button - logoutBtn.addEventListener('click', async () => { - try { - const res = await fetch('/api/users/logout', { method: 'POST' }); - if (res.ok) { - window.location.href = 'login.html'; - } else { - console.error('Logout failed'); - } - } catch (err) { - console.error('Error during logout:', err); + // Add new task + form.addEventListener("submit", async (e) => { + e.preventDefault(); + const description = input.value.trim(); + if (description) { + await addTask(description); + input.value = ""; + } + }); + + // Function to add new task + async function addTask(description) { + try { + const res = await fetch("/api/todos", { + method: "POST", + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ description }) + }); + if (res.ok) { + const newTodo = await res.json(); + taskList.appendChild(createTaskElement(newTodo)); + } else { + console.error('Error añadiendo tarea.'); + } + } catch (err) { + console.error('Error añadiendo tarea:', err); + } + } + + // Logout button event + logoutBtn.addEventListener('click', async () => { + try { + const res = await fetch('/api/users/logout', { method: 'POST' }); + if (res.ok) { + window.location.href = 'login.html'; + } else { + console.error('Logout failed'); } - }); -}); \ No newline at end of file + } catch (err) { + console.error('Error during logout:', err); + } + }); +}); diff --git a/routes/todos.js b/routes/todos.js index 8154cf9113a0a9ffa824114ea20067481d3df3eb..f853d478a09a9afa8ae8f76464bc82c367f6989f 100644 --- a/routes/todos.js +++ b/routes/todos.js @@ -11,7 +11,7 @@ function isAuthenticated(req, res, next) { } } - +// Route to logout router.post('/logout', (req, res) => { req.session.destroy((err) => { if (err) { @@ -22,4 +22,89 @@ router.post('/logout', (req, res) => { }); }); +// Route to obtain all tasks of authenticated user +router.get('/', isAuthenticated, async (req, res) => { + try { + const todos = await Todo.find({ userId: req.session.user.id }); + res.json(todos); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Server error' }); + } +}); + +// Route to add a task +router.post('/', isAuthenticated, async (req, res) => { + const { description } = req.body; + + try { + const todo = new Todo({ description, userId: req.session.user.id }); + await todo.save(); + res.json(todo); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Server error' }); + } +}); + + +// Route to eliminate tasks +router.delete('/:id', isAuthenticated, async (req, res) => { + try { + const todo = await Todo.findById(req.params.id); + + if (!todo || todo.userId.toString() !== req.session.user.id) { + return res.status(404).json({ error: 'Todo not found or unauthorized' }); + } + + await todo.deleteOne(); + res.json({ success: true }); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Server error' }); + } +}); + +// Route to update the description of a task +router.put('/:id/description', isAuthenticated, async (req, res) => { + const { description } = req.body; + + try { + const todo = await Todo.findById(req.params.id); + + if (!todo || todo.userId.toString() !== req.session.user.id) { + return res.status(404).json({ error: 'Todo not found or unauthorized' }); + } + + todo.description = description; + await todo.save(); + + res.json({ todo }); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Server error' }); + } +}); + +// Route to mark a task as completed +router.put('/:id/done', isAuthenticated, async (req, res) => { + const { isDone } = req.body; + + try { + const todo = await Todo.findById(req.params.id); + + if (!todo || todo.userId.toString() !== req.session.user.id) { + return res.status(404).json({ error: 'Todo not found or unauthorized' }); + } + + todo.isDone = isDone; + await todo.save(); + + res.json({ todo }); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Server error' }); + } +}); + module.exports = router; \ No newline at end of file