Source: public/js/tasks.js

// tasks.js

//=========================
// CONSTANTS
//=========================

/**
 * The end of today's date set to 23:59:59:999.
 * @type {Date}
 */
const todayDate = new Date();
todayDate.setHours(23, 59, 59, 999);

/**
 * The numeric day for today.
 * @type {number}
 */
const today = todayDate.getDate();

/**
 * The date object representing one week from today, with time set to 23:59:59:999.
 * @type {Date}
 */
const weekDate = new Date();
weekDate.setDate(today + 7);
weekDate.setHours(23, 59, 59, 999);

/**
 * The date object representing approximately one month (30 days) from today, with time set to 23:59:59:999.
 * @type {Date}
 */
const monthDate = new Date();
monthDate.setDate(today + 30);
monthDate.setHours(23, 59, 59, 999);


//=========================
// MAIN FUNCTION FOR 
// CREATING LIST ITEMS / EDITING LIST ITEMS
//=========================

/**
 * Creates a list item element representing a task event.
 *
 * @param {Object} event - The task event object.
 * @param {string|number} event.id - The unique identifier for the event.
 * @param {string} event.category - The category of the event.
 * @param {number} event.priority - The priority level of the event (0 for low, 1 for normal, 2 for high).
 * @param {boolean} event.complete - The completion status of the event.
 * @param {Date|string} event.startDate - The start date/time of the event.
 * @param {string} event.title - The title of the event.
 * @returns {HTMLLIElement} The list item element that represents the task.
 */
function createTaskListItem(event) {
  
  const li = document.createElement("li");
  li.dataset.eventId = event.id;
  li.dataset.category = event.category;
  li.dataset.priority = event.priority;

  //code for checkbox
  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";
  checkbox.checked = event.complete;
  checkbox.addEventListener("click", () => {
    toggleComplete(event, li, checkbox);
  });

  //formattting date to short date
  const formatDate = new Intl.DateTimeFormat("en-US", {
    month: "numeric",
    day: "numeric",
    year: "numeric",
  });
  const rawDate = new Date(event.startDate);
  rawDate.setDate(rawDate.getDate() + 1);
  const formattedDate = formatDate.format(rawDate);

  let priorityFlag;
  switch (event.priority) {
    case 0: priorityFlag = "💤";
      break;
    case 1: priorityFlag = "";
      break;
    case 2: priorityFlag = "⚠";
      break;
    default: priorityFlag = "";
      break;
  };

  //put title and formatted date onto span
  const span = document.createElement("span");
  span.textContent = `${priorityFlag} ${event.title} - ${formattedDate}`;

  //code for edit button
  const editButton = document.createElement("button");
  editButton.textContent = "✏";
  editButton.addEventListener("click", () => {
    openEditWindow(event, li);
  });

  /*//code for delete button
  const deleteButton = document.createElement("button");
  deleteButton.textContent = "🗑";
  deleteButton.addEventListener("click", async () => {
    if (confirm(`Delete this item? "${event.title}"`)) {
      const success = await deleteEvent(event.id);
      if (success) {
        li.remove();
      } else {
        alert("error deleting event from sql server");
      }
    }
  }); */

  li.appendChild(checkbox);
  li.appendChild(span);
  li.appendChild(editButton);
  //li.appendChild(deleteButton);

  return li;
}


//=========================
// GET TASKS
//=========================

/**
 * Fetches task events from the server when the DOM is fully loaded, creates corresponding list items,
 * and appends them to their respective lists based on due dates.
 */
