├── .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 | }
166 | sx={{ minWidth: '120px' }}
167 | >
168 | Add Task
169 |
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 |
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
--------------------------------------------------------------------------------