diff --git a/README.md b/README.md index ffe9815..234945c 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,31 @@ -### [JSL11] Agile Board - Kanban Task Management App +# JSL11: Portfolio Piece: Agile Board - Kanban Task Management App -Welcome to the Agile Board project, the final project for the JSL course! In this portfolio piece project, you will be stepping into the shoes of a juniour developer tasked with bringing a Kanban Task Management App to life. You're not starting from scratch, though. In this project, you are tasked with tackling the provided user stories to both identify and fix bugs in the code, as well as to develop your own functions to extend the application's capabilities. Key assignments include importing utility functions, initializing data, and diving into debugging tasks such as setting up data correctly in local storage, dynamically displaying boards and tasks, and enhancing user interactions. +**LOOM video link:** [https://www.loom.com/share/598da8757e9648289d5f563aaadeb155?sid=ed38ce5b-51ee-4536-9b4a-3333e1d5c6b6] -Additionally, you will enhance the application by crafting code to meet specific functionalities outlined in the user stories, like managing task details and their lifecycle, toggling theme customization, and ensuring the persistence of data through local storage. This blend of debugging and creative coding invites you to apply your critical thinking and problem-solving skills in a hands-on manner, equipping you for the intricacies of real-world software development scenarios. +# Introduction -We're providing you with a head start: +The task was to use the provided user stories to identify and fix bugs in the code, as well as to develop our own functions to extend the application's capabilities. The goal was to enhance the application by crafting code to meet specific functionalities outlined in the user stories, like managing task details and their lifecycle, toggling theme customization, etc. -- **Starter Code**: You will receive starter code for the user interface (UI) of the application. This includes the basic layout and some of the JavaScript (JS) functionality needed to make the app interactive. +# Elements Included -- **Your Mission**: Your main task is to complete the app by implementing the features described in the provided user stories. These stories outline the functionality that users expect from the app, such as adding, editing, and deleting tasks, as well as customizing themes and managing the task lifecycle. +Some of the elements in this project were buttons, input fields, modal elements, header elements, and sidebar elements. -- **🪲Important Note on Debugging🚨:** In the sections of the project where you are tasked with debugging the code, *it's crucial to focus on identifying and correcting errors within the existing functions rather than undertaking a complete refactoring of the code.* This means you will need to carefully analyze the provided starter code to pinpoint syntax errors, logical mistakes, or any bugs that prevent the application from functioning as intended. **The goal is to improve and repair the codebase by making precise adjustments, ensuring that the original structure, function logic and intended functionality are preserved.** This approach not only aligns with the project's requirements but also hones the essential skill of debugging— a critical competency for any developer. +# Reflections -### Walkthrough by Coach Kenneth +# Areas of Mastery -Jump into the walkthrough of the project and starter code here: https://www.youtube.com/watch?v=aD8Wx9PGYSc +**Task Management:** I believe I was able to maneouvre through creating, editing, deleting, and displaying the tasks according to their boards and status proficiently. -### Project Overview +# Challenges Faced -As a newly hired developer at Agile Board, a fictional company specializing in innovative task management solutions, you'll embark on an exciting journey to enhance their flagship Kanban Task Management App. +I think this was so far the most demanding project to complete. There were many challenges, one of them being managing the data in localStorage, and ensuring the correct selection and manipulation of DOM elements. -![alt text](assets/JSL11_solution.gif) +# Overall Learning Experience -Your journey through this project will involve several key activities: +Having to fix all the bugs while combining what little UI styling knowledge I have with concepts like DOM manipulation, UI/UX implementation, and event handling almost had me at my breaking point but opened my eyes to daily activities in industry and helped me build on my troubleshooting and overall coding skills. -1. **Exploring the Starter Code**: Begin by familiarizing yourself with the UI and JS functionality we've provided. This will give you a solid understanding of the project's current state and what needs to be done. -2. **Completing User Stories**: Dive into the user stories, which are your roadmap to completing the project. Each story is a feature or functionality that your app needs to support. Your goal is to write the JS code necessary to bring these stories to life. -3. **Testing and Debugging**: As you implement each feature, test your app to ensure it works as expected. Debug any issues that arise to ensure a smooth user experience. -4. **Reflecting on Your Work**: Once you've completed the user stories, take a step back and review your app. Consider the challenges you faced, what you learned, and how you might improve the app further. +# User Stories: Task Interaction and Detail Management -This project is designed to be both challenging and rewarding, providing you with hands-on experience in web development. By the end, you'll have a functional Kanban Task Management App that you can showcase in your portfolio. Ready to get started? Let's dive in! - -## What You Need to Do: - -To complete this challenge, follow these steps: - -1. Clone the provided Starter Code Repository to your local development environment: [Starter Code Repository](https://github.com/CodeSpace-Academy/Final_Project_StudentNo_Classcode_Group_Name-Surname_JSL11). -2. Open the cloned project in your code editor. -3. Code your solution to the user stories. -4. Commit your changes to your local Git repository with meaningful commit messages. -5. Push your local Git repository to your GitHub account. -6. Verify that the changes have been successfully pushed to your GitHub repository. - -🚨 Make sure that you clear the localStorage as you are building your project. This will help with checking that the tasks are loading correctly. - -![alt text](assets/clear-localStorage.gif) - - - -## What You Need to Include: - -1. Ensure that your code includes the necessary modifications to meet the challenge requirements. -2. Your GitHub repository should contain the updated code files. - -# Agile Board Project Feature List - -In this Agile Board Project Feature List, you're introduced to a comprehensive suite of functionalities designed to enrich your Kanban Task Management App. - -As you embark on implementing these features, remember the value of tackling the project one small task at a time. This approach not only makes the process more manageable but also ensures that you can focus on the quality of each feature, leading to a more robust and user-friendly application. Your journey through this project is a great opportunity to apply and hone your skills, so take it step by step and enjoy the learning experience. - -![alt text](assets/task-management-feature.gif) -# Task Interaction and Detail Management - **Clicking an Individual Task for Details**: As a user, I want to click on an individual task so that I can view its details and make edits if necessary. - **Opening the Task Edit Modal**: As a user, I want to open a modal window when adding or editing tasks to easily input task information. - **Updating the Task Title**: As a user, I want to update the task title within the modal to change how it’s displayed on the board. @@ -75,22 +40,29 @@ As you embark on implementing these features, remember the value of tackling the - **Viewing Task Details**: As a user, I want to view detailed information about a task to understand its scope and requirements fully. ![alt text](assets/delete-feature.gif) + # Task Deletion and Confirmation Mechanisms + - **Clicking "Delete Task" Button**: As a user, I want to click a "Delete Task" button within the task edit modal so I can remove tasks that are no longer necessary. - **Immediate UI Update on Task Deletion**: As a user, I expect a task to disappear from the UI immediately after I confirm its deletion to reflect the current state of my task list. -![alt text]() + ![alt text]() # Theme Customization + - **Switching to Dark Mode**: As a user, I want to switch to dark mode so that I can reduce eye strain in low-light conditions. - **Switching Back to Light Mode**: As a user, I want to switch back to light mode from dark mode to better suit bright environments and see the logo update accordingly. ![alt text](assets/sidebar-feature.gif) + # Managing the Sidebar + - **Hiding the Side Bar for More Workspace**: As a user, I want the ability to hide the side bar to gain more workspace. - **Opening the Side Bar for Navigation and Options**: As a user, I want to easily open the side bar to navigate between boards. ![alt text](assets/add-task-feature.gif) + # Task Lifecycle Management + - **Clicking "Add New Task" to Start Adding a Task**: As a user, I want to click the "Add New Task" button so I can begin the process of adding a new task to my board. - **Modal Opens for New Task Input**: As a user, I expect the modal to open when I click "Add New Task" to provide me with a form to input the task's details. - **Adding a Title to the New Task**: As a user, I want to be able to add a title to my new task so I can clearly identify it on the board. @@ -102,7 +74,9 @@ As you embark on implementing these features, remember the value of tackling the - **Editing New Task Details**: As a user, I want to edit the details of the New Task to correct or update information as needed. ![alt text](assets/localStorage-feature.gif) + # Local Storage and Data Persistence + - **Saving New Tasks in localStorage**: As a user, I want my newly created tasks to be saved in localStorage so that my tasks persist even when I close or refresh the browser. - **Reflecting Task Updates in localStorage**: As a user, I expect tasks that I update to have their changes reflected in localStorage so that any modifications are not lost. - **Removing Deleted Tasks from localStorage**: As a user, I want tasks that I delete to be removed from localStorage so that my task list remains accurate and up-to-date. diff --git a/index.html b/index.html index 816d6af..b1958b7 100644 --- a/index.html +++ b/index.html @@ -1,154 +1,217 @@ - - - - + + + - - + + Agile Board Task Management - + - +
- - -
-
- -
-
- -

