Merge remote-tracking branch 'origin/LabWork04' into LabWork04
# Conflicts: # front/premium_store/public/index.html # spring_online_calculator/src/main/java/premium_store/model/TankLevel.java
This commit is contained in:
commit
d6cdd178cd
70
front/premium_store/README.md
Normal file
70
front/premium_store/README.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
### Code Splitting
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
29676
front/premium_store/package-lock.json
generated
Normal file
29676
front/premium_store/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
front/premium_store/package.json
Normal file
41
front/premium_store/package.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "premuim_store",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"axios": "^1.3.5",
|
||||
"cors": "^2.8.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.10.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
11
front/premium_store/public/index.html
Normal file
11
front/premium_store/public/index.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
41
front/premium_store/src/components/MainHead.jsx
Normal file
41
front/premium_store/src/components/MainHead.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import '../styles/App.css';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
//компонент с кнопками навигации по сайту
|
||||
const MainHead = (props) => {
|
||||
|
||||
return(
|
||||
<div>
|
||||
<div>
|
||||
<h1 className="Main-label">Мир танков</h1>
|
||||
</div>
|
||||
<form className="collapse navbar-collapse">
|
||||
<nav className="navbar navbar-expand-lg justify-content-around">
|
||||
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation" style={{backgroundColor: '#379dc2'}}>
|
||||
<span className="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div className="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul className="Main_head navbar-nav me-auto align-items-center">
|
||||
{
|
||||
props.links.map(route =>
|
||||
<li key={route.path}
|
||||
className="nav-item">
|
||||
<div className="Button_Main_Group container p-2" div="div">
|
||||
<NavLink className="nav-link btn border border-3 border-dark fs-4 lh-15" role="button"
|
||||
to={route.path}>
|
||||
{route.label}
|
||||
</NavLink>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MainHead;
|
133
front/premium_store/src/components/PageForChecking.jsx
Normal file
133
front/premium_store/src/components/PageForChecking.jsx
Normal file
@ -0,0 +1,133 @@
|
||||
import React, { useState, useEffect, useRef} from 'react';
|
||||
import axios from 'axios';
|
||||
import '../styles/App.css';
|
||||
import TankList from './items/Tank/TankList';
|
||||
import './AddTank.css';
|
||||
|
||||
const PageForChecking = () => {
|
||||
|
||||
const[tankItems, setTankItems] = useState([]);
|
||||
|
||||
const [nationItems, setNationItems] = useState([]);
|
||||
|
||||
const [levelItems, setLevelItems] = useState([]);
|
||||
|
||||
const [chooiceNation, setChooiceNation] = useState();
|
||||
|
||||
const [chooiceFirstLevel, setChooiceFirstLevel] = useState();
|
||||
|
||||
const [chooiceSecondLevel, setChooiceSecondLevel] = useState();
|
||||
|
||||
//загрузка всех имеющихся танков, а также уровней и наций при старте
|
||||
useEffect(() => {
|
||||
axios.get('http://localhost:8080/level/')
|
||||
.then((responce) => {
|
||||
console.log(responce.data);
|
||||
setLevelItems(responce.data)
|
||||
});
|
||||
axios.get('http://localhost:8080/nation/')
|
||||
.then((responce) => {
|
||||
console.log(responce.data);
|
||||
setNationItems(responce.data)
|
||||
});
|
||||
}, [])
|
||||
|
||||
const getChoiceNation = (newId) => {
|
||||
setChooiceNation(nationItems[newId - 1].nation);
|
||||
}
|
||||
|
||||
const getChooiceFirstLevel = (newId) => {
|
||||
setChooiceFirstLevel(levelItems[newId - 1].level);
|
||||
}
|
||||
|
||||
const getChooiceSecondLevel = (newId) => {
|
||||
setChooiceSecondLevel(levelItems[newId - 1].level);
|
||||
}
|
||||
|
||||
function findList(){
|
||||
console.log(chooiceNation);
|
||||
console.log(chooiceFirstLevel);
|
||||
console.log(chooiceSecondLevel);
|
||||
|
||||
axios.get('http://localhost:8080/tank/filteredList/?nation=' + chooiceNation + '&firstLevel=' + chooiceFirstLevel + '&secondLevel=' + chooiceSecondLevel)
|
||||
.then((response) => {
|
||||
setTankItems(response.data)
|
||||
});
|
||||
|
||||
console.log(tankItems)
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<div className="Group_create_level">
|
||||
<h1>Фильтрация танков</h1>
|
||||
<div>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Выберите нацию:
|
||||
<select
|
||||
onChange={(event) => getChoiceNation(event.target.selectedIndex)}
|
||||
>
|
||||
<option selected>Выберите нацию</option>
|
||||
{nationItems.map((nationItem) =>
|
||||
<option
|
||||
value={nationItem.nation}
|
||||
key={nationItem.id}
|
||||
>
|
||||
{nationItem.nation}
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</p>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Выберите начальный диапазон уровней:
|
||||
<select style={{marginTop: "10px"}}
|
||||
onChange={(event) => getChooiceFirstLevel(event.target.selectedIndex)}
|
||||
>
|
||||
<option selected>Выберите уровень</option>
|
||||
{levelItems.map((levelItem) =>
|
||||
<option
|
||||
value={levelItem.level}
|
||||
key={levelItem.id}
|
||||
>
|
||||
{levelItem.level}
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</p>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Выберите конечный диапазон уровней:
|
||||
<select style={{marginTop: "10px"}}
|
||||
onChange={(event) => getChooiceSecondLevel(event.target.selectedIndex)}
|
||||
>
|
||||
<option selected>Выберите уровень</option>
|
||||
{levelItems.map((levelItem) =>
|
||||
<option
|
||||
value={levelItem.level}
|
||||
key={levelItem.id}
|
||||
>
|
||||
{levelItem.level}
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</p>
|
||||
</div>
|
||||
<button className='add-level-button'
|
||||
onClick={findList}
|
||||
>
|
||||
Найти танки
|
||||
</button>
|
||||
</div>
|
||||
<div className="Card_list">
|
||||
{tankItems.length !== 0
|
||||
?
|
||||
<TankList tankItems={tankItems}
|
||||
/>
|
||||
:
|
||||
<h1 style={{textAlign: 'center'}}>Отсутствуют какие-либо танки при таком фильтре!</h1>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PageForChecking
|
@ -0,0 +1,106 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import cl from '../GameClient/ModalClient.module.css';
|
||||
import '../../AddClient.css';
|
||||
|
||||
const ModalClient = ({data, visible, setVisible}) => {
|
||||
|
||||
//для обновления уровня
|
||||
const [clientNickName, setClientNickName] = useState(data.nickName);
|
||||
|
||||
const [clientEmail, setClientEmail] = useState(data.email);
|
||||
|
||||
const [clientBalance, setClientBalance] = useState(data.balance);
|
||||
|
||||
const [clientTank, setClientTank] = useState(null);
|
||||
|
||||
const [tankItems, setTankItems] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('Обращение к БД');
|
||||
axios.get('http://localhost:8080/tank/')
|
||||
.then((responce) => {
|
||||
console.log(responce.data);
|
||||
setTankItems(responce.data)
|
||||
});
|
||||
}, [])
|
||||
|
||||
//для контроля видимости модалки
|
||||
const rootClasses = [cl.myModal];
|
||||
|
||||
if(visible)
|
||||
{
|
||||
rootClasses.push(cl.active);
|
||||
}
|
||||
|
||||
//добавление нового уровня
|
||||
function updateLevel(){
|
||||
axios.put('http://localhost:8080/client/' + data.id + '?nickName='
|
||||
+ clientNickName + '&email=' + clientEmail + '&balance=' + clientBalance + '&tankId=' + clientTank)
|
||||
.then((response) => {
|
||||
console.log("Обновление клиента с id " + data.id)
|
||||
});
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
const getChoiceNation = (newId) => {
|
||||
setClientTank(tankItems[newId - 1].id);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={rootClasses.join(' ')} onClick={() => setVisible(false)}>
|
||||
<div className={cl.myModalContent} onClick={(e) => e.stopPropagation()}>
|
||||
<p style={{marginTop: "10px"}}>
|
||||
Никнейм:
|
||||
<input
|
||||
className="add-client-input"
|
||||
value={clientNickName}
|
||||
onChange={e => setClientNickName(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p style={{marginTop: "10px"}}>
|
||||
Почта:
|
||||
<input
|
||||
className="add-client-input"
|
||||
value={clientEmail}
|
||||
onChange={e => setClientEmail(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p style={{marginTop: "10px"}}>
|
||||
Баланс:
|
||||
<input
|
||||
className="add-client-input"
|
||||
value={clientBalance}
|
||||
onChange={e => setClientBalance(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Выберите новый танк:
|
||||
<select
|
||||
onChange={(event) => getChoiceNation(event.target.selectedIndex)}
|
||||
>
|
||||
<option selected>Выберите танк</option>
|
||||
{tankItems.map((tankItem) =>
|
||||
<option
|
||||
value={tankItem.name}
|
||||
key={tankItem.id}
|
||||
>
|
||||
{tankItem.name}
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</p>
|
||||
<button
|
||||
style={{marginTop: "10px"}}
|
||||
className={cl.modalButton}
|
||||
type="button"
|
||||
onClick={updateLevel}
|
||||
>
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalClient
|
@ -0,0 +1,34 @@
|
||||
.myModal{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.myModal.active{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.myModalContent{
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
background: #FF652F;
|
||||
border-radius: 16px;
|
||||
min-width: 300px;
|
||||
min-height: 100px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modalButton{
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #FFE430;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
37
front/premium_store/src/components/items/Level/LevelItem.css
Normal file
37
front/premium_store/src/components/items/Level/LevelItem.css
Normal file
@ -0,0 +1,37 @@
|
||||
.level-card{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border: 5px solid;
|
||||
border-color: #14A76C;
|
||||
border-radius: 10px;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.level-attribute{
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.level-button-group{
|
||||
display: flex;
|
||||
width: 20%;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.level-button{
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
46
front/premium_store/src/components/items/Level/LevelItem.jsx
Normal file
46
front/premium_store/src/components/items/Level/LevelItem.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import './LevelItem.css';
|
||||
import ModalLevel from './ModalLevel';
|
||||
|
||||
//отвечает за отдельно взятый уровень (вывод карточки с ним)
|
||||
const LevelItem = (data) => {
|
||||
|
||||
const [level, setLevel] = useState(null);
|
||||
|
||||
//состояние для контроля вызова модального окна
|
||||
const[modal, setModal] = useState(false);
|
||||
|
||||
function deleteLevel(){
|
||||
axios.delete('http://localhost:8080/level/' + data.levelItem.id)
|
||||
.then((response) => {
|
||||
console.log("Удаление уровня с id " + data.levelItem.id)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="level-card">
|
||||
<p className="level-attribute"> id: {data.levelItem.id} </p>
|
||||
<p className="level-attribute"> уровень: {data.levelItem.level} </p>
|
||||
<div className='level-button-group'>
|
||||
<button className="level-button" type="button"
|
||||
onClick={() => setModal(true)}
|
||||
>
|
||||
Редактировать
|
||||
</button>
|
||||
<button className="level-button" type="button"
|
||||
onClick={deleteLevel}
|
||||
>
|
||||
Удалить
|
||||
</button>
|
||||
<ModalLevel
|
||||
data={data.levelItem}
|
||||
visible={modal}
|
||||
setVisible={setModal}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LevelItem;
|
25
front/premium_store/src/components/items/Level/LevelList.jsx
Normal file
25
front/premium_store/src/components/items/Level/LevelList.jsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React, { useEffect} from 'react';
|
||||
import LevelItem from './LevelItem';
|
||||
|
||||
//const host = import.meta.env.VITE_API_URL;
|
||||
|
||||
//отвечает за список всех уровней. Передаём сюда пропсом массив уровней
|
||||
const LevelList = (levels) => {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h1 style={{textAlign: 'center', fontFamily: 'courier, monospace', background: '#FF652F', borderRadius: '10px'}}>
|
||||
Список существующих уровней:
|
||||
</h1>
|
||||
</div>
|
||||
{levels.levelItems.map((levelItem) =>
|
||||
<LevelItem
|
||||
levelItem={levelItem}
|
||||
key={levelItem.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LevelList;
|
@ -0,0 +1,49 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import cl from './ModalLevel.module.css';
|
||||
import '../../AddLevel.css';
|
||||
|
||||
const ModalLevel = ({data, visible, setVisible}) => {
|
||||
|
||||
//для обновления уровня
|
||||
const [level, setLevel] = useState(data.level);
|
||||
|
||||
//для контроля видимости модалки
|
||||
const rootClasses = [cl.myModal];
|
||||
|
||||
if(visible)
|
||||
{
|
||||
rootClasses.push(cl.active);
|
||||
}
|
||||
|
||||
//добавление нового уровня
|
||||
function updateLevel(){
|
||||
setLevel()
|
||||
axios.put('http://localhost:8080/level/' + data.id + '?Level=' + level)
|
||||
.then((response) => {
|
||||
console.log("Обновление уровня с id " + data.id)
|
||||
});
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={rootClasses.join(' ')} onClick={() => setVisible(false)}>
|
||||
<div className={cl.myModalContent} onClick={(e) => e.stopPropagation()}>
|
||||
<input
|
||||
className="add-level-input"
|
||||
value={level}
|
||||
onChange={e => setLevel(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className={cl.modalButton}
|
||||
type="button"
|
||||
onClick={updateLevel}
|
||||
>
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalLevel;
|
@ -0,0 +1,34 @@
|
||||
.myModal{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.myModal.active{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.myModalContent{
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
background: #FF652F;
|
||||
border-radius: 16px;
|
||||
min-width: 300px;
|
||||
min-height: 100px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modalButton{
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #FFE430;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import cl from './ModalNation.module.css';
|
||||
import '../../AddNation.css';
|
||||
|
||||
const ModalNation = ({data, visible, setVisible}) => {
|
||||
//для обновления уровня
|
||||
const [nation, setNation] = useState(data.nation);
|
||||
|
||||
const nullId = 0;
|
||||
|
||||
//для контроля видимости модалки
|
||||
const rootClasses = [cl.myModal];
|
||||
|
||||
if(visible)
|
||||
{
|
||||
rootClasses.push(cl.active);
|
||||
}
|
||||
|
||||
//добавление новой нации
|
||||
function updateLevel(){
|
||||
setNation()
|
||||
axios.put('http://localhost:8080/nation/' + data.id + '?nation=' + nation + '&tankId=' + nullId)
|
||||
.then((response) => {
|
||||
console.log("Обновление нации с id " + data.id)
|
||||
});
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={rootClasses.join(' ')} onClick={() => setVisible(false)}>
|
||||
<div className={cl.myModalContent} onClick={(e) => e.stopPropagation()}>
|
||||
<input
|
||||
className="add-nation-input"
|
||||
value={nation}
|
||||
onChange={e => setNation(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className={cl.modalButton}
|
||||
type="button"
|
||||
onClick={updateLevel}
|
||||
>
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalNation
|
@ -0,0 +1,34 @@
|
||||
.myModal{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.myModal.active{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.myModalContent{
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
background: #FF652F;
|
||||
border-radius: 16px;
|
||||
min-width: 300px;
|
||||
min-height: 100px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modalButton{
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #FFE430;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import cl from './ModalNation.module.css';
|
||||
import '../../AddNation.css';
|
||||
import TankList from '../Tank/TankList';
|
||||
import { useFetcher } from 'react-router-dom';
|
||||
|
||||
const ModalTankNation = ({data, visible, setVisible}) => {
|
||||
//для обновления уровня
|
||||
const [nation, setNation] = useState(data.tanks);
|
||||
|
||||
//для контроля видимости модалки
|
||||
const rootClasses = [cl.myModal];
|
||||
|
||||
//загрузка всех имеющихся танков, а также уровней и наций при старте
|
||||
useEffect(() => {
|
||||
console.log("Загрузка МОДАЛКИ №1")
|
||||
console.log(nation);
|
||||
}, [])
|
||||
|
||||
if(visible)
|
||||
{
|
||||
rootClasses.push(cl.active);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={rootClasses.join(' ')} onClick={() => setVisible(false)}>
|
||||
<div className={cl.myModalContent} onClick={(e) => e.stopPropagation()}>
|
||||
{nation.length !== 0
|
||||
?
|
||||
<TankList
|
||||
label={"Список танков у нации"}
|
||||
tankItems={nation}
|
||||
/>
|
||||
:
|
||||
<h1 style={{textAlign: 'center'}}>В БД отсутствуют какие-либо танки данной нации!</h1>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalTankNation
|
@ -0,0 +1,38 @@
|
||||
.nation-card{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border: 5px solid;
|
||||
border-color: #14A76C;
|
||||
border-radius: 10px;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.nation-attribute{
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nation-button-group{
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
width: 20%;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nation-button{
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import './NationItem.css';
|
||||
import ModalNation from './ModalNation';
|
||||
import ModalTankNation from './ModalTankNation';
|
||||
|
||||
//отвечает за отдельно взятую нацию (вывод карточки с ним)
|
||||
const NationItem = (data) => {
|
||||
const [nation, setNation] = useState(null);
|
||||
|
||||
//состояние для контроля вызова модального окна
|
||||
const[modal, setModal] = useState(false);
|
||||
|
||||
//состояние для вызова окна показа списка танков нации
|
||||
const[modalNation, setModalNation] = useState(false);
|
||||
|
||||
function deleteNation(){
|
||||
axios.delete('http://localhost:8080/nation/' + data.nationItem.id)
|
||||
.then((response) => {
|
||||
console.log("Удаление нации с id " + data.nationItem.id)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="nation-card">
|
||||
<p className="nation-attribute"> id: {data.nationItem.id} </p>
|
||||
<p className="nation-attribute"> нация: {data.nationItem.nation} </p>
|
||||
<div className='nation-button-group'>
|
||||
<button className="nation-button" type="button"
|
||||
onClick={() => setModal(true)}
|
||||
>
|
||||
Редактировать
|
||||
</button>
|
||||
<button className="nation-button" type="button"
|
||||
onClick={deleteNation}
|
||||
>
|
||||
Удалить
|
||||
</button>
|
||||
<button className="nation-button" type="button"
|
||||
onClick={() => setModalNation(true)}
|
||||
>
|
||||
Список танков
|
||||
</button>
|
||||
<ModalNation
|
||||
data={data.nationItem}
|
||||
visible={modal}
|
||||
setVisible={setModal}
|
||||
/>
|
||||
<ModalTankNation
|
||||
data={data.nationItem}
|
||||
visible={modalNation}
|
||||
setVisible={setModalNation}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default NationItem
|
@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
import NationItem from './NationItem';
|
||||
|
||||
const NationList = (nations) => {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h1 style={{textAlign: 'center', fontFamily: 'courier, monospace', background: '#FF652F', borderRadius: '10px'}}>
|
||||
Список существующих наций:
|
||||
</h1>
|
||||
</div>
|
||||
{nations.nationItems.map((nationItem) =>
|
||||
<NationItem
|
||||
nationItem={nationItem}
|
||||
key={nationItem.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default NationList
|
125
front/premium_store/src/components/items/Tank/ModalTank.jsx
Normal file
125
front/premium_store/src/components/items/Tank/ModalTank.jsx
Normal file
@ -0,0 +1,125 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import cl from './ModalTank.module.css';
|
||||
import '../../AddTank.css';
|
||||
|
||||
const ModalTank = ({data, visible, setVisible}) => {
|
||||
//для обновления танка
|
||||
const [tankName, setTankName] = useState(data.name);
|
||||
|
||||
const [tankCost, setTankCost] = useState(data.cost);
|
||||
|
||||
const [chooiceNation, setChoiceNation] = useState(data.nation.id);
|
||||
|
||||
const [chooiceLevel, setChoiceLevel] = useState(data.nation.id);
|
||||
|
||||
const [nationItems, setNationItems] = useState([]);
|
||||
|
||||
const [levelItems, setLevelItems] = useState([]);
|
||||
|
||||
//загрузка всех имеющихся танков, а также уровней и наций при старте
|
||||
useEffect(() => {
|
||||
axios.get('http://localhost:8080/level/')
|
||||
.then((responce) => {
|
||||
console.log(responce.data);
|
||||
setLevelItems(responce.data)
|
||||
});
|
||||
axios.get('http://localhost:8080/nation/')
|
||||
.then((responce) => {
|
||||
console.log(responce.data);
|
||||
setNationItems(responce.data)
|
||||
});
|
||||
}, [])
|
||||
|
||||
//для контроля видимости модалки
|
||||
const rootClasses = [cl.myModal];
|
||||
|
||||
if(visible)
|
||||
{
|
||||
rootClasses.push(cl.active);
|
||||
}
|
||||
|
||||
//добавление нового танка
|
||||
function updateLevel(){
|
||||
setTankName()
|
||||
axios.put('http://localhost:8080/tank/' + data.id + '?firstName=' + tankName + '&nationId='
|
||||
+ chooiceNation + '&levelId=' + chooiceLevel + '&cost=' + tankCost)
|
||||
.then((response) => {
|
||||
console.log("Обновление танка с id " + data.id)
|
||||
});
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
const getChoiceNation = (newId) => {
|
||||
setChoiceNation(nationItems[newId - 1].id);
|
||||
}
|
||||
|
||||
const getChoiceLevel = (newId) => {
|
||||
setChoiceLevel(levelItems[newId - 1].id);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={rootClasses.join(' ')} onClick={() => setVisible(false)}>
|
||||
<div className={cl.myModalContent} onClick={(e) => e.stopPropagation()}>
|
||||
<p>
|
||||
Название:
|
||||
<input
|
||||
className="add-tank-input"
|
||||
value={tankName}
|
||||
onChange={e => setTankName(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
Стоимость:
|
||||
<input
|
||||
className="add-tank-input"
|
||||
value={tankCost}
|
||||
onChange={e => setTankCost(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Выберите нацию:
|
||||
<select
|
||||
onChange={(event) => getChoiceNation(event.target.selectedIndex)}
|
||||
>
|
||||
<option selected>Выберите нацию</option>
|
||||
{nationItems.map((nationItem) =>
|
||||
<option
|
||||
value={nationItem.nation}
|
||||
key={nationItem.id}
|
||||
>
|
||||
{nationItem.nation}
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</p>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Выберите уровень:
|
||||
<select style={{marginTop: "10px"}}
|
||||
onChange={(event) => getChoiceLevel(event.target.selectedIndex)}
|
||||
>
|
||||
<option selected>Выберите уровень</option>
|
||||
{levelItems.map((levelItem) =>
|
||||
<option
|
||||
value={levelItem.level}
|
||||
key={levelItem.id}
|
||||
>
|
||||
{levelItem.level}
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</p>
|
||||
<button
|
||||
className={cl.modalButton}
|
||||
type="button"
|
||||
onClick={updateLevel}
|
||||
>
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalTank
|
@ -0,0 +1,33 @@
|
||||
.myModal{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.myModal.active{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.myModalContent{
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
background: #FF652F;
|
||||
border-radius: 16px;
|
||||
min-width: 300px;
|
||||
min-height: 100px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modalButton{
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #FFE430;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
37
front/premium_store/src/components/items/Tank/TankItem.css
Normal file
37
front/premium_store/src/components/items/Tank/TankItem.css
Normal file
@ -0,0 +1,37 @@
|
||||
.tank-card{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border: 5px solid;
|
||||
border-color: #14A76C;
|
||||
border-radius: 10px;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.tank-attribute{
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tank-button-group{
|
||||
display: flex;
|
||||
width: 20%;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tank-button{
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
48
front/premium_store/src/components/items/Tank/TankItem.jsx
Normal file
48
front/premium_store/src/components/items/Tank/TankItem.jsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import './TankItem.css';
|
||||
import ModalTank from './ModalTank';
|
||||
|
||||
const TankItem = (data) => {
|
||||
|
||||
const [tank, setTank] = useState(null);
|
||||
|
||||
//состояние для контроля вызова модального окна
|
||||
const[modal, setModal] = useState(false);
|
||||
|
||||
function deleteTank(){
|
||||
axios.delete('http://localhost:8080/tank/' + data.tankItem.id)
|
||||
.then((response) => {
|
||||
console.log("Удаление танка с id " + data.tankItem.id)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tank-card">
|
||||
<p className="tank-attribute"> id: {data.tankItem.id} </p>
|
||||
<p className="tank-attribute"> название: {data.tankItem.name} </p>
|
||||
<p className="tank-attribute"> уровень: {data.tankItem.level.level} </p>
|
||||
<p className="tank-attribute"> нация: {data.tankItem.nation.nation} </p>
|
||||
<p className="tank-attribute"> стоимость: {data.tankItem.cost} </p>
|
||||
<div className='tank-button-group'>
|
||||
<button className="tank-button" type="button"
|
||||
onClick={() => setModal(true)}
|
||||
>
|
||||
Редактировать
|
||||
</button>
|
||||
<button className="tank-button" type="button"
|
||||
onClick={deleteTank}
|
||||
>
|
||||
Удалить
|
||||
</button>
|
||||
<ModalTank
|
||||
data={data.tankItem}
|
||||
visible={modal}
|
||||
setVisible={setModal}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TankItem
|
22
front/premium_store/src/components/items/Tank/TankList.jsx
Normal file
22
front/premium_store/src/components/items/Tank/TankList.jsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
import TankItem from './TankItem';
|
||||
|
||||
const TankList = (tanks) => {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h1 style={{textAlign: 'center', fontFamily: 'courier, monospace', background: '#FF652F', borderRadius: '10px'}}>
|
||||
Список существующих наций:
|
||||
</h1>
|
||||
</div>
|
||||
{tanks.tankItems.map((tankItem) =>
|
||||
<TankItem
|
||||
tankItem={tankItem}
|
||||
key={tankItem.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TankList;
|
BIN
front/premium_store/src/images/wot-268-4.jpg
Normal file
BIN
front/premium_store/src/images/wot-268-4.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 497 KiB |
BIN
front/premium_store/src/images/wot-is-4.jpg
Normal file
BIN
front/premium_store/src/images/wot-is-4.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 474 KiB |
8
front/premium_store/src/index.js
Normal file
8
front/premium_store/src/index.js
Normal file
@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<App />
|
||||
);
|
@ -0,0 +1,15 @@
|
||||
package premium_store;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
//отключение Cors фильтра - не позволяет организовавыть взаимодействие с разных доменов
|
||||
@Configuration
|
||||
class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedMethods("*");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import premium_store.model.TankLevel;
|
||||
|
||||
public class LevelDTO {
|
||||
private final Long id;
|
||||
private final int level;
|
||||
|
||||
public LevelDTO(TankLevel level){
|
||||
this.id = level.getId();
|
||||
this.level = level.getLevel();
|
||||
}
|
||||
|
||||
public Long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getLevel(){
|
||||
return level;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import premium_store.model.Nation;
|
||||
|
||||
public class SimpleNationDTO {
|
||||
private final Long id;
|
||||
private final String nation;
|
||||
|
||||
public SimpleNationDTO(Nation nation){
|
||||
this.id = nation.getId();
|
||||
this.nation = nation.getNation();
|
||||
}
|
||||
|
||||
public Long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getNation(){
|
||||
return nation;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import premium_store.model.Tank;
|
||||
|
||||
public class TankDTO {
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final SimpleNationDTO nation;
|
||||
private final LevelDTO level;
|
||||
private final int cost;
|
||||
|
||||
public TankDTO(Tank tank){
|
||||
this.id = tank.getId();
|
||||
this.nation = new SimpleNationDTO(tank.getNation());
|
||||
this.level = new LevelDTO(tank.getLevel());
|
||||
this.name = tank.getName();
|
||||
this.cost = tank.getCost();
|
||||
}
|
||||
|
||||
public long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public SimpleNationDTO getNation(){
|
||||
return nation;
|
||||
}
|
||||
|
||||
public LevelDTO getLevel(){
|
||||
return level;
|
||||
}
|
||||
|
||||
public int getCost(){
|
||||
return cost;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.controller.DTO.FullNationDTO;
|
||||
import premium_store.service.NationService;
|
||||
import premium_store.service.TankService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//привязываем наш контроллер к придуманному корневому URL благодаря аннотациям
|
||||
//здесь происходит внедрение зависимости нашего сервиса
|
||||
//так же здесь прописываем вызовы методов CRUD в привязке к URL
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping("/nation")
|
||||
public class NationController {
|
||||
private final NationService nationService;
|
||||
private final TankService tankService;
|
||||
|
||||
public NationController(NationService nationService, TankService tankService){
|
||||
this.nationService = nationService;
|
||||
this.tankService = tankService;
|
||||
}
|
||||
|
||||
//аннотация PathVariable связывает значения id из URL и Long id
|
||||
@GetMapping("/{id}")
|
||||
public FullNationDTO getNation(@PathVariable Long id) {
|
||||
return new FullNationDTO(nationService.findNation(id));
|
||||
}
|
||||
|
||||
//с помощью Java Stream преобразуем набор пришедших данных в объекты StudentDto
|
||||
@GetMapping("/")
|
||||
public List<FullNationDTO> getNations() {
|
||||
return nationService.findAllNations().stream()
|
||||
.map(FullNationDTO::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PostMapping("/")
|
||||
public FullNationDTO createNation(@RequestParam("nation") String nation) {
|
||||
return new FullNationDTO(nationService.addNation(nation));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public FullNationDTO updateNation(@PathVariable Long id,
|
||||
@RequestParam("nation") String nation,
|
||||
@RequestParam(value = "tankId", required = false) Long tankId ) {
|
||||
return new FullNationDTO(nationService.updateNation(id, nation, (tankId > 0) ? tankService.findTank(tankId) : null));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public FullNationDTO deleteNation(@PathVariable Long id) {
|
||||
return new FullNationDTO(nationService.deleteNation(id));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,71 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.controller.DTO.TankDTO;
|
||||
import premium_store.service.NationService;
|
||||
import premium_store.service.TankLevelService;
|
||||
import premium_store.service.TankService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping("/tank")
|
||||
public class TankController {
|
||||
private final TankService tankService;
|
||||
private final TankLevelService tankLevelService;
|
||||
private final NationService nationService;
|
||||
|
||||
public TankController(TankService tankService, TankLevelService tankLevelService, NationService nationService){
|
||||
this.tankService = tankService;
|
||||
this.tankLevelService = tankLevelService;
|
||||
this.nationService = nationService;
|
||||
}
|
||||
|
||||
//аннотация PathVariable связывает значения id из URL и Long id
|
||||
@GetMapping("/{id}")
|
||||
public TankDTO getTank(@PathVariable Long id) {
|
||||
return new TankDTO(tankService.findTank(id));
|
||||
}
|
||||
|
||||
//с помощью Java Stream преобразуем набор пришедших данных в объекты TankDTO
|
||||
@GetMapping("/")
|
||||
public List<TankDTO> getTanks() {
|
||||
return tankService.findAllTanks().stream()
|
||||
.map(TankDTO::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/filteredList/")
|
||||
public List<TankDTO> getFilteredTanks(@RequestParam("nation") String nation,
|
||||
@RequestParam("firstLevel") int firstLevel,
|
||||
@RequestParam("secondLevel") int secondLevel) {
|
||||
return tankService.findListTank(nation, firstLevel, secondLevel).stream()
|
||||
.map(TankDTO::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PostMapping("/")
|
||||
public TankDTO createTank(@RequestParam("firstName") String name,
|
||||
@RequestParam("nationId") Long nationId,
|
||||
@RequestParam("levelId") Long tankLevelId,
|
||||
@RequestParam("cost") int cost
|
||||
) {
|
||||
return new TankDTO(tankService.addTank(name, nationService.findNation(nationId), tankLevelService.findLevel(tankLevelId), cost));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public TankDTO updateTank(@PathVariable Long id,
|
||||
@RequestParam("firstName") String name,
|
||||
@RequestParam("nationId") Long nationId,
|
||||
@RequestParam("levelId") Long tankLevelId,
|
||||
@RequestParam("cost") int cost
|
||||
) {
|
||||
return new TankDTO(tankService.updateTank(id, name, nationService.findNation(nationId), tankLevelService.findLevel(tankLevelId), cost));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public TankDTO deleteTank(@PathVariable Long id) {
|
||||
return new TankDTO(tankService.deleteTank(id));
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.controller.DTO.LevelDTO;
|
||||
import premium_store.service.TankLevelService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//привязываем наш контроллер к придуманному корневому URL благодаря аннотациям
|
||||
//здесь происходит внедрение зависимости нашего сервиса
|
||||
//так же здесь прописываем вызовы методов CRUD в привязке к URL
|
||||
@RestController
|
||||
@RequestMapping("/level")
|
||||
public class TankLevelController {
|
||||
private final TankLevelService tankLevelService;
|
||||
|
||||
public TankLevelController(TankLevelService tankLevelService){
|
||||
this.tankLevelService = tankLevelService;
|
||||
}
|
||||
|
||||
//аннотация PathVariable связывает значения id из URL и Long id
|
||||
@GetMapping("/{id}")
|
||||
public LevelDTO getLevel(@PathVariable Long id) {
|
||||
return new LevelDTO(tankLevelService.findLevel(id));
|
||||
}
|
||||
|
||||
//с помощью Java Stream преобразуем набор пришедших данных в объекты StudentDto
|
||||
@GetMapping("/")
|
||||
public List<LevelDTO> getLevels() {
|
||||
return tankLevelService.findAllLevels().stream()
|
||||
.map(LevelDTO::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PostMapping("/")
|
||||
public LevelDTO createLevel(@RequestParam("Level") int level) {
|
||||
return new LevelDTO(tankLevelService.addLevel(level));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public LevelDTO updateLevel(@PathVariable Long id,
|
||||
@RequestParam("Level") int level) {
|
||||
return new LevelDTO(tankLevelService.updateLevel(id, level));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public LevelDTO deleteLevel(@PathVariable Long id) {
|
||||
return new LevelDTO(tankLevelService.deleteLevel(id));
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package premium_store.model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
public class Level {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private int level;
|
||||
|
||||
public Level() {
|
||||
}
|
||||
|
||||
public Level(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
//возвращает id
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
//возвращает нацию
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
//метод для сравнения
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
Level level = (Level) o;
|
||||
|
||||
return Objects.equals(id, level.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
//преобразование данных по объекту в строчку
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Level{" +
|
||||
"id=" + id +
|
||||
", level='" + level + '}';
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package premium_store.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import premium_store.model.Nation;
|
||||
|
||||
//класс для взаимодействия с БД вместо низкоуровневого EntityManager
|
||||
//передаём тип класса и тип id его элементов
|
||||
public interface NationRepository extends JpaRepository<Nation, Long> {
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package premium_store.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import premium_store.model.TankLevel;
|
||||
|
||||
//класс для взаимодействия с БД вместо низкоуровневого EntityManager
|
||||
//передаём тип класса и тип id его элементов
|
||||
public interface TankLevelRepository extends JpaRepository<TankLevel, Long> {
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package premium_store.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import premium_store.model.Tank;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TankRepository extends JpaRepository<Tank, Long> {
|
||||
@Query("SELECT t FROM Tank t WHERE t.nation.nation = :nation AND t.tankLevel.level BETWEEN :llevelid AND :l2levelid")
|
||||
List<Tank> checkNationAndLevel(
|
||||
@Param("nation") String nation,
|
||||
@Param("llevelid") int levelOne,
|
||||
@Param("l2levelid") int levelTwo);
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
package premium_store.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import premium_store.model.TankLevel;
|
||||
import premium_store.repository.TankLevelRepository;
|
||||
import premium_store.service.exception.LevelNotFoundException;
|
||||
import premium_store.util.validation.ValidatorUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
//сервис после удаления EntityManager и добавления нашего репозитория. То есть у него уже все методы работы с полями прописаны за нас?
|
||||
@Service
|
||||
public class TankLevelService {
|
||||
private final TankLevelRepository tankLevelRepository;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
|
||||
public TankLevelService(TankLevelRepository tankLevelRepository, ValidatorUtil validatorUtil){
|
||||
this.tankLevelRepository = tankLevelRepository;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TankLevel addLevel(int newLevel) {
|
||||
final TankLevel tankLevel = new TankLevel(newLevel);
|
||||
validatorUtil.validate(tankLevel);
|
||||
|
||||
return tankLevelRepository.save(tankLevel);
|
||||
}
|
||||
|
||||
//здесь используем Optional - спец. тип данных, позволяющий определять, вернулось ли что-то при вызове метода, или вернулся null
|
||||
@Transactional(readOnly = true)
|
||||
public TankLevel findLevel(Long id) {
|
||||
final Optional<TankLevel> level = tankLevelRepository.findById(id);
|
||||
|
||||
//благодаря Optional можем вызвать orElseThrow, который в случае null сделает проброс кастомного исключения
|
||||
return level.orElseThrow(() -> new LevelNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<TankLevel> findAllLevels() {
|
||||
return tankLevelRepository.findAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TankLevel updateLevel(Long id, int newLevel) {
|
||||
final TankLevel currentTankLevel = findLevel(id);
|
||||
currentTankLevel.setLevel(newLevel);
|
||||
validatorUtil.validate(currentTankLevel);
|
||||
|
||||
return tankLevelRepository.save(currentTankLevel);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TankLevel deleteLevel(Long id) {
|
||||
final TankLevel currentTankLevel = findLevel(id);
|
||||
tankLevelRepository.delete(currentTankLevel);
|
||||
|
||||
return currentTankLevel;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllLevels() {
|
||||
tankLevelRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
package premium_store.service.exception;
|
||||
|
||||
//класс-обработчик ошибки, когда вытаемся вытащить студента из бд по некорректному id
|
||||
//наследуем от RuntimeException, т. к. он позволяет его проще вызывать
|
||||
public class LevelNotFoundException extends RuntimeException {
|
||||
public LevelNotFoundException(Long id) {
|
||||
super(String.format("Level with id [%s] is not found", id));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package premium_store.service.exception;
|
||||
|
||||
public class NationNotFoundException extends RuntimeException {
|
||||
public NationNotFoundException(Long id) {
|
||||
super(String.format("Nation with id [%s] is not found", id));
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package premium_store.service.exception;
|
||||
|
||||
public class TankNotFoundException extends RuntimeException {
|
||||
public TankNotFoundException(Long id) {
|
||||
super(String.format("Tank with id [%s] is not found", id));
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package premium_store.util.validation;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
//класс для передачи списка ошибок, если они возникают
|
||||
public class ValidationException extends RuntimeException {
|
||||
public ValidationException(Set<String> errors) {
|
||||
super(String.join("\n", errors));
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package premium_store.util.validation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.ValidatorFactory;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
//компонент для ручной валидации данных (проверка на заполнение или пустоту, выход за допустимые диапазоны значений и т. д.)
|
||||
@Component
|
||||
public class ValidatorUtil {
|
||||
private final Validator validator;
|
||||
|
||||
public ValidatorUtil() {
|
||||
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
|
||||
this.validator = factory.getValidator();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void validate(T object) {
|
||||
final Set<ConstraintViolation<T>> errors = validator.validate(object);
|
||||
if (!errors.isEmpty()) {
|
||||
throw new ValidationException(errors.stream()
|
||||
.map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user