├── public ├── favicon.png ├── Microsoft To Do Logo.png └── vite.svg ├── src ├── assets │ ├── Logo.png │ └── react.svg ├── main.jsx ├── Components │ ├── Sidebar.jsx │ ├── Navbar.jsx │ ├── Clock.jsx │ ├── RightBar.jsx │ └── Calendar.jsx ├── App.jsx ├── App.css └── Tasks.jsx ├── index.html ├── .gitignore ├── package.json └── README.md /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayyjeb/taskly-to-do-app/HEAD/public/favicon.png -------------------------------------------------------------------------------- /src/assets/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayyjeb/taskly-to-do-app/HEAD/src/assets/Logo.png -------------------------------------------------------------------------------- /public/Microsoft To Do Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayyjeb/taskly-to-do-app/HEAD/public/Microsoft To Do Logo.png -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | 5 | createRoot(document.getElementById("root")).render( 6 | <> 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /src/Components/Sidebar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ClockTimer from "./Clock"; 3 | import Calendar from "./Calendar"; 4 | const Sidebar = () => { 5 | return ( 6 |
7 | 8 | 9 | 10 |
11 | ); 12 | }; 13 | 14 | export default Sidebar; 15 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./App.css"; 4 | import Navbar from "./Components/Navbar"; 5 | import Tasks from "./Tasks"; 6 | import Sidebar from "./Components/Sidebar"; 7 | function App() { 8 | return ( 9 | <> 10 | 11 | 12 | 13 | ); 14 | } 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Taskly | Home 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | package-lock.json 15 | eslint.config.js 16 | vite.config.js 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | 28 | -------------------------------------------------------------------------------- /src/Components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Logo from "../assets/Logo.png"; 3 | 4 | const Navbar = () => { 5 | return ( 6 |
7 |
8 | 9 |
10 |
11 | 15 |
16 |
17 | ); 18 | }; 19 | 20 | export default Navbar; 21 | -------------------------------------------------------------------------------- /src/Components/Clock.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | 3 | function ClockTimer() { 4 | const [time, setTime] = useState(new Date()); 5 | 6 | useEffect(() => { 7 | const interval = setInterval(() => setTime(new Date()), 1000); 8 | 9 | return () => { 10 | clearInterval(interval); 11 | }; 12 | }, []); 13 | 14 | return ( 15 |
16 |

17 | {time.toLocaleTimeString([], { 18 | hour: "2-digit", 19 | minute: "2-digit", 20 | hour12: true, 21 | })} 22 |

23 |
24 | ); 25 | } 26 | 27 | export default ClockTimer; 28 | -------------------------------------------------------------------------------- /src/Components/RightBar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Sidebar = () => { 4 | return ( 5 |
6 | 16 |
17 | App launch illustration 21 |
22 |
23 | ); 24 | }; 25 | 26 | export default Sidebar; 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "taskly-to-do-app", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^17.0.0 || ^18.0.0", 14 | "react-clock": "^5.1.0", 15 | "react-dom": "^17.0.0 || ^18.0.0", 16 | "react-icons": "^5.3.0", 17 | "react-live-clock": "^6.1.24", 18 | "react-open-weather": "^1.3.8", 19 | "styled-components": "^6.1.13" 20 | }, 21 | "peerDependencies": { 22 | "react": "^17.0.0 || ^18.0.0", 23 | "react-dom": "^17.0.0 || ^18.0.0" 24 | }, 25 | "devDependencies": { 26 | "@eslint/js": "^9.13.0", 27 | "@types/react": "^18.3.11", 28 | "@types/react-dom": "^18.3.1", 29 | "@vitejs/plugin-react": "^4.3.3", 30 | "eslint": "^9.13.0", 31 | "eslint-plugin-react": "^7.37.1", 32 | "eslint-plugin-react-hooks": "^5.0.0", 33 | "eslint-plugin-react-refresh": "^0.4.13", 34 | "globals": "^15.11.0", 35 | "tailwindcss": "^3.4.14", 36 | "vite": "^5.4.9" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](https://github.com/user-attachments/assets/3629e03d-5d01-4aef-9b04-68e5f098869a) 2 | # Taskly 3 | 4 | Taskly is a simple, user-friendly to-do list web application designed to help users manage their tasks efficiently. The app allows users to add, edit, delete, and mark tasks as completed while tracking their progress. All task data is stored in the browser’s session storage, ensuring that tasks remain intact during a session. Additionally, users are greeted with a refreshing success-based quote in the welcome text, which changes randomly using an API. 5 | 6 | ## Features 7 | 8 | - **Add New Tasks:** Users can add new tasks to their to-do list. 9 | - **Edit Existing Tasks:** Users can edit any task in the list. 10 | - **Delete Tasks:** Users can remove any task from their list. 11 | - **Mark Tasks as Completed:** Completed tasks are displayed with a line-through style for easy identification. 12 | - **Progress Tracker:** Displays the number of completed tasks out of the total tasks. 13 | - **Random Success-Based Quotes:** Users see refreshing success-based quotes in the welcome text, which change randomly through an API. 14 | 15 | ## Installation 16 | 17 | 1. Clone the repository: 18 | ```bash 19 | git clone https://github.com/rayyjeb/taskly-to-do-app.git 20 | 21 | 2. Navigate to the project directory: 22 | ```bash 23 | cd Taskly 24 | 25 | 3. Install Dependencies: 26 | ```bash 27 | npm install 28 | 29 | 30 | ## Usage 31 | 32 | 1. To Start the application 33 | ```bash 34 | npm start 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Components/Calendar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | 3 | const Calendar = () => { 4 | const [currentDate, setCurrentDate] = useState(new Date()); 5 | 6 | const year = currentDate.getFullYear(); 7 | const month = currentDate.getMonth(); 8 | const today = currentDate.getDate(); 9 | 10 | const firstDayOfMonth = new Date(year, month, 1).getDay(); 11 | const daysInMonth = new Date(year, month + 1, 0).getDate(); 12 | 13 | const daysArray = Array.from({ length: firstDayOfMonth }, () => ""); 14 | for (let day = 1; day <= daysInMonth; day++) { 15 | daysArray.push(day); 16 | } 17 | 18 | return ( 19 |
20 |
21 |

