As an expert developer and tutor could you rate these files' code and then provide these files after improvement?

// ui.js
import { getCurrentProject, getState, setCurrentProjectIndex, toggleTodoCompletion, updateTodo, addTodo, addProject } from './app.js';
import { StorageManager } from './localStorage.js';
import { Todo } from './todo.js';
import Sortable from 'sortablejs';

/**
 * Render the list of projects in the sidebar.
 * @param {Array} projects - Array of Project instances.
 */
export function renderProjects(projects) {
    const projectContainer = document.querySelector('#project-list');
    projectContainer.innerHTML = ''; // Clear existing projects

    const fragment = document.createDocumentFragment();

    projects.forEach((project, index) => {
        const projectElement = document.createElement('li');
        projectElement.textContent = project.name;
        projectElement.classList.add('project-item');
        projectElement.setAttribute('data-index', index);
        projectElement.setAttribute('tabindex', '0'); // Make focusable
        projectElement.setAttribute('role', 'button');

        fragment.appendChild(projectElement);
    });

    projectContainer.appendChild(fragment);

    highlightSelectedProject(getState().currentProjectIndex);

    // const projectIcon = document.createElement('i');
    // projectIcon.className = 'fas fa-folder';
    // projectElement.prepend(projectIcon);
}

/**
 * Highlight the selected project in the sidebar.
 * @param {number} index – Index of the selected project.
 */
function highlightSelectedProject(index) {
    const items = document.querySelectorAll('#project-list li')
    items.forEach((item, i) => {
        item.classList.toggle('selected', i === index);
    });
}

/**
 * Render the todos for the selected project.
 * @param {Project} project – The Project instance whose todos are to be rendered.
 */
export function renderTodos(project) {
    const todoContainer = document.querySelector('#todo-container');
    todoContainer.innerHTML = ''; // Clear existing todos

    if (!project || !project.todos) {
        console.error('Invalid project or no todos available');
        return;
    }

    const fragment = document.createDocumentFragment();

    project.todos.forEach((todo) => {
        const todoElement = createTodoElement(todo);
        fragment.appendChild(todoElement);
    });

    todoContainer.appendChild(fragment);
}

/**
 * Create a DOM element for a single todo item.
 * @param {Todo} todo – The todo item to create an element for.
 * @returns {HTMLElement} The DOM element representing the todo.
 */
function createTodoElement(todo) {
    const todoElement = document.createElement('div');
    todoElement.classList.add('todo-item', `priority-${todo.priority}`);
    todoElement.dataset.id = todo.id;

    // Todo checkbox
    const checkbox = document.createElement('input');
    checkbox.type = 'checkbox';
    checkbox.checked = todo.completed;
    checkbox.setAttribute('aria-label', 'Mark as completed');
    checkbox.addEventListener('change', () => {
        // console.log('Before toggleCompletion:', todo); // Debugging
        if (typeof todo.toggleCompletion === 'function') {
            // todo.toggleCompletion();
            toggleTodoCompletion(todo.id);
            updateTodoStatus(todo);
        } else {
            console.error('toggleCompletion is not a function on todo:', todo);
        }        
    });

    todoElement.appendChild(checkbox);

    // Todo info (Title and Due Date)
    const todoInfo = document.createElement('span');
    todoInfo.textContent = `${todo.title} – Due: ${todo.dueDate}`;
    todoInfo.classList.add('todo-info');
    todoElement.appendChild(todoInfo);

    // Tags
    if (todo.tags && todo.tags.length > 0) {
        const tagsContainer = document.createElement('div');
        tagsContainer.classList.add('tags-container');
        todo.tags.forEach((tag) => {
            const tagElement = document.createElement('span');
            tagElement.classList.add('tag');
            tagElement.textContent = tag;
            tagsContainer.appendChild(tagElement);
        });
        todoElement.appendChild(tagsContainer);
    }

    // Action Buttons Container
    const actionsContainer = document.createElement('div');
    actionsContainer.classList.add('actions-container');

    // Edit button
    const editButton = createButton({ iconClass: 'fas fa-edit', ariaLabel: 'Edit Todo' }, 'edit-button');
    actionsContainer.appendChild(editButton);

    // Delete button
    const deleteButton = createButton({ iconClass: 'fas fa-trash', ariaLabel: 'Delete Todo' }, 'delete-button');
    actionsContainer.appendChild(deleteButton);

    todoElement.appendChild(actionsContainer);

    // Apply priority-based styling
    todoElement.classList.add(`priority-${todo.priority}`);

    // Toggle completed class
    if (todo.completed) {
        todoElement.classList.add('completed');
    }

    return todoElement;
}

/**
 * Create a button element with optional icon and text.
 * @param {Object} options – Options for the button.
 * @param {string} [options.iconClass] – Font Awesome icon classes.
 * @param {string} [options.text] – Text content for the button.
 * @param {string} className – Additional class names for the button.
 * @returns {HTMLElement} The created button element.
 */
function createButton({ iconClass, text, ariaLabel }, className) {
    const button = document.createElement('button');
    // button.textContent = text;
    button.classList.add(className);
    button.setAttribute('aria-label', ariaLabel);

    if (iconClass) {
        const icon = document.createElement('i');
        icon.className = iconClass;
        button.appendChild(icon);
    }

    if (text) {
        const textNode = document.createTextNode(text);
        button.appendChild(textNode);
    }

    return button;
}

/**
 * Highlight the selected project when selected via keyboard.
 * @param {number} index – Index of the selected project.
 */
function highlightProjectSelection(index) {
    highlightSelectedProject(index);
    const currentProject = getState().projects[index];
    renderTodos(currentProject);
}

/**
 * Handle updating the todo's completion status
 * @param {Todo} todo – The todo item to update.
 */
function updateTodoStatus(todo) {
    // StorageManager.saveProjects(getState().projects);
    const todoElement = document.querySelector(`.todo-item[data-id="${todo.id}"]`);
    if (todo.completed) {
        console.log(`Adding 'completed' class to todo: ${todo.title}`);
        todoElement.classList.add('completed');
    } else {
        todoElement.classList.remove('completed');
    }
}

/**
 * Set up interactions for todo items (edit, delete, toggle completion).
 */
