diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..aef8443 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/README.md b/README.md index ffe9815..9443614 100644 --- a/README.md +++ b/README.md @@ -1,108 +1,3 @@ ### [JSL11] 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. - -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. - -We're providing you with a head start: - -- **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. - -- **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. - -- **🪲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. - -### Walkthrough by Coach Kenneth - -Jump into the walkthrough of the project and starter code here: https://www.youtube.com/watch?v=aD8Wx9PGYSc - -### Project Overview - -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. - -![alt text](assets/JSL11_solution.gif) - -Your journey through this project will involve several key activities: - -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. - -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. -- **Updating the Task Description**: As a user, I want to update the task description within the modal so I can better describe what needs to be done. -- **Updating the Task Status**: As a user, I want to update the current status of a task (todo, doing, done) to track its progress. -- **Saving Task Changes**: As a user, I want to save the changes I make to a task so that the updated details are stored and displayed. -- **Updating the UI with Task Changes**: As a user, I expect the changes I make to a task to be reflected immediately on the UI without needing to refresh. -- **Deleting a Task from the Edit Modal**: As a user, I want the ability to delete a task directly from the edit modal if it’s no longer needed. -- **Canceling Edits Without Saving**: As a user, I want to be able to cancel my edits and close the modal without saving to avoid accidental changes. -- **Editing Task Details**: As a user, I want to edit the details of an existing task to correct or update information as needed. -- **Easy Navigation Between Task Statuses**: As a user, I want to easily move tasks between statuses (todo, doing, done) to reflect their current progress. -- **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]() - -# 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. -- **Adding a Description to the New Task**: As a user, I want to be able to add a description to my new task to provide more details about what needs to be done. -- **Selecting a Status for the New Task**: As a user, I want to select a status for my new task (e.g., Todo, Doing, Done) to categorize it based on its progress. -- **Creating the New Task**: As a user, I want to click a "Create Task" button in the modal to save the new task to the board. -- **New Task Appears in UI Under Correct Status**: As a user, I expect the new task to appear in the UI under the correct status column immediately after creation. -- **Viewing New Task Details**: As a user, I want to view detailed information about the New Task to understand its scope and requirements fully. -- **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. +Presentation Link : [https://www.loom.com/share/d51db3e9d10145349e6e45f8cc4ec74a?sid=e402d4a0-1dca-453e-893b-342fdd801092] diff --git a/git b/git new file mode 100644 index 0000000..e69de29 diff --git a/index.css b/index.css index 92c3dec..f7b603f 100644 --- a/index.css +++ b/index.css @@ -62,6 +62,11 @@ body { width: 350px; } +.side-bar-bottom{ + position:absolute; + bottom: 0; +} + .boards-nav-links-div { display: flex; flex-direction: column; diff --git a/index.html b/index.html index 6b0a73c..640407e 100644 --- a/index.html +++ b/index.html @@ -148,7 +148,7 @@
- + diff --git a/index.js b/main.js similarity index 54% rename from index.js rename to main.js index e54d2c6..d1f3387 100644 --- a/index.js +++ b/main.js @@ -1,11 +1,13 @@ // TASK: import helper functions from utils +import { deleteTask, getTasks } from "./utils/taskFunctions.js"; +import { saveTasks } from "./utils/taskFunctions.js"; +import {createNewTask} from "./utils/taskFunctions.js"; +import {patchTask} from "./utils/taskFunctions.js"; +import {putTask} from "./utils/taskFunctions.js"; // TASK: import initialData +import { initialData } from "./initialData.js"; -/************************************************************************************************************************************************* - * FIX BUGS!!! - * **********************************************************************************************************************************************/ - // Function checks if local storage already has data, if not it loads initialData to localStorage function initializeData() { if (!localStorage.getItem('tasks')) { @@ -16,30 +18,39 @@ function initializeData() { } } -// TASK: Get elements from the DOM +// Get elements from the DOM const elements = { - + headerBoardName : document.getElementById("header-board-name"), + columnDivs : document.querySelectorAll(".column-div"), + filterDiv : document.getElementById("filterDiv"), + hideSideBarBtn : document.getElementById("hide-side-bar-btn"), + showSideBarBtn: document.getElementById("show-side-bar-btn"), + themeSwitch : document.getElementById("switch"), + createNewTaskBtn : document.getElementById("add-new-task-btn"), + modalWindow : document.getElementById("new-task-modal-window"), + editTaskModal : document.querySelector(".edit-task-modal-window"), + taskContainerEl : document.querySelector("tasks-container"), + logo : document.getElementById("logo") } -let activeBoard = "" +let activeBoard = ""; -// Extracts unique board names from tasks -// TASK: FIX BUGS -function fetchAndDisplayBoardsAndTasks() { - const tasks = getTasks(); +function fetchAndDisplayBoardsAndTasks(){ + const tasks = getTasks(); const boards = [...new Set(tasks.map(task => task.board).filter(Boolean))]; - displayBoards(boards); - if (boards.length > 0) { + + console.log(boards); + displayBoards(boards) + + if(boards.length > 0){ const localStorageBoard = JSON.parse(localStorage.getItem("activeBoard")) - activeBoard = localStorageBoard ? localStorageBoard ; boards[0]; + activeBoard = localStorageBoard ? localStorageBoard : boards[0]; elements.headerBoardName.textContent = activeBoard - styleActiveBoard(activeBoard) + styleActiveBoard(activeBoard); refreshTasksUI(); } } -// Creates different boards in the DOM -// TASK: Fix Bugs function displayBoards(boards) { const boardsContainer = document.getElementById("boards-nav-links-div"); boardsContainer.innerHTML = ''; // Clears the container @@ -47,27 +58,27 @@ function displayBoards(boards) { const boardElement = document.createElement("button"); boardElement.textContent = board; boardElement.classList.add("board-btn"); - boardElement.click() { + boardElement.addEventListener("click" , () => { elements.headerBoardName.textContent = board; filterAndDisplayTasksByBoard(board); activeBoard = board //assigns active board localStorage.setItem("activeBoard", JSON.stringify(activeBoard)) styleActiveBoard(activeBoard) - }; + }); boardsContainer.appendChild(boardElement); }); } -// 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); + + console.log(filteredTasks); - // Ensure the column titles are set outside of this function or correctly initialized before this function runs + // Ensure the column titles are set outside of this function or correctly initialized before this function runs - elements.columnDivs.forEach(column => { + elements.columnDivs.forEach(column => { const status = column.getAttribute("data-status"); // Reset column content while preserving the column title column.innerHTML = `
@@ -78,14 +89,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 => { 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" , () => { openEditTaskModal(task); }); @@ -94,34 +105,33 @@ function filterAndDisplayTasksByBoard(boardName) { }); } - -function refreshTasksUI() { +function refreshTasksUI(){ filterAndDisplayTasksByBoard(activeBoard); } -// Styles the active board by adding an active class -// TASK: Fix Bugs -function styleActiveBoard(boardName) { - document.querySelectorAll('.board-btn').foreach(btn => { +function styleActiveBoard(boardName){ + document.querySelectorAll('.board-btn').forEach(btn => { if(btn.textContent === boardName) { - btn.add('active') + btn.classList.add('active') } else { - btn.remove('active'); + btn.classList.remove('active'); } }); } - function addTaskToUI(task) { const column = document.querySelector('.column-div[data-status="${task.status}"]'); if (!column) { console.error(`Column not found for status: ${task.status}`); return; + }else{ + console.log("column found"); } let tasksContainer = column.querySelector('.tasks-container'); + if (!tasksContainer) { console.warn(`Tasks container not found for status: ${task.status}, creating one.`); tasksContainer = document.createElement('div'); @@ -134,15 +144,14 @@ function addTaskToUI(task) { taskElement.textContent = task.title; // Modify as needed taskElement.setAttribute('data-task-id', task.id); - tasksContainer.appendChild(); + tasksContainer.appendChild(taskElement); + } - - 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 +167,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); @@ -172,27 +181,28 @@ function setupEventListeners() { // Add new task form submission event listener elements.modalWindow.addEventListener('submit', (event) => { - addTask(event) + addTask(event); }); } -// Toggles tasks modal -// Task: Fix bugs function toggleModal(show, modal = elements.modalWindow) { - modal.style.display = show ? 'block' => 'none'; + modal.style.display = show ? 'block' : 'none'; } -/************************************************************************************************************************************************* - * COMPLETE FUNCTION CODE - * **********************************************************************************************************************************************/ - function addTask(event) { event.preventDefault(); //Assign user input to the task object const task = { - + id : Date.now(), + title : elements.modalWindow.querySelector("#title-input").value, + description: elements.modalWindow.querySelector("#desc-input").value, + status: elements.modalWindow.querySelector("#select-status").value, + board : activeBoard }; + + console.log(task); + const newTask = createNewTask(task); if (newTask) { addTaskToUI(newTask); @@ -203,28 +213,68 @@ function addTask(event) { } } +function toggleSidebar(show){ -function toggleSidebar(show) { - + const sideBarEl = document.querySelector(".side-bar"); + + switch(show){ + case true: + sideBarEl.style.display = "block"; + elements.showSideBarBtn.style.display = "none"; + + break; + case false: + sideBarEl.style.display = "none"; + elements.showSideBarBtn.style.display = "block"; + break; +} } function toggleTheme() { - -} + const isLightTheme = document.body.classList.toggle('light-theme'); + localStorage.setItem("light-theme", isLightTheme ? "enabled" : "disabled"); + elements.logo.src = isLightTheme ? "./assets/logo-light.svg" : "./assets/logo-dark.svg"; + +} function openEditTaskModal(task) { // Set task details in modal inputs - + const taskDetails = { + title : task.title, + description : task.description, + status : task.status + } - // Get button elements from the task modal +const title = elements.editTaskModal.querySelector("#edit-task-title-input"); +const description = elements.editTaskModal.querySelector("#edit-task-desc-input"); +const status = elements.editTaskModal.querySelector("#edit-select-status"); + +title.value = taskDetails.title +description.value = taskDetails.description +status.value = taskDetails.status + +elements.editTaskModal.style.display = "block"; + // Get button elements from the task modal +const saveEditBtn = document.getElementById("save-task-changes-btn"); +const cancelEditBtn = document.getElementById("cancel-edit-btn"); +const deleteEditBtn = document.getElementById("delete-task-btn"); // Call saveTaskChanges upon click of Save Changes button + saveEditBtn.addEventListener("click" , () => { + saveTaskChanges(task.id); + toggleModal(false, elements.editTaskModal); + }); // Delete task using a helper function and close the task modal + deleteEditBtn.addEventListener("click", () => { + deleteTask(task.id); + toggleModal(false, elements.editTaskModal); + refreshTasksUI(); + }); toggleModal(true, elements.editTaskModal); // Show the edit task modal @@ -232,30 +282,39 @@ function openEditTaskModal(task) { function saveTaskChanges(taskId) { // Get new user inputs - + const titleEl = elements.editTaskModal.querySelector("#edit-task-title-input"); +const descriptionEl = elements.editTaskModal.querySelector("#edit-task-desc-input"); +const statusEl = elements.editTaskModal.querySelector("#edit-select-status"); // Create an object with the updated task details + const updatedTaskDetails = { + title : titleEl.value, + description : descriptionEl.value, + status : statusEl.value + } // Update task using a hlper functoin + const details = patchTask(taskId,updatedTaskDetails); +; - // Close the modal and refresh the UI to reflect the changes refreshTasksUI(); } -/*************************************************************************************************************************************************/ - document.addEventListener('DOMContentLoaded', function() { init(); // init is called after the DOM is fully loaded }); function init() { + initializeData(); setupEventListeners(); const showSidebar = localStorage.getItem('showSideBar') === 'true'; toggleSidebar(showSidebar); const isLightTheme = localStorage.getItem('light-theme') === 'enabled'; document.body.classList.toggle('light-theme', isLightTheme); - fetchAndDisplayBoardsAndTasks(); // Initial display of boards and tasks + fetchAndDisplayBoardsAndTasks(); + +// Initial display of boards and tasks } \ No newline at end of file diff --git a/utils/taskFunctions.js b/utils/taskFunctions.js index 741c179..e099e7c 100644 --- a/utils/taskFunctions.js +++ b/utils/taskFunctions.js @@ -7,7 +7,7 @@ export const getTasks = () => { }; // Simulate saving tasks to localStorage -const saveTasks = (tasks) => { +export const saveTasks = (tasks) => { localStorage.setItem('tasks', JSON.stringify(tasks)); };