├── .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 | 🚀 **![image](https://github.com/user-attachments/assets/d6b3e4a3-9853-4e74-9e20-a1a37e5f3e72)** 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 |
121 | setNewTask(e.target.value)} 125 | className="todo-input" 126 | placeholder="Add a new task..." 127 | /> 128 | 132 |
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 | --------------------------------------------------------------------------------