export function setupTodoInteraction() {
    const todoContainer = document.querySelector('#todo-container');

    // Handle edit, delete and toggle completion using event delegation
    todoContainer.addEventListener('click', (event) => {
        const target = event.target.closest('button');
        if (!target) return;

        const todoElement = target.closest('.todo-item');
        if (!todoElement) return;

        const todoId = todoElement.getAttribute('data-id');
        const currentProject = getCurrentProject();
        const todo = currentProject.getTodoById(todoId);

        if (!todo) {
            console.error(`Todo with ID: ${todoId} not found`)
            return;
        }

        if (target.matches('.edit-button')) {
            showEditForm(todo);
        }

        if (target.matches('.delete-button')) {
            handleDeleteTodo(todoId, currentProject);
        }
    });

    // Handling checkbox changes
    todoContainer.addEventListener('change', (event) => {
        if (event.target.type === 'checkbox') {
            const todoElement = event.target.closest('.todo-item');
            if (!todoElement) return;

            const todoId = todoElement.getAttribute('data-id');
            const currentProject = getCurrentProject();
            const todo = currentProject.getTodoById(todoId);

            if (!todo) {
                console.error(`Todo with ID: ${todoId} not found.`);
                return;
            }

            if (typeof todo.toggleCompletion === 'function') {
                todo.toggleCompletion();
                updateTodoStatus(todo);
            } else {
                console.error('toggleCompletion is not a function on todo:', todo);
            }
        }
    });
}

/**
 * Handle deleting a todo.
 * @param {string} todoId – ID of the todo to delete.
 * @param {Project} project – The project containing the todo.
 */
function handleDeleteTodo(todoId, project) {
    if (confirm('Are you sure you want to delete this task?')) {
        project.removeTodo(todoId);
        StorageManager.saveProjects(getState().projects);
        renderTodos(project);
    }
}

/**
 * Show the edit form modal with the current task data.
 * @param {Todo} todo – The todo to edit.
 */
function showEditForm(todo) {
    // Fill in the form with current todo data
    document.querySelector('#edit-title').value = todo.title;
    document.querySelector('#edit-due-date').value = todo.dueDate;
    document.querySelector('#edit-description').value = todo.description;
    document.querySelector('#edit-priority').value = todo.priority;
    document.querySelector('#edit-tags').value = todo.tags.join(', ') // Assuming you have input for tags

    const modal = document.querySelector('#edit-task-modal');
    if (!modal) {
        console.error('Edit Task Modal not found in the DOM.');
        return;
    }

    modal.style.display = 'block';
    modal.setAttribute('aria-hidden', 'false');
    modal.querySelector('#edit-title').focus();
    document.body.classList.add('modal-open'); // Prevent background scrolling

    const saveButton = document.querySelector('#save-changes-button');
    const cancelButton = document.querySelector('#cancel-button');
    const closeButton = document.querySelector('.close-button');

    /**
     * Handle saving changes to a todo.
     * @param {Todo} todo – The todo being edited.
     */
    function saveChangesHandler() {
        const updatedTitle = document.querySelector('#edit-title').value.trim();
        const updatedDueDate = document.querySelector('#edit-due-date').value;
        const updatedDescription = document.querySelector('#edit-description').value.trim();
        const updatedPriority = document.querySelector('#edit-priority').value;
        const updatedTagsInput = document.querySelector('#edit-tags').value;
        const updatedTags = updatedTagsInput.split(',').map((tag) => tag.trim()).filter((tag) => tag);

        if (!updatedTitle || !updatedDueDate) {
            showToast('Title and Due Date are required.');
            return;
        }

        // Update todo properties
        todo.title = updatedTitle;
        todo.dueDate = updatedDueDate;
        todo.description = updatedDescription;
        todo.priority = updatedPriority;
        todo.tags = updatedTags;

        try {
            updateTodo(todo); // Update in state and save
            closeModal();
        } catch (error) {
            console.error('Error updating todo:', error);
        }
    }

    /**
     * Function to close the edit modal.
     */
    function closeModal() {
        // const modal = document.querySelector('#edit-task-modal');
        if (modal) {
            modal.style.display = 'none';
            modal.setAttribute('aria-hidden', 'true');
            document.body.classList.remove('modal-open');
        }

        // Remove event listeners to prevent duplicate handlers
        saveButton.removeEventListener('click', saveChangesHandler);
        cancelButton.removeEventListener('click', closeModal);
        closeButton.removeEventListener('click', closeModal);
        window.removeEventListener('keydown', escKeyHandler);
        window.removeEventListener('click', outsideClickHandler);
    }

    function escKeyHandler(event) {
        if (event.key === 'Escape') {
            closeModal();
        }
    }

    function outsideClickHandler(event) {
        // const modal = document.querySelector('#edit-task-modal');
        if (event.target === modal) {
            closeModal();
        }
    }

    // Add event listeners
    saveButton.addEventListener('click', saveChangesHandler);
    cancelButton.addEventListener('click', closeModal);
    closeButton.addEventListener('click', closeModal);
    window.addEventListener('keydown', escKeyHandler);
    window.addEventListener('click', outsideClickHandler);
}

/**
 * Initialize UI interactions.
 */
export function initializeUI() {
    setupProjectSelection();
    setupTodoInteraction();
    setupSidebarToggle();
    setupFormValidation();
    setupDragAndDrop();
    setupThemeToggle();
}

/**
 * Setup project selection interactions.
 */
export function setupProjectSelection() {
    const projectContainer = document.querySelector('#project-list');

    projectContainer.addEventListener('click', (event) => {
        if (event.target && event.target.matches('li.project-item')) {
            const index = parseInt(event.target.getAttribute('data-index', 10));
            setCurrentProjectIndex(index);
            highlightProjectSelection(index);
            // const currentProject = getState().projects[index];
            // renderTodos(currentProject);
        }
    });

    // Accessibility: Allow selection via keyboard
    projectContainer.addEventListener('keydown', (event) => {
        if (event.key === 'Enter' && event.target.matches('li.project-item')) {
            const index = parseInt(event.target.getAttribute('data-index'), 10);
            setCurrentProjectIndex(index);
            highlightProjectSelection(index);
            // const currentProject = getState().projects[index];
            // renderTodos(currentProject);
        }
    });
}