document.addEventListener("DOMContentLoaded", async () => {
  try {
    const response = await fetch("/api.events");
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    const events = await response.json();
    const todayTaskList = document.getElementById("today-list");
    const weekTaskList = document.getElementById("week-list");
    const monthTaskList = document.getElementById("month-list");

    events.forEach(event => {
      const li = createTaskListItem(event);
      const dueDate = new Date(event.startDate);

      if (dueDate.getTime() <= todayDate.getTime()) {
        todayTaskList.appendChild(li);
      } else if (dueDate.getTime() <= weekDate.getTime()) {
        weekTaskList.appendChild(li);
      } else if (dueDate.getTime() <= monthDate.getTime()) {
        monthTaskList.appendChild(li);
      }
    });
  } catch (error) {
    console.error("Fetch error:", error);
  }
});


//=========================
// ADD TASK
//=========================

/**
 * Opens the "add task" window by making the corresponding HTML element visible.
 */
function addTaskWindow() {document.getElementById("add-task").style.display = "block";}

/**
 * Closes the "add task" window by hiding the corresponding HTML element.
 */
function closeTaskWindow() {document.getElementById("add-task").style.display = "none";}

// Handle "Add Task" button click event to create a new task
document.getElementById("addTaskButton").addEventListener("click", async function (event) {
  event.preventDefault();
  
  const taskName = document.getElementById("task-name").value;
  const dueDate = document.getElementById("task-due-date").value;
  const taskCategory = document.getElementById("task-category").value;
  const taskPriority = parseInt(document.getElementById("add-task-priority").value, 10);

  const taskData = {
    title: taskName,
    startDate: dueDate,
    complete: false,
    category: taskCategory,
    priority: taskPriority
  };

  console.log(taskData);

  closeTaskWindow();
  document.getElementById("task-name").value = "";
  document.getElementById("task-due-date").value = "";
  document.getElementById("task-category").value = "";
  document.getElementById("add-task-priority").value = 1; // or whatever default

  try {
    const response = await fetch("/api/events", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(taskData)
    });

    if(!response.ok) {
      throw new Error("error: api post error");
    }

    var newTask = await response.json();
  } catch (error) {
      console.error("API error:", error);
  }
  
  const todayTaskList = document.getElementById("today-list");
  const weekTaskList = document.getElementById("week-list");
  const monthTaskList = document.getElementById("month-list");

  const li = createTaskListItem(newTask);

  const newDate = new Date(taskData.startDate);
  newDate.setDate(newDate.getDate() + 1);

  if (newDate.getTime() <= todayDate.getTime()) {
    todayTaskList.appendChild(li);
  } else if (newDate.getTime() <= weekDate.getTime()) {
    weekTaskList.appendChild(li);
  } else if (newDate.getTime() <= monthDate.getTime()) {
    monthTaskList.appendChild(li);
  }

  setLayout();
});

//=========================
// MODIFY TASK
//=========================

/**
 * Opens the edit task window and populates it with the selected task's details.
 *
 * @param {Object} event - The task event object.
 * @param {HTMLElement} li - The list item element representing the task.
 */
function openEditWindow(event, li) {
  const editTaskWindow = document.getElementById("edit-task");
  editTaskWindow.style.display = "block";
  editTaskWindow.dataset.eventId = event.id;
  editTaskWindow.dataset.title = event.title;
  
  //new fields
  editTaskWindow.dataset.category = event.category;
  editTaskWindow.dataset.priority = event.priority;
  
  if(event.complete === true || event.complete) {
    editTaskWindow.dataset.complete = 1;
  } else {
    editTaskWindow.dataset.complete = 0;
  };

  const editTaskName = document.getElementById("edit-task-name");
  const editTaskDueDate = document.getElementById("edit-task-due-date");
  const editTaskCategory = document.getElementById("edit-task-category");
  const editTaskPriority = document.getElementById("edit-task-priority");

  editTaskName.value = event.title;
  editTaskDueDate.value = new Date(event.startDate).toISOString().split("T")[0];
  editTaskCategory.value = event.category;
  editTaskPriority.value = event.priority;

}

/**
 * Closes the edit task window by hiding the corresponding HTML element.
 */
function closeEditWindow() {document.getElementById("edit-task").style.display = "none";}