22 | {currentDate.toLocaleDateString("default", { 23 | month: "long", 24 | year: "numeric", 25 | })} 26 |

27 |
28 | {["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((day) => ( 29 |
30 | {day} 31 |
32 | ))} 33 | {daysArray.map((day, index) => ( 34 |
38 | {day} 39 |
40 | ))} 41 |
42 |
43 |
44 | 45 |
46 |
47 | ); 48 | }; 49 | 50 | export default Calendar; 51 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | /* Import Google Fonts */ 2 | @import url('https://fonts.googleapis.com/css2?family=Italiana&family=Urbanist:ital,wght@0,100..900;1,100..900&display=swap'); 3 | 4 | /* Universal font styling */ 5 | * { 6 | font-family: "Urbanist", sans-serif; 7 | } 8 | 9 | body { 10 | overflow: hidden; 11 | } 12 | 13 | /* Flex utility class */ 14 | .flex { 15 | display: flex; 16 | } 17 | 18 | /* Sidebar or Task section */ 19 | .taskSide { 20 | height: auto; 21 | } 22 | 23 | /* Logo styling */ 24 | .Logo { 25 | width: 8rem; 26 | } 27 | 28 | /* Avatar icon styling */ 29 | .Avatar { 30 | width: 3rem; 31 | } 32 | 33 | /* Navbar styling */ 34 | .Navbar { 35 | display: flex; 36 | justify-content: space-between; 37 | align-items: center; 38 | margin: 3rem; 39 | user-select: none; 40 | } 41 | 42 | /* Main container for task section */ 43 | .parentMain { 44 | display: flex; 45 | flex-direction: column; 46 | gap: 1rem; 47 | overflow-y: scroll; 48 | overflow-x: hidden; 49 | height: 80vh; 50 | width: 30vmax 51 | } 52 | 53 | /* Hides scrollbar in parentMain */ 54 | .parentMain::-webkit-scrollbar { 55 | display: none; 56 | } 57 | 58 | /* Center container */ 59 | .GrandParentDiv { 60 | display: flex; 61 | justify-content: center; 62 | gap: 8vmax; 63 | } 64 | 65 | /* Main header styling */ 66 | .MainHeaderDiv { 67 | display: flex; 68 | flex-direction: column; 69 | line-height: 5%; 70 | } 71 | 72 | /* Styling for main header text */ 73 | .MainHeaderDiv>h1 { 74 | font-weight: 500; 75 | font-size: 1.95rem; 76 | } 77 | 78 | /* Subheader text styling */ 79 | .MainHeaderDiv>h3 { 80 | font-weight: 200; 81 | font-size: 0.95rem; 82 | width: 21rem; 83 | line-height: 120%; 84 | letter-spacing: 0.3px; 85 | } 86 | 87 | /* Input section styling */ 88 | .inputTaskDiv { 89 | display: flex; 90 | gap: 0.5rem; 91 | width: 100%; 92 | } 93 | 94 | .inputTaskDiv>input { 95 | padding: 1rem 0; 96 | width: 100%; 97 | padding-left: 20px; 98 | border-radius: 5rem; 99 | } 100 | 101 | .inputBox { 102 | border: 1px solid rgba(80, 80, 80, 0.311); 103 | } 104 | 105 | /* Input box focus styling */ 106 | .inputBox:focus { 107 | outline: none; 108 | } 109 | 110 | /* Button styling in input section */ 111 | .inputTaskDiv button { 112 | background-color: #51AF4F; 113 | color: white; 114 | border-radius: 1rem; 115 | padding: 0 1rem; 116 | border: none; 117 | } 118 | 119 | /* Completed tasks message styling */ 120 | .completed-tasks-message { 121 | background-color: #ecfaf0; 122 | border-radius: 1rem; 123 | } 124 | 125 | .completed-tasks-message h5 { 126 | padding: 0 1rem; 127 | } 128 | 129 | /* Task list styling */ 130 | #todolist { 131 | padding: 0; 132 | } 133 | 134 | /* Task item styling */ 135 | #todolist li { 136 | display: flex; 137 | justify-content: space-between; 138 | padding: 1rem 0; 139 | margin-top: 1rem; 140 | background-color: #F6F7FB; 141 | border-radius: 1rem; 142 | } 143 | 144 | /* Input styling for editing tasks */ 145 | .editInput { 146 | width: 80%; 147 | border-radius: 2rem; 148 | padding: 0.5rem; 149 | border: #51AF4F; 150 | } 151 | 152 | #item { 153 | display: flex; 154 | } 155 | 156 | /* Reset button styling */ 157 | .btnReset { 158 | background-color: rgb(243, 105, 105); 159 | color: white; 160 | border: none; 161 | font-weight: 500; 162 | border-radius: 2rem; 163 | padding: 0.5rem; 164 | } 165 | 166 | /* Clock text styling */ 167 | .clockText { 168 | font-weight: 500; 169 | font-size: 1.95rem; 170 | } 171 | 172 | /* Calendar container styling */ 173 | .calendar { 174 | text-align: center; 175 | } 176 | 177 | .calendar h2 { 178 | margin-bottom: 1rem; 179 | font-weight: 300; 180 | font-size: 1rem; 181 | } 182 | 183 | /* Calendar grid styling */ 184 | .calendar-grid { 185 | display: grid; 186 | grid-template-columns: repeat(7, 1fr); 187 | gap: 5px; 188 | } 189 | 190 | /* Day header styling for calendar */ 191 | .day-header { 192 | font-weight: 600; 193 | padding: 5px; 194 | font-size: 0.8rem; 195 | } 196 | 197 | /* Day cell styling */ 198 | .day-cell { 199 | padding: 10px; 200 | border: 0.5px solid #ccc; 201 | font-size: 1rem; 202 | font-weight: 300; 203 | border-radius: 5px; 204 | } 205 | 206 | /* Current day highlight in calendar */ 207 | .today { 208 | background-color: #4caf50; 209 | color: white; 210 | font-weight: bold; 211 | } 212 | 213 | /* Illustration styling */ 214 | .illustrations { 215 | margin-top: 3rem; 216 | } 217 | 218 | .illustrations img { 219 | width: 20rem; 220 | } 221 | 222 | /* Right side background styling */ 223 | .rightSideDiv { 224 | background-color: #ecfaf0; 225 | } 226 | 227 | /* Responsive design for smaller screens */ 228 | @media only screen and (max-width: 1160px) { 229 | .fixed { 230 | display: none; 231 | } 232 | } -------------------------------------------------------------------------------- /src/Tasks.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import ClockTimer from "./Components/Clock"; 3 | import Sidebar from "./Components/Sidebar"; 4 | import RightBar from "./Components/RightBar"; 5 | 6 | const Tasks = () => { 7 | const [inputText, setInputText] = useState(""); 8 | const [todoArr, setTodoArr] = useState([]); 9 | const [editIndex, setEditIndex] = useState(null); 10 | const [editText, setEditText] = useState(""); 11 | const [quote, setQuote] = useState(""); 12 | const [author, setAuthor] = useState(""); 13 | const [loading, setLoading] = useState(true); 14 | // For Fetching Quotes 15 | useEffect(() => { 16 | const fetchQuote = async () => { 17 | try { 18 | const response = await fetch( 19 | "https://quotes-api-self.vercel.app/quote" 20 | ); 21 | const data = await response.json(); 22 | console.log("data: ", data); 23 | setQuote(data.quote); 24 | setAuthor(data.author); 25 | } catch (error) { 26 | console.error("Error fetching quote:", error); 27 | } 28 | }; 29 | 30 | fetchQuote(); 31 | }, []); 32 | // For Fetching Todo List using dummy api 33 | 34 | useEffect(() => { 35 | const fetchTodos = async () => { 36 | setLoading(true); 37 | try { 38 | const response = await fetch("https://dummyjson.com/todos"); 39 | const data = await response.json(); 40 | const todos = data.todos.map((item) => ({ 41 | text: item.todo, 42 | completed: item.completed, 43 | })); 44 | setTodoArr(todos); 45 | } catch (error) { 46 | console.error("Error fetching todos:", error); 47 | } finally { 48 | setLoading(false); 49 | } 50 | }; 51 | 52 | fetchTodos(); 53 | }, []); 54 | 55 | // Save todos to local storage whenever todoArr changes 56 | useEffect(() => { 57 | localStorage.setItem("todoarr", JSON.stringify(todoArr)); 58 | }, [todoArr]); 59 | 60 | // Functionality to add a new item to the list 61 | const addItemToArray = () => { 62 | if (inputText.trim() !== "") { 63 | // Prepend the new task to the existing todoArr 64 | setTodoArr([{ text: inputText, completed: false }, ...todoArr]); 65 | setInputText(""); 66 | } 67 | }; 68 | 69 | // Functionality to toggle complete status 70 | const toggleComplete = (index) => { 71 | const updatedArr = todoArr.map((item, i) => 72 | i === index ? { ...item, completed: !item.completed } : item 73 | ); 74 | setTodoArr(updatedArr); // Update the state 75 | }; 76 | 77 | // Functionality to delete an item 78 | const deleteItem = (index) => { 79 | const updatedArr = todoArr.filter((_, i) => i !== index); 80 | setTodoArr(updatedArr); // Update the state 81 | }; 82 | 83 | // Functionality to enter edit mode for a specific item 84 | const startEditItem = (index) => { 85 | setEditIndex(index); 86 | setEditText(todoArr[index].text); 87 | }; 88 | 89 | // Functionality to save the edited item 90 | const saveEditItem = (index) => { 91 | const updatedArr = [...todoArr]; 92 | updatedArr[index].text = editText; 93 | setTodoArr(updatedArr); // Update the state 94 | setEditIndex(null); 95 | setEditText(""); 96 | }; 97 | 98 | // Functionality to reset the todo list 99 | const resetList = () => { 100 | setTodoArr([]); 101 | }; 102 | 103 | // Functionality to calculate number of completed tasks 104 | const completedTasks = todoArr.filter((item) => item.completed).length; 105 | 106 | return ( 107 |
108 |
109 | 110 |
111 |
112 |
113 |
114 | {/* TOP HEADER TEXT */} 115 |

Hi There! 👋🏻

116 |

{quote}

117 |

- {author}

118 |
119 |
120 |
121 | {completedTasks}/{todoArr.length} Completed Tasks 122 |
123 |
124 |
125 | {/* INPUT TEXT FOR ADDING TASKS */} 126 | setInputText(e.target.value)} 130 | placeholder="Add a new task" 131 | onKeyPress={(e) => e.key === "Enter" && addItemToArray()} 132 | /> 133 | 134 |
135 |
136 | {/* LIST OF ADDED TASKS */} 137 | {loading ? ( 138 |
Loading To-Do items...
// Display loading message 139 | ) : ( 140 |
    141 | {todoArr.map((item, index) => ( 142 |
  • 152 | {editIndex === index ? ( 153 | setEditText(e.target.value)} 158 | onBlur={() => saveEditItem(index)} 159 | onKeyPress={(e) => 160 | e.key === "Enter" && saveEditItem(index) 161 | } 162 | autoFocus 163 | /> 164 | ) : ( 165 |
    173 | toggleComplete(index)} 183 | > 184 | {item.completed ? "✅" : "⭕"} {item.text} 185 | 186 |
    194 | {/* EDIT AND DELETE BUTTON */} 195 | startEditItem(index)}>Edit 196 | deleteItem(index)}>× 197 |
    198 |
    199 | )} 200 |
  • 201 | ))} 202 |
203 | )} 204 |
205 | {/* RESET BUTTON */} 206 | 209 |
210 |
211 |
212 | 213 |
214 |
215 | ); 216 | }; 217 | 218 | export default Tasks; 219 | --------------------------------------------------------------------------------