/**
 * Function to handle adding a new todo.
 */
function handleAddTodo() {
    try {
        const title = document.querySelector('#todo-title').value.trim();
        const dueDate = document.querySelector('#todo-due-date').value;
        const description = document.querySelector('#todo-description').value.trim();
        const priority = document.querySelector('#todo-priority').value;
        const tagsInput = document.querySelector('#todo-tags').value;
        const tags = tagsInput.split(',').map((tag) => tag.trim()).filter((tag) => tag);

        if (title && dueDate) { // Ensure title and due date are provided
            addTodo(title, dueDate, description, priority, tags); // Call function to add a new todo
            clearTodoInputs();
        } else {
            showToast('Please fill in both title and due date.'); // Alert if fields are empty
        }
    } catch (error) {
        console.error('Error adding todo:', error);
    }
}

/**
 * Function to handle adding a new project.
 */
function handleAddProject() {
    try {
        const projectName = document.querySelector('#project-name').value.trim();

        // Call function to add a new project
        if (projectName) {
            addProject(projectName);
            document.querySelector('#project-name').value = ''; // Clear input field
        } else {
            showToast('Please enter a project name.') // Alert if project name is empty
        }
    } catch (error) {
        console.error('Error adding project:', error);
    }
}

/**
 * Function to clear todo input fields
 */
function clearTodoInputs() {
    document.querySelector('#todo-title').value = ''; // Clear input field after adding
    document.querySelector('#todo-due-date').value = '';
    document.querySelector('#todo-description').value = '';
    document.querySelector('#todo-priority').value = 'low'; // Reset priority to default
    document.querySelector('#todo-tags').value = '';
}

// Function to show alert messages
function showAlert(message) {
    const alertBox = document.createElement('div');
    alertBox.classList.add('alert');
    alertBox.textContent = message;

    const main = document.querySelector('main');
    main.appendChild(alertBox);

    // alertBox.style.display = 'block';
    setTimeout(() => {
        // alertBox.style.display = 'none';
        alertBox.classList.add('fade-out');
        alertBox.addEventListener('transitionend', () => {
            alertBox.remove();
        });
    }, 3000);
}

/**
 * Set up event listeners for the application.
 */
export function setupEventListeners() {
    document.addEventListener('click', (event) => {
        if (event.target.matches('#add-todo-button') || event.target.closest('#add-todo-button')) {
            handleAddTodo();
        }
        if (event.target.matches('#add-project-button') || event.target.closest('#add-project-button')) {
            handleAddProject();
        }
    });

    // Accessibility: Allow form submission via Enter key
    document.querySelector('#add-todo-form').addEventListener('submit', (e) => {
        e.preventDefault();
        handleAddTodo();
    });

    document.querySelector('#add-project').addEventListener('submit', (e) => {
        e.preventDefault();
        handleAddProject();
    });
}

/**
 * Setup sidebar toggle interactions.
 */
export function setupSidebarToggle() {
    const sidebar = document.querySelector('.sidebar');
    const toggleButton = document.querySelector('#sidebar-toggle');

    toggleButton.addEventListener('click', () => {
        sidebar.classList.add('collapsed');
        toggleButton.setAttribute('aria-expanded', !sidebar.classList.contains('collapsed'));
    });
}

/**
 * Setup form validation for add todo form.
 */
function setupFormValidation() {
    const todoTitleInput = document.querySelector('#todo-title');

    todoTitleInput.addEventListener('input', () => {
        if (todoTitleInput.value.trim() === '') {
            todoTitleInput.classList.add('invalid');
            todoTitleInput.setCustomValidity('Title is required.');
        } else {
            todoTitleInput.classList.remove('invalid');
            todoTitleInput.setCustomValidity('');
        }
    })
}

/**
 * Setup drag and drop functionality for todo.
 */
export function setupDragAndDrop() {
    const todoContainer = document.querySelector('#todo-container');

    new Sortable(todoContainer, {
        animation: 150,
        onEnd: function(/** Event */ evt) {
            const itemEl = evt.item; // dragged HTMLElement
            const fromIndex = evt.oldIndex;
            const toIndex = evt.newIndex;
            updateTodoOrder(fromIndex, toIndex);
        },
    });
}

/**
 * Update the order of todos after drag and drop.
 * @param {number} fromIndex – The original index of the dragged todo.
 * @param {number} toIndex – The new index of the dragged todo.
 */
function updateTodoOrder(fromIndex, toIndex) {
    const currentProject = getCurrentProject();
    const movedTodo = currentProject.todos.splice(fromIndex, 1)[0];
    currentProject.todos.splice(toIndex, 0, movedTodo);
    StorageManager.saveProjects(getState().projects);
}

/**
 * Function to show toast messages.
 * @param {string} message – The message to display.
 */
function showToast(message) {
    const toastContainer = document.querySelector('#toast-container');
    const toast = document.createElement('div');
    toast.classList.add('toast');
    toast.textContent = message;

    toastContainer.appendChild(toast);

    setTimeout(() => {
        toast.classList.add('fade-out');
        toast.addEventListener('transitionend', () => {
            toast.remove();
        });
    }, 4000);
}

/**
 * Setup theme toggle functionality.
 */
export function setupThemeToggle() {
    const themeToggle = document.querySelector('#theme-toggle');

    themeToggle.addEventListener('click', () => {
        const currentTheme = document.documentElement.getAttribute('data-theme');
        const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
        document.documentElement.setAttribute('data-theme', newTheme);
        localStorage.setItem('theme', newTheme);
        // Update icon
        const icon = themeToggle.querySelector('i');
        icon.className = newTheme === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
    });

    // On load, set theme from localStorage
    const savedTheme = localStorage.getItem('theme') || 'light';
    document.documentElement.setAttribute('data-theme', savedTheme);
    // Update icon accordingly
    const icon = themeToggle.querySelector('i');
    icon.className = savedTheme === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
}

// app.js

import { Project } from './project.js';
import { Todo } from './todo.js';
import { StorageManager } from './localStorage.js';
import { renderTodos, renderProjects } from './ui.js';