//function for checking task as complete/incomplete
/**
 * Toggles the completion status of a task and updates it on the server.
 *
 * @param {Object} event - The task event object.
 * @param {HTMLElement} li - The list item element representing the task.
 * @param {HTMLInputElement} checkbox - The checkbox element that triggered the action.
 */
async function toggleComplete(event, li, checkbox) {
  const updateComplete = !event.complete;
  const eventId = event.id;
  const eventTitle = event.title;
  const dueDate = event.startDate;

  try {
    const response = await fetch(`/api/events/${eventId}`, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        complete: updateComplete,
        title: eventTitle,
        startDate: dueDate
      })
    });

    if (!response.ok) {
      throw new Error("HTTP error: ", response.status);
    }

    event.complete = updateComplete;
    checkbox.checked = updateComplete;
  
  } catch(error) {
    console.error("error updating task complete: ", error);
    checkbox.checked = event.complete;
  }
}

//function for the edit window button
/**
 * Handles the click event for updating an existing task.
 *
 * This asynchronous function is triggered when the edit task button is clicked.
 * It prevents the default form submission behavior, retrieves updated task values
 * from the edit form, and sends a PUT request to update the event on the server.
 * If the response is successful, it updates the corresponding list item in the DOM
 * with the new title and formatted due date, and then closes the edit window.
 *
 * @param {Event} event - The click event triggered by the edit task button.
 */
document.getElementById("editTaskButton").addEventListener("click", async function (event) {
  event.preventDefault();

  const updateTitle = document.getElementById("edit-task-name").value;
  const updateDueDate = document.getElementById("edit-task-due-date").value;
  const complete = document.getElementById("edit-task").dataset.complete;
  const eventId = document.getElementById("edit-task").dataset.eventId;
  const category = document.getElementById("edit-task-category").value;
  const priority = parseInt(document.getElementById("edit-task-priority").value, 10);

  try {
    const response = await fetch(`/api/events/${eventId}`, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        title: updateTitle,
        startDate: updateDueDate,
        complete: complete,
        category: category,
        priority: priority
      })
    });

    if (!response.ok) {
      throw new Error("HTTP error: ", response.status);
    }

    const li = document.querySelector(`li[data-event-id="${eventId}"]`)
    if (li) {
      const span = li.querySelector("span");
      const formatDate = new Intl.DateTimeFormat("en-US", {
        month: "numeric",
        day: "numeric",
        year: "numeric"
      });
      const formattedDate = formatDate.format(new Date(updateDueDate));
      span.textContent = `${updateTitle} - ${formattedDate}`
    }

    closeEditWindow();
  } catch (error) {
    console.error("Error updating event: ", error);
  }
});


//=========================
// DELETE TASK
//=========================

/**
 * Deletes the selected task after confirming with the user, updates the UI accordingly,
 * and closes the edit window.
 */
const deleteButton = document.getElementById("deleteTaskButton");
  //deleteButton.textContent = "🗑";
deleteButton.addEventListener("click", async () => {
  const eventId = document.getElementById("edit-task").dataset.eventId;
  const eventTitle = document.getElementById("edit-task").dataset.title;
  const li = document.querySelector(`li[data-event-id="${eventId}"]`);


    if (confirm(`Delete this item? "${eventTitle}"`)) {
      const success = await deleteEvent(eventId);
      if (success) {
        if(li) {
          li.remove();

        }
        closeEditWindow();
        
      } else {
        alert("error deleting event from sql server");
      }
    }
});

/**
 * Deletes a task event from the server.
 *
 * @param {string|number} eventId - The ID of the task event to delete.
 * @returns {Promise<boolean>} A promise that resolves to true if the event was deleted successfully, or false otherwise.
 */
async function deleteEvent(eventId) {
  try {
    const response = await fetch(`/api/events/${eventId}`, {
      method: "DELETE"
    });
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`)
    }

    return true;
  } catch (error) {
    console.error("delete failed:", error);
    return false;
  }
}