├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── iconstodo.png
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── components
├── TodoList.css
└── TodoList.js
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
└── setupTests.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 | # MadifyTech 🛠️🚀
2 |
3 | MadifyTech is a simple and interactive **To-Do List App** 📝 built using **React.js** with an Express.js & MongoDB backend. This project was assigned to me as part of my selection process for **Madify**.
4 |
5 | ## 🌟 Features
6 |
7 | ✅ **Add Tasks** – Quickly add new tasks to your to-do list.
8 | ✅ **Mark as Complete** – Toggle tasks between completed and pending states.
9 | ✅ **Delete Tasks** – Remove unwanted tasks with a single click.
10 | ✅ **Filter Tasks** – View all, completed, or pending tasks.
11 | ✅ **User-Friendly UI** – Clean and responsive design.
12 | ✅ **SweetAlert2 Integration** – Beautiful pop-up notifications for a great user experience.
13 |
14 | ## 🛠️ Tech Stack
15 |
16 | - **Frontend:** React.js ⚛️, Lucide-React Icons 🖼️, SweetAlert2 🎉
17 | - **Backend:** Node.js 🌿, Express.js 🚀, MongoDB 🍃
18 | - **API Handling:** Axios 🌐
19 |
20 | ## 🎯 Installation & Setup
21 |
22 | ### 1️⃣ Clone the Repository
23 | ```bash
24 | git clone https://github.com/machinelearningprodigy/MadifyTech.git
25 | cd MadifyTech
26 | ```
27 |
28 | ### 2️⃣ Install Dependencies
29 | ```bash
30 | npm install
31 | ```
32 |
33 | ### 3️⃣ Start the Development Server
34 | ```bash
35 | npm start
36 | ```
37 |
38 | The app will be available at **http://localhost:3000/** 🌍
39 |
40 | ## 🔗 API Endpoints
41 | MadifyTech communicates with a backend hosted on **Render**. Below are the key API endpoints:
42 |
43 | - **GET /tasks** – Fetch all tasks 📝
44 | - **POST /tasks** – Add a new task ➕
45 | - **PUT /tasks/:id** – Update task completion ✅
46 | - **DELETE /tasks/:id** – Remove a task 🗑️
47 |
48 | ## 📸 Screenshots
49 |
50 | 🚀 ****
51 |
52 | ## 📜 License
53 | This project is licensed under the MIT License. 📝
54 |
55 | ## 👨💻 Author
56 | Developed by **Rahul Mishra** 🚀
57 |
58 | ---
59 |
60 | Enjoy using **MadifyTech**! 🎯🚀
61 |
62 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@headlessui/react": "^2.2.0",
7 | "axios": "^1.7.9",
8 | "cra-template": "1.2.0",
9 | "lucide-react": "^0.474.0",
10 | "react": "^19.0.0",
11 | "react-dom": "^19.0.0",
12 | "react-scripts": "5.0.1",
13 | "sweetalert2": "^11.15.10",
14 | "tailwindcss": "^4.0.0",
15 | "web-vitals": "^4.2.4"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test",
21 | "eject": "react-scripts eject"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/machinelearningprodigy/MadifyTech/2fcfbe82b3d9753c9001e0f838826473441ebd40/public/favicon.ico
--------------------------------------------------------------------------------
/public/iconstodo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/machinelearningprodigy/MadifyTech/2fcfbe82b3d9753c9001e0f838826473441ebd40/public/iconstodo.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Rahul.ToDo
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/machinelearningprodigy/MadifyTech/2fcfbe82b3d9753c9001e0f838826473441ebd40/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/machinelearningprodigy/MadifyTech/2fcfbe82b3d9753c9001e0f838826473441ebd40/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "iconstodo.png",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | // frontend/src/App.js
2 | import React from 'react';
3 | import TodoList from './components/TodoList';
4 |
5 | // Import Tailwind CSS
6 | import './index.css';
7 |
8 | function App() {
9 | return (
10 |
11 |
12 |
13 | );
14 | }
15 |
16 | export default App;
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/components/TodoList.css:
--------------------------------------------------------------------------------
1 | /* TodoList.css */
2 | .todo-container {
3 | min-height: 100vh;
4 | background: linear-gradient(135deg, #f6f9fc 0%, #ecf1f7 100%);
5 | padding: 2rem 1rem;
6 | }
7 |
8 | .todo-card {
9 | max-width: 800px;
10 | margin: 0 auto;
11 | background: white;
12 | border-radius: 20px;
13 | box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05);
14 | overflow: hidden;
15 | }
16 |
17 | .todo-header {
18 | padding: 2rem;
19 | background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
20 | color: white;
21 | text-align: center;
22 | }
23 |
24 | .todo-title {
25 | font-size: 2rem;
26 | font-weight: 700;
27 | margin: 0;
28 | letter-spacing: -0.5px;
29 | }
30 |
31 | .todo-content {
32 | padding: 2rem;
33 | }
34 |
35 | .todo-form {
36 | display: flex;
37 | gap: 1rem;
38 | margin-bottom: 2rem;
39 | }
40 |
41 | .todo-input {
42 | flex: 1;
43 | padding: 0.875rem 1rem;
44 | border: 2px solid #e5e7eb;
45 | border-radius: 12px;
46 | font-size: 1rem;
47 | transition: all 0.2s ease;
48 | background: #f9fafb;
49 | }
50 |
51 | .todo-input:focus {
52 | outline: none;
53 | border-color: #6366f1;
54 | background: white;
55 | box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
56 | }
57 |
58 | .add-button {
59 | display: flex;
60 | align-items: center;
61 | gap: 0.5rem;
62 | padding: 0.875rem 1.5rem;
63 | background: #6366f1;
64 | color: white;
65 | border: none;
66 | border-radius: 12px;
67 | font-weight: 600;
68 | cursor: pointer;
69 | transition: all 0.2s ease;
70 | }
71 |
72 | .add-button:hover {
73 | background: #4f46e5;
74 | transform: translateY(-1px);
75 | }
76 |
77 | .add-button:active {
78 | transform: translateY(0);
79 | }
80 |
81 | .filter-container {
82 | display: flex;
83 | gap: 0.75rem;
84 | margin-bottom: 2rem;
85 | padding-bottom: 1.5rem;
86 | border-bottom: 2px solid #f3f4f6;
87 | }
88 |
89 | .filter-button {
90 | padding: 0.625rem 1.25rem;
91 | border: none;
92 | border-radius: 10px;
93 | font-weight: 500;
94 | cursor: pointer;
95 | transition: all 0.2s ease;
96 | display: flex;
97 | align-items: center;
98 | gap: 0.5rem;
99 | }
100 |
101 | .filter-button.active {
102 | background: #6366f1;
103 | color: white;
104 | }
105 |
106 | .filter-button:not(.active) {
107 | background: #f3f4f6;
108 | color: #4b5563;
109 | }
110 |
111 | .filter-button:hover:not(.active) {
112 | background: #e5e7eb;
113 | }
114 |
115 | .todo-list {
116 | display: flex;
117 | flex-direction: column;
118 | gap: 1rem;
119 | }
120 |
121 | .todo-item {
122 | display: flex;
123 | align-items: center;
124 | justify-content: space-between;
125 | padding: 1rem;
126 | background: #f9fafb;
127 | border-radius: 12px;
128 | transition: all 0.2s ease;
129 | animation: slideIn 0.3s ease;
130 | }
131 |
132 | .todo-item:hover {
133 | background: #f3f4f6;
134 | transform: translateX(4px);
135 | }
136 |
137 | .todo-checkbox-container {
138 | display: flex;
139 | align-items: center;
140 | gap: 1rem;
141 | flex: 1;
142 | }
143 |
144 | .todo-checkbox {
145 | width: 1.25rem;
146 | height: 1.25rem;
147 | border-radius: 6px;
148 | border: 2px solid #6366f1;
149 | cursor: pointer;
150 | transition: all 0.2s ease;
151 | }
152 |
153 | .todo-checkbox:checked {
154 | background: #6366f1;
155 | border-color: #6366f1;
156 | }
157 |
158 | .todo-text {
159 | font-size: 1rem;
160 | color: #1f2937;
161 | transition: all 0.2s ease;
162 | }
163 |
164 | .todo-text.completed {
165 | text-decoration: line-through;
166 | color: #9ca3af;
167 | }
168 |
169 | .delete-button {
170 | padding: 0.5rem;
171 | border: none;
172 | background: transparent;
173 | color: #ef4444;
174 | cursor: pointer;
175 | border-radius: 8px;
176 | transition: all 0.2s ease;
177 | display: flex;
178 | align-items: center;
179 | justify-content: center;
180 | }
181 |
182 | .delete-button:hover {
183 | background: #fee2e2;
184 | color: #dc2626;
185 | }
186 |
187 | .error-message {
188 | background: #fee2e2;
189 | color: #dc2626;
190 | padding: 1rem;
191 | border-radius: 12px;
192 | margin-bottom: 1rem;
193 | display: flex;
194 | align-items: center;
195 | gap: 0.5rem;
196 | animation: slideIn 0.3s ease;
197 | }
198 |
199 | @keyframes slideIn {
200 | from {
201 | opacity: 0;
202 | transform: translateY(10px);
203 | }
204 | to {
205 | opacity: 1;
206 | transform: translateY(0);
207 | }
208 | }
209 |
210 | /* Responsive Design */
211 | @media (max-width: 640px) {
212 | .todo-container {
213 | padding: 1rem 0.5rem;
214 | }
215 |
216 | .todo-content {
217 | padding: 1.5rem 1rem;
218 | }
219 |
220 | .todo-form {
221 | flex-direction: column;
222 | }
223 |
224 | .add-button {
225 | width: 100%;
226 | justify-content: center;
227 | }
228 |
229 | .filter-container {
230 | overflow-x: auto;
231 | padding-bottom: 1rem;
232 | }
233 |
234 | .filter-button {
235 | white-space: nowrap;
236 | }
237 | }
238 |
239 | /* Dark Mode Support */
240 | @media (prefers-color-scheme: dark) {
241 | .todo-container {
242 | background: linear-gradient(135deg, #1f2937 0%, #111827 100%);
243 | }
244 |
245 | .todo-card {
246 | background: #1f2937;
247 | }
248 |
249 | .todo-input {
250 | background: #374151;
251 | border-color: #4b5563;
252 | color: white;
253 | }
254 |
255 | .todo-input:focus {
256 | background: #374151;
257 | }
258 |
259 | .todo-item {
260 | background: #374151;
261 | }
262 |
263 | .todo-item:hover {
264 | background: #4b5563;
265 | }
266 |
267 | .todo-text {
268 | color: #f9fafb;
269 | }
270 |
271 | .todo-text.completed {
272 | color: #9ca3af;
273 | }
274 |
275 | .filter-button:not(.active) {
276 | background: #374151;
277 | color: #e5e7eb;
278 | }
279 |
280 | .filter-button:hover:not(.active) {
281 | background: #4b5563;
282 | }
283 | }
--------------------------------------------------------------------------------
/src/components/TodoList.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Plus, Trash2, Check, Filter, AlertCircle } from 'lucide-react';
3 | import axios from 'axios';
4 | import Swal from 'sweetalert2'; // Import SweetAlert2
5 | import './TodoList.css';
6 |
7 | const TodoList = () => {
8 | const [tasks, setTasks] = useState([]);
9 | const [newTask, setNewTask] = useState('');
10 | const [filter, setFilter] = useState('ALL');
11 | const [error, setError] = useState('');
12 |
13 | // API base URL
14 | const API_URL = 'https://madify-backend.onrender.com/api';
15 |
16 | // Fetch all tasks
17 | const fetchTasks = async () => {
18 | try {
19 | const response = await axios.get(`${API_URL}/tasks`);
20 | setTasks(response.data);
21 | } catch (err) {
22 | setError('Failed to fetch tasks');
23 | }
24 | };
25 |
26 | // Add new task
27 | const addTask = async (e) => {
28 | e.preventDefault();
29 | if (!newTask.trim()) {
30 | setError('Task cannot be empty');
31 | return;
32 | }
33 | try {
34 | await axios.post(`${API_URL}/tasks`, { text: newTask, completed: false });
35 | setNewTask('');
36 | fetchTasks();
37 | setError('');
38 | // Show success alert
39 | Swal.fire({
40 | title: 'Task Added!',
41 | text: 'Your task has been added successfully.',
42 | icon: 'success',
43 | confirmButtonText: 'Cool',
44 | });
45 | } catch (err) {
46 | setError('Failed to add task');
47 | Swal.fire({
48 | title: 'Error!',
49 | text: 'Something went wrong while adding the task.',
50 | icon: 'error',
51 | confirmButtonText: 'Try Again',
52 | });
53 | }
54 | };
55 |
56 | // Toggle task completion
57 | const toggleTask = async (id, completed) => {
58 | try {
59 | await axios.put(`${API_URL}/tasks/${id}`, { completed: !completed });
60 | fetchTasks();
61 | // Show success alert
62 | Swal.fire({
63 | title: 'Task Updated!',
64 | text: `Task has been marked as ${completed ? 'incomplete' : 'completed'}.`,
65 | icon: 'success',
66 | confirmButtonText: 'Great',
67 | });
68 | } catch (err) {
69 | setError('Failed to update task');
70 | Swal.fire({
71 | title: 'Error!',
72 | text: 'Something went wrong while updating the task.',
73 | icon: 'error',
74 | confirmButtonText: 'Try Again',
75 | });
76 | }
77 | };
78 |
79 | // Delete task
80 | const deleteTask = async (id) => {
81 | try {
82 | await axios.delete(`${API_URL}/tasks/${id}`);
83 | fetchTasks();
84 | // Show success alert
85 | Swal.fire({
86 | title: 'Task Deleted!',
87 | text: 'Your task has been deleted successfully.',
88 | icon: 'success',
89 | confirmButtonText: 'Okay',
90 | });
91 | } catch (err) {
92 | setError('Failed to delete task');
93 | Swal.fire({
94 | title: 'Error!',
95 | text: 'Something went wrong while deleting the task.',
96 | icon: 'error',
97 | confirmButtonText: 'Try Again',
98 | });
99 | }
100 | };
101 |
102 | // Filter tasks
103 | const filteredTasks = tasks.filter(task => {
104 | if (filter === 'COMPLETED') return task.completed;
105 | if (filter === 'PENDING') return !task.completed;
106 | return true;
107 | });
108 |
109 | useEffect(() => {
110 | fetchTasks();
111 | }, []);
112 |
113 | return (
114 |
115 |
116 |
117 |
Todo List
118 |
119 |
120 |
133 |
134 | {error && (
135 |
136 |
137 | {error}
138 |
139 | )}
140 |
141 |
142 |
149 |
156 |
163 |
164 |
165 |
166 | {filteredTasks.map((task) => (
167 |
168 |
169 | toggleTask(task.id, task.completed)}
173 | className="todo-checkbox"
174 | />
175 |
176 | {task.text}
177 |
178 |
179 |
186 |
187 | ))}
188 |
189 |
190 |
191 |
192 | );
193 | };
194 |
195 | export default TodoList;
196 |
--------------------------------------------------------------------------------
/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 | import reportWebVitals from './reportWebVitals';
6 |
7 | const root = ReactDOM.createRoot(document.getElementById('root'));
8 | root.render(
9 |
10 |
11 |
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------