/**
 * Application state encapsulated in an object.
 */
const state = {
  /** @type {Project[]} */
  projects: [],
  /** @type {number} */
  currentProjectIndex: 0
};

/**
 * Getter for the application state.
 * Returns a deep copy to prevent direct mutation.
 * @returns {Object} The current state.
 */
export function getState() {
  return state;
  // {
  //   ...state,
  //   projects: state.projects.map((project) => ({
  //     ...project,
  //     todos: project.todos.map((todo) => ({ ...todo })), // Returns shallow copies of todos
      //   {
      //   // Ensures each todo is an instance of Todo
      //   if (!(todo instanceof Todo)) {
      //     return new Todo(
      //       todo.title,
      //       todo.dueDate,
      //       todo.description,
      //       todo.priority,
      //       todo.tags,
      //       todo.notes,
      //       todo.id
      //     );
      //   }
      //   return todo;
      // }),
    // })),
    // currentProjectIndex: state.currentProjectIndex,
  // };
}

/**
 * Gets the index of the current project.
 * @returns {number} The current project index.
 */
export function getCurrentProjectIndex() {
  return state.currentProjectIndex;
}

/**
 * Gets the current project instance.
 * @returns {Project} The current project.
 */
export function getCurrentProject() {
  return state.projects[state.currentProjectIndex];
}

/**
 * Sets the current project index and renders its todos.
 * @param {number} index – The index of the project to set as current.
 */
export function setCurrentProjectIndex(index) {
  if (index >= 0 && index < state.projects.length) {
    state.currentProjectIndex = index;
    renderTodos(state.projects[index]);
  } else {
    console.error('Invalid project index.');
  }
}

// export function selectProject(index) {
//   if (index => 0 && index < projectsArray.length) {
//     currentProjectIndex = index;
//     renderTodos(projectsArray[index]);
//   } else {
//     console.error('Invalid project index');
//   }
// }

/**
 * Initializes the application by loading projects and rendering them.
 */
export function initializeApp() {
  // Load existing projects from localStorage on startup
  const loadedProjects = StorageManager.loadProjects();

  if (loadedProjects.length > 0) {
    state.projects = loadedProjects;
  } else {
    // Create a default project if none exist
    const defaultProject = new Project('Default Project');
    state.projects.push(defaultProject);
    StorageManager.saveProjects(state.projects);
  }

  // Render the loaded or default projects
  renderProjects(state.projects);
  // renderTodos(getCurrentProject());
  setCurrentProjectIndex(0);
}

/**
 * Adds a new todo to the current project
 * @param {string} title – The title of the todo.
 * @param {string} description – The description of the todo.
 * @param {string} dueDate – The due date of the todo.
 * @param {string} priority – The priority of the todo.
 * @param  {string[]} tags – Tags associated with the todo.
 */
export function addTodo(title, dueDate, description, priority, tags = []) {
  const currentProject = getCurrentProject(); // Use selected project

  if (!currentProject) {
    console.error('No current project selected');
    return;
  }

  try {
    const newTodo = new Todo(title, dueDate, description, priority, tags);
    currentProject.addTodo(newTodo);
    // Save updated projects to localStorage
    StorageManager.saveProjects(state.projects);

    // Render todos for the current project
    renderTodos(currentProject);
  } catch (error) {
    console.error('Error adding todo:', error);
  }
  // return newTodo; // Return the created Todo object
}

/**
 * Adds a new project to the application.
 * @param {string} projectName – The name of the new project.
 */
export function addProject(projectName) {
  if (!projectName || typeof projectName !== 'string') {
    console.error('Invalid project name');
    return;
  }
  try {
    const newProject = new Project(projectName);
    state.projects.push(newProject);
    // Save updated projects to localStorage
    StorageManager.saveProjects(state.projects);

    // Re-render the projects list
    renderProjects(state.projects);
  } catch (error) {
    console.error('Error adding project:', error);
  }
}

/**
 * Updates the existing todo in the current project.
 * @param {Todo} updatedTodo – The todo instance with updated details.
 */
export function updateTodo(updatedTodo) {
  const currentProject = getCurrentProject();
  if (!currentProject) {
    console.error('No current project selected.');
    return;
  }

  try {
    const success = currentProject.updateTodo(updatedTodo);
    if (success) {
      StorageManager.saveProjects(state.projects);
      renderTodos(currentProject);
    }
    
  } catch (error) {
    console.error('Error updating todo:', error);
  }
}


/**
 * Toggles the completion status of a todo.
 * @param {string} todoId - The ID of the todo to toggle.
 */
export function toggleTodoCompletion(todoId) {
  const currentProject = getCurrentProject();
  const todo = currentProject.getTodoById(todoId);
  if (todo) {
      todo.toggleCompletion();
      StorageManager.saveProjects(state.projects);
  } else {
      console.error(`Todo with ID ${todoId} not found.`);
  }
}

// todo.js

import { v4 as uuidv4 } from 'uuid'; // Ensure you have installed the `uuid` package via npm

const PRIORITY_LEVELS = {
    LOW: 'low',
    MEDIUM: 'medium',
    HIGH: 'high',
};

/**
 * Represents a single Todo item.
 */
export class Todo {
    /**
     * Creates a new Todo instance.
     * @param {string} title – The title of the todo.
     * @param {string} dueDate – The due date of the todo in YYYY-MM-DD format.
     * @param {string} [description=''] – The description of the todo.
     * @param {string} [priority='low'] – The priority level ('low', 'medium', 'high').
     * @param {string} [tags=[]] – Tags associated with the todo.
     * @param {string} [notes=''] – Additional notes for the todo.
     * @param {string} [id] – Unique identifier for the todo.
     */
    constructor(title, dueDate, description = '', priority = 'low', tags = [], notes = '', id = uuidv4()) {
        // Data Validation
        if (!title || typeof title !== 'string') {
            throw new Error('Title is required and must be a string.');
        }
        if (!dueDate || isNaN(Date.parse(dueDate))) {
            throw new Error('A valid Due Date is required');
        }
        if (!Object.values(PRIORITY_LEVELS).includes(priority)) {
            throw new Error(`Priority must be one of ${Object.values(PRIORITY_LEVELS).join(', ')}.`);
        }

        // Object.defineProperty(this, 'id', {
        //     value: id,
        //     writable: false,
        //     enumerable: true,
        // });

        this.id = id; // Robust unique ID generation
        this.title = title;
        this.dueDate = dueDate;
        this.description = description;
        this.priority = priority;
        this.tags = tags;
        this.notes = notes;
        this.completed = false; // Track completion status
        this.subtasks = []; // Array to hold subtasks if needed
        this.recurrence = null; // For recurring tasks
        this.checklist = [];
    }