TODO

-
- -
-
- -
-
- -

DOING

-
- -
-
- -
-
- -

DONE

-
- -
-
-
-
+ +
+
+ +
+
+ +

DONE

+
+ +
+
+ +
-
-
- - -
-
- -
-
- - -
-
- - - -
-
+
+
+ + +
+
+ +
+
+ + +
+
+ + + +
+
-
- - + diff --git a/index.js b/index.js index e54d2c6..e5070ae 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ // TASK: import helper functions from utils // TASK: import initialData - +import { getTasks, createNewTask, patchTask, putTask, deleteTask } from "./utils/taskFunctions.js"; +import { initialData } from "./initialData.js"; /************************************************************************************************************************************************* * FIX BUGS!!! @@ -9,16 +10,57 @@ // Function checks if local storage already has data, if not it loads initialData to localStorage function initializeData() { if (!localStorage.getItem('tasks')) { - localStorage.setItem('tasks', JSON.stringify(initialData)); + localStorage.setItem('tasks', JSON.stringify(initialData)); localStorage.setItem('showSideBar', 'true') } else { console.log('Data already exists in localStorage'); } } +initializeData(); + // TASK: Get elements from the DOM const elements = { - + // Navigation Sidebar + headerBoardName: document.getElementById('header-board-name'), + sideBar: document.querySelector('.side-bar'), + sideBarDiv: document.getElementById('side-bar-div'), + hideSideBarBtn: document.getElementById('hide-side-bar-btn'), + showSideBarBtn: document.getElementById('show-side-bar-btn'), + sideLogoDiv: document.getElementById('logo'), + themeSwitch: document.getElementById('switch'), + boardsNavLinksDiv: document.getElementById('boards-nav-links-div'), + + // Primary layout (header, add task button) + header: document.getElementById('header'), + addNewTaskBtn: document.getElementById('add-new-task-btn'), + deleteBoardBtn: document.getElementById('delete-board-btn'), + dropdownBtn: document.getElementById('dropdownBtn'), + editBoardBtn: document.getElementById('edit-board-btn'), + + // Primary layout (main area for task columns) + columnDivs: document.querySelectorAll('.column-div'), // document.getElementsByClassName('column-div'), + tasksContainer: document.querySelector('.tasks-container'), + + // New task modal(form for adding a new task) + modalWindow: document.getElementById('new-task-modal-window'), + descInput: document.getElementById('desc-input'), + titleInput: document.getElementById('title-input'), + selectStatus: document.getElementById('select-status'), + cancelAddTaskBtn: document.getElementById('cancel-add-task-btn'), + createNewTaskBtn: document.getElementById('add-new-task-btn'), + + // Edit task modal(form for editing an existing task's details) + cancelEditBtn: document.getElementById('cancel-edit-btn'), + deleteTaskBtn: document.getElementById('delete-task-btn'), + editTaskDescInput: document.getElementById('edit-task-desc-input'), + editTaskTitleInput: document.getElementById('edit-task-title-input'), + editSelectStatus: document.getElementById('edit-select-status'), + editTaskForm: document.getElementById('edit-task-form'), + editTaskModal: document.querySelector('.edit-task-modal-window'), + + // Filter div + filterDiv: document.getElementById('filterDiv'), } let activeBoard = "" @@ -27,11 +69,13 @@ let activeBoard = "" // TASK: FIX BUGS function fetchAndDisplayBoardsAndTasks() { const tasks = getTasks(); + console.log(tasks) const boards = [...new Set(tasks.map(task => task.board).filter(Boolean))]; displayBoards(boards); if (boards.length > 0) { const localStorageBoard = JSON.parse(localStorage.getItem("activeBoard")) - activeBoard = localStorageBoard ? localStorageBoard ; boards[0]; + // Replaced the semi-colon with a colon + activeBoard = localStorageBoard ? localStorageBoard : boards[0]; elements.headerBoardName.textContent = activeBoard styleActiveBoard(activeBoard) refreshTasksUI(); @@ -47,29 +91,35 @@ function displayBoards(boards) { const boardElement = document.createElement("button"); boardElement.textContent = board; boardElement.classList.add("board-btn"); - boardElement.click() { + boardElement.addEventListener('click', () => { // Added eventlistener to fix click event elements.headerBoardName.textContent = board; filterAndDisplayTasksByBoard(board); activeBoard = board //assigns active board localStorage.setItem("activeBoard", JSON.stringify(activeBoard)) styleActiveBoard(activeBoard) - }; + }); boardsContainer.appendChild(boardElement); }); } +const columnTitles = { + todo: 'TODO', + doing: 'DOING', + done: 'DONE', +} + // Filters tasks corresponding to the board name and displays them on the DOM. // TASK: Fix Bugs function filterAndDisplayTasksByBoard(boardName) { const tasks = getTasks(); // Fetch tasks from a simulated local storage function - const filteredTasks = tasks.filter(task => task.board = boardName); + const filteredTasks = tasks.filter(task => task.board === boardName); // Used the equality operator '===' for comparison // Ensure the column titles are set outside of this function or correctly initialized before this function runs - elements.columnDivs.forEach(column => { const status = column.getAttribute("data-status"); // Reset column content while preserving the column title + const columnTitle = columnTitles[status]; column.innerHTML = `

