├── src
├── image
│ ├── todo.png
│ └── trash-solid.svg
├── App.js
├── index.js
├── index.css
└── Component
│ ├── style.css
│ └── TodoList.js
├── .gitignore
├── package.json
├── public
└── index.html
└── README.md
/src/image/todo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apni-coding/Todo-List-Using-React/HEAD/src/image/todo.png
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/image/trash-solid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 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 |
--------------------------------------------------------------------------------