    /**
     * Toggle the completion status of todo.
     * @returns {Todo} The updated todo instance.
     */
    toggleCompletion() {
        this.completed = !this.completed;
        console.log(`Todo "${this.title}" completed status toggled to: ${this.completed}`);
        return this;
    }

    /**
     * Add a subtask to the todo.
     * @param {Todo} subtask – The subtask to add.
     */
    addSubtask(subtask) {
        if (subtask instanceof Todo) {
            this.subtasks.push(subtask);
        } else {
            throw new Error('Subtask must be an instance of Todo.');
        }
    }

    /**
     * Remove a subtask from the todo by ID.
     * @param {string} subtaskId – The ID of the subtask to remove.
     */
    removeSubtask(subtaskId) {
        this.subtasks = this.subtasks.filter((st) => st.id !== subtaskId);
    }
}

// project.js

import { Todo } from './todo.js';

/**
 * Represents a project containing multiple todos.
 */
export class Project {
    /**
     * Creates a new Project instance.
     * @param {string} name – The name of the project.
     */
    constructor(name) {
        if (!name || typeof name !== 'string') {
            throw new Error('Project name is required and must be a string.');
        }

        this.name = name;
        /** @type {Todo[]} */
        this.todos = []; // Array to hold todos
    }

    /**
     * Add a new Todo to the project.
     * @param {Todo} todo – The todo instance to add.
     * @returns {boolean} True if added successfully, false otherwise.
     */
    addTodo(todo) {
        if (todo instanceof Todo) {
            this.todos.push(todo);
            return true;
        } else {
            console.error('Only instances of Todo can be added.');
            return false;
        }
    }

    /**
     * Remove a todo by its ID.
     * @param {string} todoId – The ID of the todo to remove.
     */
    removeTodo(todoId) {
        const initialLength = this.todos.length;
        this.todos = this.todos.filter((t) => t.id !== todoId); // Remove the todo
        if (this.todos.length === initialLength) {
            console.warn(`Todo with ID ${todoId} not found.`);
        }
    }

    /**
     * Get a todo by its ID.
     * @param {string} todoId – The ID of the todo to retrieve.
     * @returns {Todo|null} The todo instance or null if not found.
     */
    getTodoById(todoId) {
        return this.todos.find((t) => t.id === todoId) || null;
    }

    /**
     * Update a todo's details.
     * @param {Todo} updatedTodo – The updated Todo instance.
     * @returns {boolean} True if updated successfully, false otherwise.
     */
    updateTodo(updatedTodo) {
        const index = this.todos.findIndex((t) => t.id === updatedTodo.id);
        if (index !== -1) {
            this.todos[index] = updatedTodo;
            return true;
        } else {
            console.error(`Todo with ID ${updatedTodo.id} not found.`);
            return false;
        }
    }

    /**
     * Toggle completion status of a todo by its ID.
     * @param {string} todoId – The ID of the todo to toggle.
     * @returns {boolean} True if toggled successfully, false otherwise.
     */
    toggleTodoCompletion(todoId) {
        const todo = this.getTodoById(todoId);
        if (todo) {
            todo.toggleCompletion();
            return true;
        } else {
            console.error(`Todo with ID ${todoId} not found.`);
            return false;
        }
    }

    /**
     * Get all completed todos.
     * @returns {Todo[]} Array of completed todos.
     */
    getCompletedTodos() {
        return this.todos.filter((todo) => todo.completed);
    }

    /**
     * Get all incomplete todos.
     * @returns {Todo[]} Array of incomplete todos.
     */
    getIncompleteTodos() {
        return this.todos.filter((todo) => !todo.completed);
    }
}

// localStorage.js

import { Project } from './project.js';
import { Todo } from './todo.js';

/**
 * Manages saving and loading projects to and from localStorage.
 */
export class StorageManager {
    /**
     * Save projects to localStorage.
     * @param {Project[]} projects – Array of Project instances.
     */
    static saveProjects(projects) {
        try {
            const serializedProjects = JSON.stringify(projects);
            localStorage.setItem('projects', serializedProjects);
        } catch (error) {
            console.error('Error saving to localStorage:', error);
        }
    }

    /**
     * Loads projects from localStorage.
     * @returns {Project[]} Array of Project instances.
     */
    static loadProjects() {
        try {
            const serializedProjects = localStorage.getItem('projects');
            if (!serializedProjects) return [];

            const projectsData = JSON.parse(serializedProjects);

            return projectsData.map((projectData) => {
                const project = new Project(projectData.name);

                if (Array.isArray(projectData.todos)) {
                    project.todos = projectData.todos.map((todoData) => {
                        // Validate dueDate
                        if (!todoData.title || !todoData.dueDate || isNaN(Date.parse(todoData.dueDate))) {
                            console.error(`Invalid due date (or title) for todo: ${todoData.dueDate} in project: ${projectData.name}`);
                            // todoData.dueDate = new Date().toISOString().split('T')[0]; // Assign current date if invalid
                            return null; // Skip invalid todos
                        }

                        // Reconstruct Todo instance
                        const todo = new Todo(
                            todoData.title,
                            todoData.dueDate,
                            todoData.description,
                            todoData.priority,
                            todoData.tags,
                            todoData.notes,
                            todoData.id // Pass the existing ID
                        );

                        // Restore completion status
                        todo.completed = todoData.completed || false;

                        // Reconstruct subtasks if any
                        if (Array.isArray(todoData.subtasks)) {
                            todo.subtasks = todoData.subtasks.map((subtaskData) => {
                                const subtask = new Todo(
                                    subtaskData.title,
                                    subtaskData.dueDate,
                                    subtaskData.description,
                                    subtaskData.priority,
                                    subtaskData.tags,
                                    subtaskData.notes,
                                    subtaskData.id
                                );
                            subtask.completed = subtaskData.completed || false;
                            return subtask;
                        }).filter((subtask) => subtask !== null); // Remove invalid subtasks
                    }

                    // todo.recurrence = todoData.recurrence || null;
                    return todo;
                    }).filter((todo) => todo !== null); // Remove invalid todos
                }
            return project;
            });
        } catch (error) {
            console.error('Failed to load projects from localStorage:', error);
            // Optional: clean corrupted data
            localStorage.removeItem('projects');
            alert('Stored data was corrupted and has been reset.');
            // Return an empty array or default projects
            return [];
        }
    }
}

