├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public └── index.html └── src ├── App.js ├── Component ├── TodoList.js └── style.css ├── image ├── todo.png └── trash-solid.svg ├── index.css └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Hosted Link 2 | https://apni-coding.github.io/Todo-List-Using-React/ 3 | 4 | # Todo List 5 | 6 | This is a simple Todo List application built using React. It allows users to add, edit, and delete tasks, mark tasks as completed, and filter tasks based on their completion status. 7 | 8 | ## Features 9 | 10 | - Add tasks: Users can enter new tasks in the input field and click the "Add" button to add them to the list. 11 | - Edit tasks: Users can click the edit icon next to a task to edit its title. 12 | - Delete tasks: Users can click the delete icon next to a task to delete it from the list. 13 | - Mark tasks as completed: Users can check the checkbox next to a task to mark it as completed. 14 | - Complete all tasks: Users can click the "Complete all tasks" link to mark all tasks as completed. 15 | - Delete completed tasks: Users can click the "Delete Comp tasks" link to delete all completed tasks. 16 | - Filter tasks: Users can use the dropdown menu to filter tasks based on their completion status. 17 | - Task count: The total number of tasks and the number of completed tasks are displayed. 18 | 19 | ## Installation 20 | 21 | 1. Clone the repository: 22 | ``` 23 | git clone 24 | ``` 25 | 26 | 2. Navigate to the project directory: 27 | ``` 28 | cd todo-list 29 | ``` 30 | 31 | 3. Install the dependencies: 32 | ``` 33 | npm install 34 | ``` 35 | 36 | 4. Start the application: 37 | ``` 38 | npm start 39 | ``` 40 | 41 | 5. Open your web browser and visit http://localhost:3000 to see the Todo List application. 42 | 43 | ## Dependencies 44 | 45 | The following dependencies are used in this project: 46 | 47 | - react: ^16.0.0 48 | - react-dom: ^16.0.0 49 | - react-toastify: ^8.0.0 50 | 51 | You can find the complete list of dependencies with their versions in the `package.json` file. 52 | 53 | ## API 54 | 55 | The application uses the JSONPlaceholder API to fetch and update tasks. The API endpoint used is: https://jsonplaceholder.typicode.com/todos 56 | 57 | 58 | ## Contributing 59 | 60 | If you'd like to contribute to this project, please follow these steps: 61 | 62 | 1. Fork the repository. 63 | 2. Create a new branch for your feature or bug fix. 64 | 3. Make your changes and commit them. 65 | 4. Push your changes to your fork. 66 | 5. Submit a pull request. 67 | 68 | 69 | ## Acknowledgements 70 | 71 | - The Todo List app was created as a learning exercise based on a tutorial or example. 72 | - [React](https://reactjs.org/) - The JavaScript library used for building the user interface. 73 | - [React Toastify](https://fkhadra.github.io/react-toastify/) - A library for displaying toast notifications in React applications. 74 | ## Owner 75 | Vivek Kumar 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todolist", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-scripts": "5.0.1", 12 | "react-toastify": "^9.1.3", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "gh-pages": "^5.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 19 | 20 | 29 | TodoList || Vivek Kumar 30 | 31 | 32 | 33 |
34 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import TodoList from './Component/TodoList'; 2 | 3 | function App() { 4 | return ( 5 | <> 6 | 7 | 8 | ); 9 | } 10 | 11 | export default App; 12 | -------------------------------------------------------------------------------- /src/Component/TodoList.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { ToastContainer, toast } from 'react-toastify'; 3 | import 'react-toastify/dist/ReactToastify.css'; 4 | import './style.css'; 5 | import todoImage from "../image/todo.png" 6 | 7 | const TodoList = () => { 8 | // State variables 9 | const [tasks, setTasks] = useState([]); // Holds the list of tasks 10 | const [inputValue, setInputValue] = useState(''); // Holds the value of the input field 11 | const [filter, setFilter] = useState('all'); // Holds the current filter type 12 | const [isLoading, setIsLoading] = useState(true); // Indicates whether the data is being loaded 13 | const [editTaskId, setEditTaskId] = useState(null); // Holds the ID of the task being edited 14 | 15 | // Fetch initial data 16 | useEffect(() => { 17 | fetchTodos(); 18 | }, []); 19 | 20 | // Fetch todos from an API 21 | const fetchTodos = async () => { 22 | try { 23 | const response = await fetch('https://jsonplaceholder.typicode.com/todos?_limit=4'); 24 | const todos = await response.json(); 25 | setTasks(todos); 26 | setIsLoading(false); 27 | } catch (error) { 28 | console.log('Error fetching todos:', error); 29 | setIsLoading(false); 30 | } 31 | }; 32 | 33 | // Handle input change 34 | const handleInputChange = (event) => { 35 | setInputValue(event.target.value); 36 | }; 37 | 38 | // Add a new task 39 | const handleAddTask = async () => { 40 | if (inputValue.trim() === '') { 41 | return; 42 | } 43 | 44 | const newTask = { 45 | title: inputValue, 46 | completed: false 47 | }; 48 | 49 | try { 50 | const response = await fetch('https://jsonplaceholder.typicode.com/todos', { 51 | method: 'POST', 52 | body: JSON.stringify(newTask), 53 | headers: { 54 | 'Content-type': 'application/json; charset=UTF-8', 55 | }, 56 | }); 57 | const addedTask = await response.json(); 58 | setTasks((prevTasks) => [...prevTasks, addedTask]); 59 | setInputValue(''); 60 | toast.success('Task added successfully'); 61 | } catch (error) { 62 | console.log('Error adding task:', error); 63 | toast.error('Error adding task'); 64 | } 65 | }; 66 | 67 | // Handle checkbox change for a task 68 | const handleTaskCheckboxChange = (taskId) => { 69 | setTasks((prevTasks) => 70 | prevTasks.map((task) => 71 | task.id === taskId ? { ...task, completed: !task.completed } : task 72 | ) 73 | ); 74 | }; 75 | 76 | // Delete a task 77 | const handleDeleteTask = (taskId) => { 78 | setTasks((prevTasks) => prevTasks.filter((task) => task.id !== taskId)); 79 | toast.success('Task deleted successfully'); 80 | }; 81 | 82 | // Edit a task 83 | const handleEditTask = (taskId) => { 84 | setEditTaskId(taskId); 85 | const taskToEdit = tasks.find((task) => task.id === taskId); 86 | setInputValue(taskToEdit.title); 87 | }; 88 | 89 | // Update a task 90 | const handleUpdateTask = async () => { 91 | if (inputValue.trim() === '') { 92 | return; 93 | } 94 | 95 | const updatedTask = { 96 | title: inputValue, 97 | completed: false 98 | }; 99 | 100 | try { 101 | const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${editTaskId}`, { 102 | method: 'PUT', 103 | body: JSON.stringify(updatedTask), 104 | headers: { 105 | 'Content-type': 'application/json; charset=UTF-8', 106 | }, 107 | }); 108 | const updatedTaskData = await response.json(); 109 | setTasks((prevTasks) => 110 | prevTasks.map((task) => 111 | task.id === editTaskId ? { ...task, title: updatedTaskData.title } : task 112 | ) 113 | ); 114 | setInputValue(''); 115 | setEditTaskId(null); 116 | toast.success('Task updated successfully'); 117 | } catch (error) { 118 | console.log('Error updating task:', error); 119 | toast.error('Error updating task'); 120 | } 121 | }; 122 | 123 | // Mark all tasks as completed 124 | const handleCompleteAll = () => { 125 | setTasks((prevTasks) => 126 | prevTasks.map((task) => ({ ...task, completed: true })) 127 | ); 128 | }; 129 | 130 | // Clear completed tasks 131 | const handleClearCompleted = () => { 132 | setTasks((prevTasks) => prevTasks.filter((task) => !task.completed)); 133 | }; 134 | 135 | // Handle filter change 136 | const handleFilterChange = (filterType) => { 137 | setFilter(filterType); 138 | }; 139 | 140 | // Filter tasks based on the selected filter 141 | const filteredTasks = tasks.filter((task) => { 142 | if (filter === 'all') { 143 | return true; 144 | } else if (filter === 'completed') { 145 | return task.completed; 146 | } else if (filter === 'uncompleted') { 147 | return !task.completed; 148 | } 149 | return true; 150 | }); 151 | 152 | // Display loading message while data is being fetched 153 | if (isLoading) { 154 | return
Loading...
; 155 | } 156 | 157 | // Render the todo list 158 | return ( 159 |
160 | 161 |
162 |

