├── .gitignore ├── README.md ├── frontend ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.css │ ├── App.test.tsx │ ├── App.tsx │ ├── index.css │ ├── index.tsx │ ├── logo.svg │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ ├── setupTests.ts │ └── types │ │ └── Task.ts └── tsconfig.json ├── pom.xml └── src └── main ├── java └── com │ └── taskmanager │ ├── TaskManagerApplication.java │ ├── config │ └── OpenApiConfig.java │ ├── controller │ └── TaskController.java │ ├── model │ └── Task.java │ ├── repository │ └── TaskRepository.java │ └── service │ └── TaskService.java └── resources └── application.properties /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | ### Node ### 36 | node_modules/ 37 | /frontend/node_modules 38 | /frontend/build 39 | /frontend/.env 40 | /frontend/.env.local 41 | /frontend/.env.development.local 42 | /frontend/.env.test.local 43 | /frontend/.env.production.local 44 | /frontend/npm-debug.log* 45 | /frontend/yarn-debug.log* 46 | /frontend/yarn-error.log* 47 | 48 | ### Application Properties ### 49 | src/main/resources/application-dev.properties 50 | src/main/resources/application-prod.properties -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Task Manager Application 2 | 3 | A full-stack Task Manager application built with Spring Boot and React. 4 | 5 | ## Features 6 | - Create, read, update, and delete tasks 7 | - Mark tasks as complete/incomplete 8 | - Task categorization 9 | - RESTful API with Swagger documentation 10 | - MySQL database integration 11 | 12 | ## Tech Stack 13 | - Backend: Spring Boot 2.7.0 14 | - Frontend: React with Material-UI 15 | - Database: MySQL 16 | - API Documentation: Swagger UI 17 | 18 | ## Prerequisites 19 | - Java 11 or higher 20 | - Node.js and npm 21 | - MySQL 8.0 22 | - Maven 23 | 24 | ## Setup Instructions 25 | 26 | ### Backend Setup 27 | 1. Clone the repository 28 | 2. Configure MySQL database in `src/main/resources/application.properties` 29 | 3. Run the Spring Boot application: 30 | ```bash 31 | mvn spring-boot:run 32 | ``` 33 | 4. Access Swagger UI at: `http://localhost:8081/swagger-ui.html` 34 | 35 | ### Frontend Setup 36 | 1. Navigate to the frontend directory: 37 | ```bash 38 | cd frontend 39 | ``` 40 | 2. Install dependencies: 41 | ```bash 42 | npm install 43 | ``` 44 | 3. Start the development server: 45 | ```bash 46 | npm start 47 | ``` 48 | 4. Access the application at: `http://localhost:3000` 49 | 50 | ## API Endpoints 51 | - GET `/api/tasks` - Get all tasks 52 | - GET `/api/tasks/{id}` - Get task by ID 53 | - POST `/api/tasks` - Create new task 54 | - PUT `/api/tasks/{id}` - Update task 55 | - DELETE `/api/tasks/{id}` - Delete task 56 | -------------------------------------------------------------------------------- /frontend/.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 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.14.0", 7 | "@emotion/styled": "^11.14.0", 8 | "@mui/icons-material": "^7.1.0", 9 | "@mui/material": "^7.1.0", 10 | "@testing-library/dom": "^10.4.0", 11 | "@testing-library/jest-dom": "^6.6.3", 12 | "@testing-library/react": "^16.3.0", 13 | "@testing-library/user-event": "^13.5.0", 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "^16.18.126", 16 | "@types/react": "^19.1.3", 17 | "@types/react-dom": "^19.1.3", 18 | "axios": "^1.9.0", 19 | "react": "^19.1.0", 20 | "react-dom": "^19.1.0", 21 | "react-scripts": "5.0.1", 22 | "typescript": "^4.9.5", 23 | "web-vitals": "^2.1.4" 24 | }, 25 | "scripts": { 26 | "start": "react-scripts start", 27 | "build": "react-scripts build", 28 | "test": "react-scripts test", 29 | "eject": "react-scripts eject" 30 | }, 31 | "eslintConfig": { 32 | "extends": [ 33 | "react-app", 34 | "react-app/jest" 35 | ] 36 | }, 37 | "browserslist": { 38 | "production": [ 39 | ">0.2%", 40 | "not dead", 41 | "not op_mini all" 42 | ], 43 | "development": [ 44 | "last 1 chrome version", 45 | "last 1 firefox version", 46 | "last 1 safari version" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/Task_Manager_JavaProject/e84e7e75cca951d3c276f5c1c94af5e71c15b75d/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/Task_Manager_JavaProject/e84e7e75cca951d3c276f5c1c94af5e71c15b75d/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/Task_Manager_JavaProject/e84e7e75cca951d3c276f5c1c94af5e71c15b75d/frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 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 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/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 | -------------------------------------------------------------------------------- /frontend/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | Container, 4 | Typography, 5 | Box, 6 | Paper, 7 | List, 8 | ListItem, 9 | ListItemText, 10 | ListItemSecondaryAction, 11 | IconButton, 12 | TextField, 13 | Button, 14 | Checkbox, 15 | Dialog, 16 | DialogTitle, 17 | DialogContent, 18 | DialogActions, 19 | Chip, 20 | Snackbar, 21 | Alert, 22 | Card, 23 | CardContent, 24 | Grid, 25 | useTheme, 26 | } from '@mui/material'; 27 | import { Delete as DeleteIcon, Edit as EditIcon, Add as AddIcon } from '@mui/icons-material'; 28 | import axios from 'axios'; 29 | import { Task } from './types/Task'; 30 | 31 | const API_URL = 'http://localhost:8081/api/tasks'; 32 | 33 | type Severity = 'success' | 'error' | 'info' | 'warning'; 34 | 35 | function App() { 36 | const theme = useTheme(); 37 | const [tasks, setTasks] = useState([]); 38 | const [newTask, setNewTask] = useState({ 39 | title: '', 40 | description: '', 41 | completed: false, 42 | }); 43 | const [editTask, setEditTask] = useState(null); 44 | const [openDialog, setOpenDialog] = useState(false); 45 | const [snackbar, setSnackbar] = useState<{ open: boolean; message: string; severity: Severity }>({ 46 | open: false, 47 | message: '', 48 | severity: 'success', 49 | }); 50 | 51 | useEffect(() => { 52 | fetchTasks(); 53 | }, []); 54 | 55 | const fetchTasks = async () => { 56 | try { 57 | const response = await axios.get(API_URL); 58 | setTasks(response.data); 59 | } catch (error) { 60 | console.error('Error fetching tasks:', error); 61 | showSnackbar('Error fetching tasks', 'error'); 62 | } 63 | }; 64 | 65 | const handleCreateTask = async () => { 66 | try { 67 | await axios.post(API_URL, newTask); 68 | setNewTask({ title: '', description: '', completed: false }); 69 | fetchTasks(); 70 | showSnackbar('Task created successfully!', 'success'); 71 | } catch (error) { 72 | console.error('Error creating task:', error); 73 | showSnackbar('Error creating task', 'error'); 74 | } 75 | }; 76 | 77 | const handleUpdateTask = async (task: Task) => { 78 | try { 79 | await axios.put(`${API_URL}/${task.id}`, task); 80 | setEditTask(null); 81 | setOpenDialog(false); 82 | fetchTasks(); 83 | showSnackbar('Task updated successfully!', 'success'); 84 | } catch (error) { 85 | console.error('Error updating task:', error); 86 | showSnackbar('Error updating task', 'error'); 87 | } 88 | }; 89 | 90 | const handleDeleteTask = async (id: number) => { 91 | try { 92 | await axios.delete(`${API_URL}/${id}`); 93 | fetchTasks(); 94 | showSnackbar('Task deleted successfully!', 'success'); 95 | } catch (error) { 96 | console.error('Error deleting task:', error); 97 | showSnackbar('Error deleting task', 'error'); 98 | } 99 | }; 100 | 101 | const handleToggleComplete = async (task: Task) => { 102 | try { 103 | const updatedTask = { ...task, completed: !task.completed }; 104 | await axios.put(`${API_URL}/${task.id}`, updatedTask); 105 | fetchTasks(); 106 | showSnackbar( 107 | updatedTask.completed ? 'Task marked as completed!' : 'Task marked as pending!', 108 | 'success' 109 | ); 110 | } catch (error) { 111 | console.error('Error toggling task completion:', error); 112 | showSnackbar('Error updating task status', 'error'); 113 | } 114 | }; 115 | 116 | const showSnackbar = (message: string, severity: Severity) => { 117 | setSnackbar({ open: true, message, severity }); 118 | }; 119 | 120 | const handleCloseSnackbar = () => { 121 | setSnackbar({ ...snackbar, open: false }); 122 | }; 123 | 124 | return ( 125 | 126 | 127 | 139 | Task Manager 140 | 141 | 142 | 143 | 144 | 145 | setNewTask({ ...newTask, title: e.target.value })} 150 | variant="outlined" 151 | size="small" 152 | /> 153 | setNewTask({ ...newTask, description: e.target.value })} 158 | variant="outlined" 159 | size="small" 160 | /> 161 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | {tasks.map((task) => ( 177 | 178 | 189 | 197 | handleToggleComplete(task)} 200 | color="primary" 201 | /> 202 | 211 | {task.title} 212 | 213 | } 214 | secondary={ 215 | 216 | 217 | {task.description} 218 | 219 | 225 | 226 | } 227 | /> 228 | 229 | { 233 | setEditTask(task); 234 | setOpenDialog(true); 235 | }} 236 | sx={{ mr: 1 }} 237 | > 238 | 239 | 240 | task.id && handleDeleteTask(task.id)} 244 | > 245 | 246 | 247 | 248 | 249 | 250 | 251 | ))} 252 | 253 | 254 | 255 | setOpenDialog(false)}> 256 | Edit Task 257 | 258 | 263 | setEditTask(editTask ? { ...editTask, title: e.target.value } : null) 264 | } 265 | margin="normal" 266 | /> 267 | 272 | setEditTask(editTask ? { ...editTask, description: e.target.value } : null) 273 | } 274 | margin="normal" 275 | /> 276 | 277 | 278 | 279 | 285 | 286 | 287 | 288 | 294 | 299 | {snackbar.message} 300 | 301 | 302 | 303 | 304 | ); 305 | } 306 | 307 | export default App; 308 | -------------------------------------------------------------------------------- /frontend/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 | background-color: #f5f5f5; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/index.tsx: -------------------------------------------------------------------------------- 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( 8 | document.getElementById('root') as HTMLElement 9 | ); 10 | root.render( 11 | 12 | 13 | 14 | ); 15 | 16 | // If you want to start measuring performance in your app, pass a function 17 | // to log results (for example: reportWebVitals(console.log)) 18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 19 | reportWebVitals(); 20 | -------------------------------------------------------------------------------- /frontend/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /frontend/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /frontend/src/setupTests.ts: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /frontend/src/types/Task.ts: -------------------------------------------------------------------------------- 1 | export interface Task { 2 | id?: number; 3 | title: string; 4 | description: string; 5 | completed: boolean; 6 | createdAt?: string; 7 | updatedAt?: string; 8 | } -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.7.0 11 | 12 | 13 | 14 | com.taskmanager 15 | task-manager 16 | 0.0.1-SNAPSHOT 17 | task-manager 18 | Task Manager Application 19 | 20 | 21 | 11 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-data-jpa 32 | 33 | 34 | mysql 35 | mysql-connector-java 36 | 8.0.28 37 | 38 | 39 | org.projectlombok 40 | lombok 41 | 1.18.30 42 | provided 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-test 47 | test 48 | 49 | 50 | org.springdoc 51 | springdoc-openapi-ui 52 | 1.6.15 53 | 54 | 55 | org.springdoc 56 | springdoc-openapi-webmvc-core 57 | 1.6.15 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-maven-plugin 66 | 2.7.0 67 | 68 | 69 | 70 | org.projectlombok 71 | lombok 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/com/taskmanager/TaskManagerApplication.java: -------------------------------------------------------------------------------- 1 | package com.taskmanager; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class TaskManagerApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(TaskManagerApplication.class, args); 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/java/com/taskmanager/config/OpenApiConfig.java: -------------------------------------------------------------------------------- 1 | package com.taskmanager.config; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.info.Info; 5 | import io.swagger.v3.oas.models.info.Contact; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class OpenApiConfig { 11 | 12 | @Bean 13 | public OpenAPI taskManagerOpenAPI() { 14 | return new OpenAPI() 15 | .info(new Info() 16 | .title("Task Manager API") 17 | .description("API for managing tasks") 18 | .version("1.0") 19 | .contact(new Contact() 20 | .name("Task Manager Team") 21 | .email("support@taskmanager.com"))); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/com/taskmanager/controller/TaskController.java: -------------------------------------------------------------------------------- 1 | package com.taskmanager.controller; 2 | 3 | import com.taskmanager.model.Task; 4 | import com.taskmanager.service.TaskService; 5 | import io.swagger.v3.oas.annotations.Operation; 6 | import io.swagger.v3.oas.annotations.tags.Tag; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import java.util.List; 12 | 13 | @RestController 14 | @RequestMapping("/api/tasks") 15 | @CrossOrigin(origins = "http://localhost:3000") 16 | @Tag(name = "Task Controller", description = "APIs for managing tasks") 17 | public class TaskController { 18 | 19 | @Autowired 20 | private TaskService taskService; 21 | 22 | @GetMapping 23 | @Operation(summary = "Get all tasks", description = "Retrieves a list of all tasks") 24 | public List getAllTasks() { 25 | return taskService.getAllTasks(); 26 | } 27 | 28 | @GetMapping("/{id}") 29 | @Operation(summary = "Get task by ID", description = "Retrieves a specific task by its ID") 30 | public ResponseEntity getTaskById(@PathVariable Long id) { 31 | return taskService.getTaskById(id) 32 | .map(ResponseEntity::ok) 33 | .orElse(ResponseEntity.notFound().build()); 34 | } 35 | 36 | @PostMapping 37 | @Operation(summary = "Create new task", description = "Creates a new task") 38 | public Task createTask(@RequestBody Task task) { 39 | return taskService.createTask(task); 40 | } 41 | 42 | @PutMapping("/{id}") 43 | @Operation(summary = "Update task", description = "Updates an existing task by its ID") 44 | public ResponseEntity updateTask(@PathVariable Long id, @RequestBody Task taskDetails) { 45 | try { 46 | Task updatedTask = taskService.updateTask(id, taskDetails); 47 | return ResponseEntity.ok(updatedTask); 48 | } catch (RuntimeException e) { 49 | return ResponseEntity.notFound().build(); 50 | } 51 | } 52 | 53 | @DeleteMapping("/{id}") 54 | @Operation(summary = "Delete task", description = "Deletes a task by its ID") 55 | public ResponseEntity deleteTask(@PathVariable Long id) { 56 | try { 57 | taskService.deleteTask(id); 58 | return ResponseEntity.ok().build(); 59 | } catch (RuntimeException e) { 60 | return ResponseEntity.notFound().build(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/java/com/taskmanager/model/Task.java: -------------------------------------------------------------------------------- 1 | package com.taskmanager.model; 2 | 3 | import lombok.Data; 4 | import javax.persistence.*; 5 | import java.time.LocalDateTime; 6 | 7 | @Data 8 | @Entity 9 | @Table(name = "tasks") 10 | public class Task { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | 15 | @Column(nullable = false) 16 | private String title; 17 | 18 | @Column(length = 1000) 19 | private String description; 20 | 21 | @Column(nullable = false) 22 | private boolean completed; 23 | 24 | @Column(name = "created_at") 25 | private LocalDateTime createdAt; 26 | 27 | @Column(name = "updated_at") 28 | private LocalDateTime updatedAt; 29 | 30 | @PrePersist 31 | protected void onCreate() { 32 | createdAt = LocalDateTime.now(); 33 | updatedAt = LocalDateTime.now(); 34 | } 35 | 36 | @PreUpdate 37 | protected void onUpdate() { 38 | updatedAt = LocalDateTime.now(); 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/taskmanager/repository/TaskRepository.java: -------------------------------------------------------------------------------- 1 | package com.taskmanager.repository; 2 | 3 | import com.taskmanager.model.Task; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface TaskRepository extends JpaRepository { 9 | } -------------------------------------------------------------------------------- /src/main/java/com/taskmanager/service/TaskService.java: -------------------------------------------------------------------------------- 1 | package com.taskmanager.service; 2 | 3 | import com.taskmanager.model.Task; 4 | import com.taskmanager.repository.TaskRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Service 12 | public class TaskService { 13 | 14 | @Autowired 15 | private TaskRepository taskRepository; 16 | 17 | public List getAllTasks() { 18 | return taskRepository.findAll(); 19 | } 20 | 21 | public Optional getTaskById(Long id) { 22 | return taskRepository.findById(id); 23 | } 24 | 25 | public Task createTask(Task task) { 26 | return taskRepository.save(task); 27 | } 28 | 29 | public Task updateTask(Long id, Task taskDetails) { 30 | Task task = taskRepository.findById(id) 31 | .orElseThrow(() -> new RuntimeException("Task not found with id: " + id)); 32 | 33 | task.setTitle(taskDetails.getTitle()); 34 | task.setDescription(taskDetails.getDescription()); 35 | task.setCompleted(taskDetails.isCompleted()); 36 | 37 | return taskRepository.save(task); 38 | } 39 | 40 | public void deleteTask(Long id) { 41 | Task task = taskRepository.findById(id) 42 | .orElseThrow(() -> new RuntimeException("Task not found with id: " + id)); 43 | taskRepository.delete(task); 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # MySQL Configuration 2 | spring.datasource.url=jdbc:mysql://localhost:3306/new_taskmanager?createDatabaseIfNotExist=true&useSSL=false&serverTimezone=UTC 3 | spring.datasource.username=root 4 | spring.datasource.password=root 5 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 6 | 7 | # JPA Configuration 8 | spring.jpa.hibernate.ddl-auto=update 9 | spring.jpa.show-sql=true 10 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect 11 | spring.jpa.properties.hibernate.format_sql=true 12 | 13 | # Server Configuration 14 | server.port=8081 15 | 16 | spring.h2.console.enabled=true 17 | spring.h2.console.path=/h2-console --------------------------------------------------------------------------------