// index.js

import './styles/main.css';
import '@fortawesome/fontawesome-free/css/all.min.css';
import { initializeApp } from './app.js';
import { initializeUI, setupEventListeners } from './ui.js';

/**
 * Initializes the application.
 */
function init() {
    // Initialize the application state and render projects
    initializeApp();

    // Initialize UI interactions and event listeners
    initializeUI();

    // Set up additional event listeners
    setupEventListeners();
}

// Initialize the application on DOMContentLoaded
document.addEventListener('DOMContentLoaded', init);

<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Meta Tags for Character Set and Responsive Design -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>To-Do List App</title>
    <meta name="description" content="A simple and effective to-do list application">

    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
    
    <!-- Font Awesome for Icons -->
     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" integrity="sha512-HK5fgLBL+xXOPQ6ZG/7eAs69/Gk+IrWnrFJf5G5Y7dBTfjBTD22gg8p6BiEm8qpGk7tCq+IIn6bz8RF13X0Yw==" crossorigin="anonymous" />

    <!-- Include CSS -->
    <!-- <link rel="stylesheet" href="main.css"> -->
     <!-- The CSS will be injected by Webpack -->

    <!-- Include SortableJS -->
    <script type="module" src="https://cdn.jsdelivr.net/npm/sortablejs@1.14.0/Sortable.min.js"></script>
</head>
<body>
    <header>
        <button id="theme-toggle" aria-label="Toggle Dark Mode">
            <i class="fas fa-moon"></i>
        </button>
    </header>
    <div class="wrapper">
        <!-- Sidebar -->
         <aside id="sidebar" class="sidebar" role="complementary" aria-label="Project Sidebar">
            <h2>Projects</h2>
            <ul id="project-list">
                <!-- Projects will be rendered here -->
            </ul>
            <form id="add-project">
                <label for="project-name" class="visually-hidden">New Project Name</label>
                <input type="text" id="project-name" placeholder="New Project Name" required aria-required="true">

                <button id="add-project-button" class="btn" tabindex="0" aria-label="Add Project">Add Project</button>
            </form>
            <button id="sidebar-toggle" aria-label="Toggle Sidebar">
                <i class="fas fa-bars"></i></button>
         </aside>

         <!-- Main Content -->
         <main id="app" role="main">
            <h1>To-Do List</h1>

            <div id="todo-container">
                <h2>Todos</h2>
                <!-- Todos will be rendered here -->
            </div>

            <form id="add-todo-form">
                <div id="add-todo">
                    <label for="todo-title" class="visually-hidden">Title</label>
                    <input type="text" id="todo-title" name="todo-title" placeholder="Title" required aria-required="true" autofocus>

                    <label for="todo-description" class="visually-hidden">Description</label>
                    <input type="text" id="todo-description" placeholder="Description">

                    <label for="todo-due-date" class="visually-hidden">Due Date</label>
                    <input type="date" id="todo-due-date" required aria-required="true">

                    <label for="todo-tags">Tags</label>
                    <input type="text" id="todo-tags" placeholder="Enter tags separated by commas">

                    <label for="todo-priority" class="visually-hidden">Priority</label>
                    <select id="todo-priority" aria-label="Priority">
                        <option value="low">Low</option>
                        <option value="medium">Medium</option>
                        <option value="high">High</option>
                    </select>

                    <button type="button" id="add-todo-button" class="btn" aria-label="Add Todo">Add Todo</button>
                </div>
            </form>

            <!-- Edit Task Modal -->
             <div id="edit-task-modal" class="modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="edit-task-title">
                <div class="modal-content">
                    <span class="close-button" aria-label="Close Modal">&times;</span>
                    <h2 id="edit-task-title">Edit task</h2>

                    <label for="edit-title" class="visually-hidden">Title:</label>
                    <input type="text" id="edit-title" required aria-required="true">

                    <label for="edit-description" class="visually-hidden">Description:</label>
                    <input type="text" id="edit-description">

                    <label for="edit-due-date" class="visually-hidden">Due Date:</label>
                    <input type="date" id="edit-due-date">

                    <label for="edit-tags" class="visually-hidden">Tags:</label>
                    <input type="text" id="edit-tags" placeholder="Enter tags separated by commas">

                    <label for="edit-priority" class="visually-hidden">Priority:</label>
                    <select id="edit-priority" aria-label="Edit Priority">
                        <option value="low">Low</option>
                        <option value="medium">Medium</option>
                        <option value="high">High</option>
                    </select>

                    <button id="save-changes-button" class="btn" aria-label="Save Changes">Save changes</button>
                    <button id="cancel-button" class="btn" aria-label="Cancel Editing">Cancel</button>
                </div>
             </div>
        </main>
    </div>
    <div id="toast-container" aria-live="polite" aria-atomic="true"></div>
    
    <!-- <script src=".dist/bundle.js"></script> -->
    <!-- <script src="./src/app.js" defer></script> -->
    <!-- <script type="module" src="./src/index.js" defer></script> -->
     <!-- Include the bundled JavaScript file -->
     <!-- <script src="bundle.js"></script> -->
</body>
</html>

/* main.css */
/* @import "~@fortawsome/fontawesome-free/css/all.min.css"; */