163 | todo-image Todo List 164 |

165 |
166 | 167 | 176 | 179 |
180 | 181 |
182 | 183 |

184 | Complete all tasks 185 |

186 |

187 | Delete comp tasks 188 |

189 |
190 | 191 |
    192 | {filteredTasks.map((task) => ( 193 |
  • 194 | handleTaskCheckboxChange(task.id)} 201 | /> 202 | 203 |
    204 | handleEditTask(task.id)} 209 | /> 210 | handleDeleteTask(task.id)} 215 | /> 216 |
    217 |
  • 218 | ))} 219 |
220 | 221 |
222 | 236 | 237 |
238 |

239 | Completed: {tasks.filter((task) => task.completed).length} 240 |

241 |
242 |
243 |

244 | 245 | Total Tasks: {tasks.length} 246 | 247 |

248 |
249 |
250 |
251 |
252 | ); 253 | }; 254 | 255 | export default TodoList; 256 | -------------------------------------------------------------------------------- /src/Component/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | box-sizing: border-box; 6 | } 7 | 8 | /* container */ 9 | .container{ 10 | width: 100%; 11 | min-height: 100vh; 12 | background: orange; 13 | /* border: 2px solid green; */ 14 | padding: 10px; 15 | } 16 | 17 | .todo-app{ 18 | width: 100%; 19 | max-width: 540px; 20 | background: white; 21 | border: 2px solid black; 22 | margin: 100px auto 20px; 23 | padding: 40px 30px 70px; 24 | border-radius: 15px; 25 | } 26 | 27 | .todo-app h2{ 28 | display: flex; 29 | color: blue; 30 | align-items: center; 31 | margin-bottom: 20px; 32 | justify-content: center; 33 | } 34 | 35 | .todo-app h2 img{ 36 | width: 30px; 37 | margin-left: 10px; 38 | } 39 | 40 | /* input box */ 41 | .row{ 42 | display: flex; 43 | align-items: center; 44 | justify-content: space-between; 45 | background: #edeef0; 46 | border-radius: 30px; 47 | padding-left: 20px; 48 | margin-bottom: 25px; 49 | } 50 | 51 | input{ 52 | position: relative; 53 | flex: 1; 54 | border: none; 55 | outline: none; 56 | background: none; 57 | padding: 10px; 58 | } 59 | 60 | button{ 61 | border: none; 62 | outline: none; 63 | padding: 16px 50px; 64 | background: #ff5945; 65 | color: white; 66 | font-size: 16px; 67 | cursor: pointer; 68 | border-radius: 40px; 69 | } 70 | 71 | /* styling on mid */ 72 | .mid{ 73 | display: flex; 74 | font-size: 1.2rem; 75 | margin-left: 20px; 76 | margin-bottom: 30px; 77 | /* justify-content: space-between; */ 78 | 79 | } 80 | 81 | #complete-all{ 82 | margin-left: 10px; 83 | cursor: pointer; 84 | } 85 | 86 | #clear-all{ 87 | margin-left: 90px; 88 | cursor: pointer; 89 | } 90 | 91 | 92 | /* styling on todo list */ 93 | 94 | ul { 95 | list-style-type: none; 96 | margin: 0; 97 | padding: 0; 98 | /* border: 1px solid #e5e5e5; */ 99 | } 100 | 101 | ul li { 102 | padding: 10px; 103 | /* padding-left: 0; */ 104 | display: flex; 105 | justify-content: space-between; 106 | align-items: inherit; 107 | align-items: center; 108 | } 109 | 110 | ul li:hover { 111 | background: lightyellow; 112 | } 113 | #list input { 114 | padding: 0; 115 | height: initial; 116 | width: initial; 117 | margin-bottom: 0; 118 | display: none; 119 | cursor: pointer; 120 | } 121 | 122 | 123 | #list label { 124 | position: relative; 125 | cursor: pointer; 126 | } 127 | 128 | #list label:before { 129 | content:''; 130 | -webkit-appearance: none; 131 | background-color: transparent; 132 | border: 2px solid #0079bf; 133 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), inset 0px -15px 10px -12px rgba(0, 0, 0, 0.05); 134 | padding: 10px; 135 | display: inline-block; 136 | position: relative; 137 | vertical-align: middle; 138 | cursor: pointer; 139 | margin-right: 10px; 140 | } 141 | #list input:checked + label:after { 142 | content: ''; 143 | display: block; 144 | position: absolute; 145 | top: 2px; 146 | left: 9px; 147 | width: 6px; 148 | height: 14px; 149 | border: solid #fff; 150 | border-width: 0 2px 2px 0; 151 | transform: rotate(45deg); 152 | } 153 | #list input:checked + label:before { 154 | background: #0079bf; 155 | } 156 | #list input:checked + label { 157 | text-decoration: line-through; 158 | } 159 | .delete { 160 | height: 25px; 161 | padding: 5px 0px; 162 | border-radius: 3px; 163 | } 164 | .delete:hover { 165 | background: #f2f2f2; 166 | cursor: pointer; 167 | } 168 | 169 | .edit{ 170 | height: 25px; 171 | padding: 5px 0px; 172 | border-radius: 3px; 173 | margin-right: 10px; 174 | } 175 | 176 | .edit:hover{ 177 | background: #f2f2f2; 178 | cursor: pointer; 179 | } 180 | 181 | h1 { 182 | text-align: center; 183 | } 184 | .add-task { 185 | width: 100%; 186 | outline: none; 187 | font-size: 15px; 188 | padding: 11px; 189 | border: 1px solid #f3f3f3; 190 | margin-bottom: 3px; 191 | } 192 | 193 | 194 | 195 | 196 | #total-tasks { 197 | margin-bottom: 20px; 198 | font-size: 13px; 199 | margin-left: 2px; 200 | color: gray; 201 | } 202 | 203 | /* 204 | dropdown*/ 205 | .filters{ 206 | margin-top: 40px; 207 | } 208 | .dropbtn { 209 | background-color: #4CAF50; 210 | color: white; 211 | padding: 10px 16px; 212 | font-size: 16px; 213 | border: none; 214 | cursor: pointer; 215 | border-radius: 5px; 216 | } 217 | 218 | .dropdown { 219 | position: relative; 220 | display: inline-block; 221 | } 222 | 223 | .dropdown-content { 224 | display: none; 225 | position: absolute; 226 | background-color: #f9f9f9; 227 | min-width: 160px; 228 | box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); 229 | z-index: 1; 230 | } 231 | 232 | .dropdown-content a { 233 | color: black; 234 | padding: 12px 16px; 235 | text-decoration: none; 236 | display: block; 237 | } 238 | 239 | .dropdown-content a:hover {background-color: #f1f1f1} 240 | 241 | .dropdown:hover .dropdown-content { 242 | display: block; 243 | } 244 | 245 | .dropdown:hover .dropbtn { 246 | background-color: #3e8e41; 247 | } 248 | 249 | .completed-task{ 250 | display: inline-block; 251 | margin-left: 100px; 252 | } 253 | 254 | .remaining-task{ 255 | display: inline-block; 256 | margin-left: 80px; 257 | } 258 | 259 | /* .remaining-task p{ 260 | font-size: 40px; 261 | color: black; 262 | } */ 263 | 264 | -------------------------------------------------------------------------------- /src/image/todo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apni-coding/Todo-List-Using-React/23438060afc1901cd75778a75d3f0f0787091810/src/image/todo.png -------------------------------------------------------------------------------- /src/image/trash-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | 15 | --------------------------------------------------------------------------------