${status.toUpperCase()}

@@ -78,14 +128,14 @@ function filterAndDisplayTasksByBoard(boardName) { const tasksContainer = document.createElement("div"); column.appendChild(tasksContainer); - filteredTasks.filter(task => task.status = status).forEach(task => { + filteredTasks.filter(task => task.status === status).forEach(task => { // Used the equality operator '===' for comparison const taskElement = document.createElement("div"); taskElement.classList.add("task-div"); taskElement.textContent = task.title; taskElement.setAttribute('data-task-id', task.id); // Listen for a click event on each task and open a modal - taskElement.click() => { + taskElement.addEventListener('click', () => { // Added an event listener openEditTaskModal(task); }); @@ -102,20 +152,22 @@ function refreshTasksUI() { // Styles the active board by adding an active class // TASK: Fix Bugs function styleActiveBoard(boardName) { - document.querySelectorAll('.board-btn').foreach(btn => { - - if(btn.textContent === boardName) { - btn.add('active') + document.querySelectorAll('.board-btn').forEach(btn => { + + if (btn.textContent === boardName) { + // Added the 'classList' method + btn.classList.add('active'); } else { - btn.remove('active'); + // Added the 'classList' method + btn.classList.remove('active'); } }); } function addTaskToUI(task) { - const column = document.querySelector('.column-div[data-status="${task.status}"]'); + const column = document.querySelector('.column-div[data-status="${task.status}"]'); if (!column) { console.error(`Column not found for status: ${task.status}`); return; @@ -133,16 +185,15 @@ function addTaskToUI(task) { taskElement.className = 'task-div'; taskElement.textContent = task.title; // Modify as needed taskElement.setAttribute('data-task-id', task.id); - - tasksContainer.appendChild(); -} + tasksContainer.appendChild(taskElement); // Appended taskElement to tasksContainer, to ensure that the newly created task is visually added to the appropriate section of the UI, making it visible to the user +} function setupEventListeners() { // Cancel editing task event listener const cancelEditBtn = document.getElementById('cancel-edit-btn'); - cancelEditBtn.click() => toggleModal(false, elements.editTaskModal)); + cancelEditBtn.addEventListener('click', () => toggleModal(false, elements.editTaskModal)); // Cancel adding new task event listener const cancelAddTaskBtn = document.getElementById('cancel-add-task-btn'); @@ -158,8 +209,8 @@ function setupEventListeners() { }); // Show sidebar event listener - elements.hideSideBarBtn.click() => toggleSidebar(false)); - elements.showSideBarBtn.click() => toggleSidebar(true)); + elements.hideSideBarBtn.addEventListener('click', () => toggleSidebar(false)); + elements.showSideBarBtn.addEventListener('click', () => toggleSidebar(true)); // Theme switch event listener elements.themeSwitch.addEventListener('change', toggleTheme); @@ -171,7 +222,7 @@ function setupEventListeners() { }); // Add new task form submission event listener - elements.modalWindow.addEventListener('submit', (event) => { + elements.modalWindow.addEventListener('submit', (event) => { addTask(event) }); } @@ -179,7 +230,8 @@ function setupEventListeners() { // Toggles tasks modal // Task: Fix bugs function toggleModal(show, modal = elements.modalWindow) { - modal.style.display = show ? 'block' => 'none'; + // Corrected ternary operator syntax + modal.style.display = show ? 'block' : 'none'; } /************************************************************************************************************************************************* @@ -187,71 +239,153 @@ function toggleModal(show, modal = elements.modalWindow) { * **********************************************************************************************************************************************/ function addTask(event) { - event.preventDefault(); + event.preventDefault(); //Assign user input to the task object - const task = { - - }; - const newTask = createNewTask(task); - if (newTask) { - addTaskToUI(newTask); - toggleModal(false); - elements.filterDiv.style.display = 'none'; // Also hide the filter overlay - event.target.reset(); - refreshTasksUI(); - } + const task_id = JSON.parse(localStorage.getItem('id')); // Retrieves value from browser's local storage + const titleInput = elements.titleInput.value; // Captures value entered in an input field, such as a task title. + const descInput = elements.descInput.value; // Captures value entered in a textarea field, such as a task description. + const selectStatus = elements.selectStatus.value; // Captures selected value from a dropdown list, indicating the status or category of the task + + // 'task' object to store info such as title, description, etc. + const task = { + title: document.getElementById('title-input').value, + desc: document.getElementById('desc-input').value, + status: document.getElementById('select-status').value, + board: activeBoard, + }; + + const newTask = createNewTask(task); + if (newTask) { + addTaskToUI(newTask); + toggleModal(false); + elements.filterDiv.style.display = 'none'; // Also hide the filter overlay + event.target.reset(); + refreshTasksUI(); + } } -function toggleSidebar(show) { - +function toggleSidebar(show) { // Controls the visibility of a sidebar in the user interface + elements.sideBar.style.display = show ? 'block' : 'none'; + elements.showSideBarBtn.style.display = show ? 'none' : 'block'; } + +// Get current theme from local storage or set to default (light) +const currentMode = localStorage.getItem('mode') || 'light'; +let isLightMode = currentMode === 'light'; + +// Set the initial SVG source based on the current mode +let sideLogoDivSrc = isLightMode ? './assets/logo-dark.svg' : './assets/logo-light.svg'; +elements.sideLogoDiv.src = sideLogoDivSrc; + + function toggleTheme() { - -} + //const isDarkTheme = document.body.classList.contains('dark-theme'); + //localStorage.setItem('dark-theme', isDarkTheme? 'enabled' :'disabled'); + if (localStorage.getItem('light-theme') == 'enable') { + document.body.classList.toggle('light-theme', false); + localStorage.setItem('light-theme', 'disable'); + let img = document.getElementById('logo'); + img.src = './assets/logo-dark.svg'; + } + else { + document.body.classList.toggle('light-theme', true); + localStorage.setItem('light-theme', 'enable'); + let img = document.getElementById('logo'); + img.src = './assets/logo-light.svg'; + } +} function openEditTaskModal(task) { // Set task details in modal inputs - + elements.editTaskTitleInput.value = task.title; // Populates task's title in an input field for editing + elements.editTaskDescInput.value = task.description; // Populates task's description in input field for editing + elements.editSelectStatus.value = task.status; // Sets selected status // Get button elements from the task modal - + const saveChangesBtn = document.getElementById('save-task-changes-btn'); // Retrieves element from HTML that represents a button that saves changes made to task + const deleteTaskBtn = document.getElementById('delete-task-btn'); // Retrieves element from HTML that represents a button that deletes task + const cancelEditTaskBtn = document.getElementById('cancel-edit-btn'); // Call saveTaskChanges upon click of Save Changes button - + saveChangesBtn.addEventListener('click', () => { + saveTaskChanges(task.id); + toggleModal(false, elements.editTaskModal); + }); // Delete task using a helper function and close the task modal + deleteTaskBtn.addEventListener('click', () => { // Included an event listener that triggers deletion of specified task 'deleteTask(task.id)', while also hiding the task modal, and then refreshing the tasks UI + deleteTask(task.id); + toggleModal(false, elements.editTaskModal); + refreshTasksUI(); + }); + cancelEditTaskBtn.addEventListener('click', () => { + toggleModal(false, elements.editTaskModal); + }); toggleModal(true, elements.editTaskModal); // Show the edit task modal + refreshTasksUI(); } + function saveTaskChanges(taskId) { // Get new user inputs - + const task_id = JSON.parse(localStorage.getItem('id')); // Fetches task ID from local storage + const titleInput = elements.editTaskTitleInput.value; // Fetches current value entered that aloows users to input/edit title of a task + const descriptionInput = elements.editTaskDescInput.value; // Allows users to input/edit description or details of a task + const selectStatus = elements.editSelectStatus.value; // Chooses the status of the task // Create an object with the updated task details + const updatedTask = { + title: titleInput, + description: descriptionInput, + status: selectStatus, + board: activeBoard, + }; - - // Update task using a hlper functoin - + // Update task using a helper function + patchTask(taskId, updatedTask); // Takes two arguments to update task identified by 'taskId' with new data provided in 'updatedTask' // Close the modal and refresh the UI to reflect the changes - + // location.reload(); + toggleModal(false, elements.editTaskModal); refreshTasksUI(); } +const displayStoredTasks = () => { + // Retrieving the tasks from localStorage + const storedTasks = localStorage.getItem('tasks'); + + if (storedTasks) { + // Parsing the JSON string to an array of tasks + const tasks = JSON.parse(storedTasks); + + // Logging tasks to console + console.log(tasks); + } else { + console.log('No tasks stored in localStorage') + } +} + +// Calling the function to display the stored tasks +displayStoredTasks(); + /*************************************************************************************************************************************************/ -document.addEventListener('DOMContentLoaded', function() { +document.addEventListener('DOMContentLoaded', function () { init(); // init is called after the DOM is fully loaded }); function init() { + // Ensure that the correct logo image is displayed based on the stored value + /* if (localStorage.getItem('sideLogoDiv') === './assets/logo-light.svg') { + elements.sideLogoDiv.src = './assets/logo-light.svg'; + } */ setupEventListeners(); const showSidebar = localStorage.getItem('showSideBar') === 'true'; toggleSidebar(showSidebar);