/* CSS Variables for consistent theming */
:root {
    /* Light theme colors */
    --primary-color: #4CAF60;
    --secondary-color: #f9f9f9;
    --accent-color: #FFC107;
    --text-color: #212121;
    --background-color: #FFFFFF;
    --modal-background: rgba(0, 0, 0, 0.4);
    --button-hover-color: #218838;
    --button-bg-color: var(--primary-color);
    --button-text-color: #fff;
    --selected-project-font-weight: bold;
    --font-family: 'Roboto', Arial, Helvetica, sans-serif;
}

[data-theme="dark"] {
    --background-color: #121212;
    --text-color: #FFFFFF;
    --secondary-color: #1e1e1e;
    --accent-color: #bb86fc;
    --modal-background: rgba(255, 255, 255, 0.1);
}

/* Global Box Sizing and Reset */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Base Styles */
body {
    font-family: var(--font-family);
    background-color: var(--background-color);
    padding: 1.25rem; /* 20px */
    color: var(--text-color);
    /* background: linear-gradient(135deg, #153677, #4e085f); */
    /* color: white; */
}

.wrapper {
    display: flex; /* Use flexbox for layout */
    flex-wrap: wrap;
}


/* Sidebar Styling */
.sidebar {
    width: 20vw;
    min-height: 100vh;
    background-color: var(--secondary-color);
    padding: 1.25rem;
    position: fixed;
    overflow-y: auto;
    transition: transform 0.3s ease;
    z-index: 1000;
}

.sidebar.collapsed {
    transform: translateX(-100%);
}

.sidebar-toggle {
    position: fixed;
    top: 10px;
    left: 10px;
    z-index: 1001;
}

.sidebar h2 {
    margin-top: 0;
    color: var(--text-color);
}

.sidebar ul {
    list-style-type: none;
    padding: 0;
}

.sidebar ul li {
    margin: 0.625rem 0;
}

.sidebar ul li.project-item {
    padding: 0.5rem;
    border-radius: 0.25rem;
    cursor: pointer;
    transition: background-color 0.3s ease;
    outline: none;
    
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.sidebar ul li.project-item:hover,
.sidebar ul li.project-item:focus {
    background-color: var(--accent-color);
}

.sidebar ul li.project-item.selected {
    font-weight: var(--selected-project-font-weight);
    background-color: var(--accent-color);
    border-left: 4px solid --primary-color;
}

/* Main Content Styling */
#app {
    flex: 1;
    /* max-width: 600px; */
    margin-left: 22vw; /* Slightly more then sidebar width for spacing */
    /* background: linear-gradient(135deg, #153677, #4e085f); */
    padding: 1.25rem;
    border-radius: 0.625rem;
    background-color: #fff;
    min-height: 100vh;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    transition: margin-left 0.3s ease;
}

.sidebar.collapsed + #app {
    margin-left: 2rem;
}

h1,
h2 {
    color: var(--text-color);
    font-weight: 500;
    margin-bottom: 1rem;
}

/* #project-list,  */
#todo-container {
    background: var(--background-color);
    padding: 0.625rem;
    border-radius: 0.3125rem;
    box-shadow: 0 0 0.625rem rgba(0, 0, 0, 0.1);
    margin-bottom: 1.25rem;
}

.todo-item {
    display: flex;
    /* flex-direction: column; */
    justify-content: space-between;
    align-items: center;
    padding: 0.5rem;
    border-bottom: 1px solid #ddd;
    margin-bottom: 10px;
    border-radius: 5px;
    background-color: var(--secondary-color);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    transition: box-shadow 0.3s ease;
}

.todo-item:hover {
    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2);
}

.todo-item:last-child {
    border-bottom: none;
}

.todo-item.completed .todo-info {
    text-decoration: line-through !important;
    color: #888 !important;
}

/* #project-list li {
    cursor: pointer;
} */

/* #project-list li.project-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
} */

/* Forms and Inputs */
input, select {
    width: calc(100% - 20px);
    padding: 10px;
    margin-bottom: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

button {
    /* padding: 0.625rem; */
    padding: 0.5rem 0.75rem;
    background-color: var(--button-bg-color);
    color: var(--button-text-color);
    border: none;
    border-radius: 0.3125rem;
    cursor: pointer;
    transition: background-color 0.3s ease, transform 0.2 ease;
    margin-left: 0.3125rem;
    font-weight: 500;
}

button:hover,
#project-list li:hover {
    background-color: var(--button-hover-color);
    transform: translateY(-2px);
}

button:focus,
.project-item:focus {
    outline: 3px solid var(--primary-color);
    outline-offset: 2px;
}

/* Priority Classes */
.priority-high {
    color: red;
}

.priority-medium {
    color: goldenrod;
}

.priority-low {
    color: green;
}

/* #edit-task-modal {
    background-color: white;
    padding: 20px;
    border-radius: 5px;
} */

/* Modal Styling */
.modal {
    display: none; /* Hidden by default */
    position: fixed; /* Stay in place */
    z-index: 1002; /* Sit on top */
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto; /* Enable scroll if needed */
    /* background-color: rgb(0, 0, 0);  Fallback color */
    background-color: var(--modal-background) /* Black w/ opacity */
}

.modal-content {
    opacity: 0;
    transform: translateY(-50px);
    animation: modalIn 0.3s forwards;
    background-color: var(--background-color);
    margin: 10% auto; /* 10% from the top and centered */
    padding: 2rem;
    border: 1px solid #888;
    width: 90%; /* Could be more or less, depending on screen size */
    max-width: 31.25rem; /* 500px */
    border-radius: 0.3125rem;
    position: relative;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);

    /* @media (min-width: 600px) {
        width: 60%;
    }

    @media (min-width: 900px) {
        width: 40%;
    } */
}

.modal-content h2 {
    margin-bottom: 1rem;
}

.modal-content input,
.modal-content select {
    width: 100%;
    margin-bottom: 1rem;
    padding: 0.75rem;
    border: 1px solid #ccc;
    border-radius: 5px;
}

.modal-content button {
    margin-right: 0.5rem;
}

@keyframes modalIn {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.close-button {
    color: #aaa;
    /* float: right; */
    top: 10px;
    right: 15px;
    font-size: 1.75rem;
    font-weight: bold;
    cursor: pointer;
    border: none;
    background: none;
}

.close-button:hover,
.close-button:focus {
    color: black;
    text-decoration: none;
    outline: none;
    /* cursor: pointer; */
}

.edit-button,
.delete-button {
    background: none;
    border: none;
    cursor: pointer;
    margin-left: 6px;
}

.edit-button i,
.delete-button i {
    font-size: 1rem;
    color: var(--text-color);
}

/* .selected {
    font-weight: bold;
} */

/* Visually Hidden Class for Accessibility */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}


/* Animations */
.todo-item.fade-in {
    animation: fadeIn 0.5s forwards;
}

.todo-item.fade-out {
    animation: fadeOut 0.5s forwards;
}

@keyframes fadeIn {
    from { 
        opacity: 0; 
    }
    to {
        opacity: 1;
    }
}

@keyframes fadeOut {
    from {
        opacity: 1;
    }
    to {
        opacity: 0;
    }
}

/* Responsive Design */
@media (max-width: 760px) {
    .sidebar {
        width: 100%;
        height: auto;
        position: relative;
        transform: translateX(0);
    }

    #app {
        margin-left: 0;
        max-width: 100%;
    }

    .modal-content {
        width: 95%;
    }
}

/* Scrollbar Styling for WebKit Browsers */
::-webkit-scrollbar {
    width: 8px;
}

::-webkit-scrollbar-track {
    background: var(--secondary-color);
}

::-webkit-scrollbar-thumb {
    background-color: var(--accent-color);
    border-radius: 10px;
    border: 2px solid --secondary-color;
}

/* Invalid Input Styling */
.invalid {
    border-color: red;
}

/* Toast Container Styling */
#toast-container {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1003;
}

.toast {
    background-color: #333;
    color: #fff;
    padding: 15px;
    margin-top: 10px;
    border-radius: 5px;
    opacity: 0;
    animation: fadeInOut 4s forwards;
}

@keyframes fadeInOut {
    0% { opacity: 0; }
    10%, 90% { opacity: 1; }
    100% { opacity: 0; }
}

/* Tags Styling */
.tags-container {
    margin-top: 10px;
}

.tag {
    display: inline-block;
    background-color: var(--accent-color);
    color: #fff;
    padding: 5px 10px;
    border-radius: 5px;
    margin-right: 5px;
    font-size: 0.75rem;
}

// webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// const Dotenv = require('dotenv-webpack'); // Optional: If using environment variables

module.exports = (env, args) => {
    const isProduction = args.mode === 'production';

    return {
        entry: './src/index.js', // Entry point of the application
        output: {
            filename: 'bundle.[contenthash].js', // Output bundle file name (Content hash for cache busting)
            path: path.resolve(__dirname, 'dist'), // Output directory
            publicPath: '', // Adjust if deploying to a subdirectory
            clean: true, // Clean the output directory before emit
        },
        mode: isProduction ? 'production' : 'development', // Set 'production' for optimized builds
        devtool: isProduction ? 'source-map' : 'inline-source-map', // Generate source maps
        devServer: {
            static: {
                directory: path.join(__dirname, 'dist'), // Directory to serve files from
            },
            compress: true,
            port: 9000, // Port number for the dev server
            open: false, // Open browser after starting the server
            hot: true, // Enable hot module replacement
        },
        resolve: {
            extensions: ['.js'],
        },
        optimization: {
            splitChunks: {
                chunks: 'all', // Code splitting
            },
        },
        module: {
            rules: [
                {
                    test: /\.css$/i,
                    use: [
                        isProduction ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader',
                    ], // Handle CSS imports
                },
                {
                    test: /\.js$/i, // Apply this rule to JavaScript files
                    exclude: /node_modules/,
                    // use: [
                    //         {
                    //             loader: 'babel-loader', // Use Babel to transpile JavaScript
                    //             options: {
                    //                 presets: ['@babel/preset-env'],
                    //         },
                    //     },
                    // ],
                },                
                {
                    test:  /\.(png|svg|jpg|jpeg|gif)$/i,
                    type: 'asset/resource', // Handle image assets
                    // Use file-loader if using Webpack versions <5
                    // use: [
                    //     {
                    //         loader: 'file-loader',
                    //         options: {
                    //             name: '[path][name].[ext]',
                    //         },
                    //     },
                    // ],
                },
                {
                    test: /\.(woff|woff2|eot|ttf|otf)$/i,
                    type: 'asset/resource', // Handle font assets
                },
                {
                    test: /\.html$/,
                    use: ['html-loader'], // Handle HTML imports (if any)
                },
            ],
        },
        plugins: [
            new CleanWebpackPlugin(), // Cleans the dist folder
            new HtmlWebpackPlugin({
                template: './src/index.html', // Template HTML file
                filename: 'index.html', // Output HTML file
            }),
            new MiniCssExtractPlugin({
                filename: 'styles.[contenthash].css',
            }),
            // new Dotenv(), // Optional: If using environment variables
        ],
    };
};

// package.json
{
  "name": "todo-list-app",
  "version": "1.0.0",
  "description": "A simple and effective to-do list application",
  "main": "src/index.js",
  "scripts": {
    "start": "webpack serve --mode development",
    "build": "webpack",
    "clean": "rm -rf dist",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "todo",
    "webpack",
    "javascript"
  ],
  "author": "VM",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.25.8",
    "@babel/preset-env": "^7.25.8",
    "@eslint/js": "^9.12.0",
    "babel-loader": "^9.2.1",
    "clean-webpack-plugin": "^4.0.0",
    "css-loader": "^7.1.2",
    "dotenv-webpack": "^8.1.0",
    "eslint": "^9.12.0",
    "file-loader": "^6.2.0",
    "globals": "^15.10.0",
    "html-loader": "^5.1.0",
    "html-webpack-plugin": "^5.6.0",
    "mini-css-extract-plugin": "^2.9.1",
    "sortable": "^2.0.0",
    "sortablejs": "^1.15.3",
    "style-loader": "^4.0.0",
    "webpack": "^5.95.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^5.1.0"
  },
  "dependencies": {
    "@fortawesome/fontawesome-free": "^6.6.0",
    "uuid": "^10.0.0"
  }
}

// .babelrc
{
    "presets": ["@babel/preset-env"]
}