Compare commits
57 Commits
main
...
LabWork06_
Author | SHA1 | Date | |
---|---|---|---|
6d3657f26c | |||
1875dcd30d | |||
f27cacb884 | |||
fef50dbb5c | |||
74625c1a88 | |||
949716b6b2 | |||
2f0e1edba8 | |||
b3f016eca3 | |||
53911bccf8 | |||
1820b567a6 | |||
92c7edb8f2 | |||
13b10d97ac | |||
537fffe177 | |||
d1b53d51b6 | |||
8803176103 | |||
1ba8b942ea | |||
1205d4125f | |||
6a83584f50 | |||
65800b9a6f | |||
68a0bd7c14 | |||
cefa4a0c02 | |||
c3577d6ad5 | |||
42e4e9c1cc | |||
fc65e71ffa | |||
ce4fbf0aeb | |||
f735902131 | |||
8c7d51f315 | |||
d473c67d59 | |||
411dc8319e | |||
6d178b58aa | |||
9b72e39432 | |||
167ca7eb82 | |||
843ec51b8c | |||
9560588d41 | |||
b4e48f5c5e | |||
9f643c860d | |||
4d76a86c8e | |||
ce8c5ca468 | |||
15b5ebd034 | |||
81f8008aab | |||
307acf867a | |||
01470af3bb | |||
a0ff253478 | |||
973e2c6a16 | |||
8a23075ac6 | |||
43cc05427d | |||
e2d99cec86 | |||
8ecf9e9cab | |||
d0f9fb2bd0 | |||
2844d545ce | |||
c4e3593fa4 | |||
8fa5b5f8c4 | |||
663fd6ad72 | |||
b79d35d1af | |||
9297fb06ae | |||
438e1ed4d4 | |||
eea6638930 |
3
.idea/.gitignore
vendored
Normal file
3
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
19
.idea/compiler.xml
Normal file
19
.idea/compiler.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Gradle Imported" enabled="true">
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<processorPath useClasspath="false">
|
||||
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-configuration-processor/2.6.5/dced3550504fffed49b76972b4c4aed274a623ee/spring-boot-configuration-processor-2.6.5.jar" />
|
||||
</processorPath>
|
||||
<module name="premium_store.main" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
<bytecodeTargetLevel>
|
||||
<module name="spring_online_calculator" target="17" />
|
||||
<module name="spring_online_calculator.main" target="17" />
|
||||
<module name="spring_online_calculator.test" target="17" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
</project>
|
8
.idea/discord.xml
Normal file
8
.idea/discord.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="ASK" />
|
||||
<option name="nameOverrideEnabled" value="true" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
19
.idea/gradle.xml
Normal file
19
.idea/gradle.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="delegatedBuild" value="true" />
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$/spring_online_calculator" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$/spring_online_calculator" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
15
.idea/inspectionProfiles/Project_Default.xml
Normal file
15
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="th:text" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
20
.idea/jarRepositories.xml
Normal file
20
.idea/jarRepositories.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
6
.idea/jpa-buddy.xml
Normal file
6
.idea/jpa-buddy.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JpaBuddyIdeaProjectConfig">
|
||||
<option name="defaultUnitInitialized" value="true" />
|
||||
</component>
|
||||
</project>
|
8
.idea/misc.xml
Normal file
8
.idea/misc.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="temurin-17" project-jdk-type="JavaSDK" />
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="jpab" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/С репозитория.iml" filepath="$PROJECT_DIR$/.idea/С репозитория.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
124
.idea/uiDesigner.xml
Normal file
124
.idea/uiDesigner.xml
Normal file
@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
9
.idea/С репозитория.iml
Normal file
9
.idea/С репозитория.iml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "online_calculator",
|
||||
"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"
|
||||
},
|
14
front/premium_store/public/index.html
Normal file
14
front/premium_store/public/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="../src/styles/App.css">
|
||||
<title>Premium store of our games! :)</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.js"></script>
|
||||
</body>
|
||||
</html>
|
2
front/premium_store/src/.env
Normal file
2
front/premium_store/src/.env
Normal file
@ -0,0 +1,2 @@
|
||||
VITE_NODE_ENV=development
|
||||
VITE_API_URL=http://localhost:8080
|
60
front/premium_store/src/App.js
Normal file
60
front/premium_store/src/App.js
Normal file
@ -0,0 +1,60 @@
|
||||
import { Routes, BrowserRouter, Route } from 'react-router-dom';
|
||||
|
||||
import PrivateRoutes from "./components/items/Pages/PrivateRoutes";
|
||||
|
||||
import AddLevel from './components/AddLevel';
|
||||
import AddNation from './components/AddNation';
|
||||
import AddTank from './components/AddTank';
|
||||
import AddClient from './components/AddClient';
|
||||
import PageForChecking from './components/PageForChecking';
|
||||
import SingupPage from './components/items/Pages/SingupPage';
|
||||
import LoginPage from './components/items/Pages/LoginPage';
|
||||
import AccountPage from './components/items/Pages/AccountPage';
|
||||
import MyNavBar from './components/items/Pages/MyNavbar';
|
||||
|
||||
/*function Router(props) {
|
||||
return useRoutes(props.rootRoute);
|
||||
}*/
|
||||
|
||||
function App() {
|
||||
const routes = [
|
||||
{ index: true, element: <LoginPage />, userGroup: "USER"},
|
||||
{ path: 'levels', element: <AddLevel />, label: 'Обзор уровней', userGroup: "AUTH" },
|
||||
{ path: 'nations', element: <AddNation />, label: 'Обзор наций', userGroup: "AUTH" },
|
||||
{ path: 'tanks', element: <AddTank />, label: 'Обзор танков', userGroup: "AUTH"},
|
||||
{ path: 'clients', element: <AddClient />, label: 'Обзор клиентов', userGroup: "ADMIN"},
|
||||
{ path: 'checkPage', element: <PageForChecking />, label: 'Фильтр по танкам', userGroup: "AUTH"},
|
||||
{ path: 'account', element: <AccountPage />, label: 'Аккаунт', userGroup: "AUTH"},
|
||||
{ path: 'login', element: <LoginPage />, label: 'Вход', userGroup: "AUTH"}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
<BrowserRouter>
|
||||
<MyNavBar links={routes}></MyNavBar>
|
||||
<div className="content-div">
|
||||
<Routes>
|
||||
<Route element={<LoginPage />} path="/login" />
|
||||
<Route element={<SingupPage />} path="/singup" />
|
||||
<Route element={<PrivateRoutes userGroup="AUTH" />}>
|
||||
<Route element={<AccountPage />} path="/account" />
|
||||
<Route element={<AddTank />} path="/tanks" />
|
||||
<Route element={<AddLevel />} path="/levels" />
|
||||
<Route element={<PageForChecking />} path="/checkPage" />
|
||||
<Route element={<AddNation />} path="/nations" exact />
|
||||
<Route element={<AddNation />} path="*" />
|
||||
</Route>
|
||||
<Route element={<PrivateRoutes userGroup="ADMIN" />}>
|
||||
<Route element={<AddClient />} path="/clients" />
|
||||
</Route>
|
||||
</Routes>
|
||||
</div>
|
||||
<footer className="border-top">
|
||||
Footer
|
||||
</footer>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
14
front/premium_store/src/components/AddClient.css
Normal file
14
front/premium_store/src/components/AddClient.css
Normal file
@ -0,0 +1,14 @@
|
||||
.add-client-input{
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border: 3px solid;
|
||||
border-radius: 10px;
|
||||
border-color: #505050;
|
||||
}
|
||||
|
||||
.add-level-button{
|
||||
border-radius: 10px;
|
||||
border-color: #505050;
|
||||
background-color: #FFE430;
|
||||
font-weight: 900;
|
||||
}
|
122
front/premium_store/src/components/AddClient.jsx
Normal file
122
front/premium_store/src/components/AddClient.jsx
Normal file
@ -0,0 +1,122 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import '../styles/App.css';
|
||||
import ClientList from './items/GameClient/ClientList';
|
||||
import './AddClient.css';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0";
|
||||
|
||||
export default function AddClient() {
|
||||
const [clientItems, setClientItems] = useState([]);
|
||||
|
||||
//для создания нового уровня
|
||||
const [clientNickName, setClientNickName] = useState();
|
||||
|
||||
const [clientEmail, setClientEmail] = useState();
|
||||
|
||||
const [clientBalance, setClientBalance] = useState();
|
||||
|
||||
const [pageNumbers, setPageNumbers] = useState([]);
|
||||
|
||||
const [pageNumber, setPageNumber] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
getUsers(1);
|
||||
}, []);
|
||||
|
||||
const pageButtonOnClick = function (page) {
|
||||
getUsers(page);
|
||||
}
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
//загрузка всех имеющихся клиентов при старте
|
||||
const getUsers = async function (page) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader()
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/users?page=${page}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
setClientItems(data.first.content);
|
||||
setPageNumber(data.first.number);
|
||||
setPageNumbers(data.second);
|
||||
}
|
||||
|
||||
//обновить список уровней
|
||||
function CheckArray(){
|
||||
getUsers(1);
|
||||
}
|
||||
|
||||
//добавили условную отрисовку
|
||||
return(
|
||||
<div>
|
||||
<div className="Group_create_level">
|
||||
<h1>Генератор клиентов</h1>
|
||||
<div>
|
||||
<p style={{fontWeight: "900", marginTop:"10px"}}>
|
||||
Введите никнейм клиента:
|
||||
<input
|
||||
className="add-client-input"
|
||||
value={clientNickName}
|
||||
onChange={e => setClientNickName(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p style={{fontWeight: "900", marginTop:"10px"}}>
|
||||
Введите почту клиента:
|
||||
<input
|
||||
className="add-client-input"
|
||||
value={clientEmail}
|
||||
onChange={e => setClientEmail(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p style={{fontWeight: "900", marginTop:"10px"}}>
|
||||
Введите баланс клиента:
|
||||
<input
|
||||
className="add-client-input"
|
||||
value={clientBalance}
|
||||
onChange={e => setClientBalance(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<button className='add-level-button'
|
||||
onClick={CheckArray}
|
||||
>
|
||||
Вывести всех клиентов
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Card_list">
|
||||
{clientItems.length !== 0
|
||||
?
|
||||
<ClientList clientItems={clientItems}
|
||||
/>
|
||||
:
|
||||
<h1 style={{textAlign: 'center'}}>В БД отсутствуют какие-либо клиенты!</h1>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
Pages:
|
||||
</p>
|
||||
<nav>
|
||||
<ul className="pagination">
|
||||
{pageNumbers.map((number) => (
|
||||
<li className={`page-item ${number === pageNumber + 1 ? "active" : ""}`}
|
||||
onClick={() => pageButtonOnClick(number)}>
|
||||
<a className="page-link" href="#">{number}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
14
front/premium_store/src/components/AddLevel.css
Normal file
14
front/premium_store/src/components/AddLevel.css
Normal file
@ -0,0 +1,14 @@
|
||||
.add-level-input{
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border: 3px solid;
|
||||
border-radius: 10px;
|
||||
border-color: #505050;
|
||||
}
|
||||
|
||||
.add-level-button{
|
||||
border-radius: 10px;
|
||||
border-color: #505050;
|
||||
background-color: #FFE430;
|
||||
font-weight: 900;
|
||||
}
|
108
front/premium_store/src/components/AddLevel.jsx
Normal file
108
front/premium_store/src/components/AddLevel.jsx
Normal file
@ -0,0 +1,108 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import '../styles/App.css';
|
||||
import LevelList from './items/Level/LevelList';
|
||||
import './AddLevel.css';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/level";
|
||||
|
||||
//компонент для просмотра, создания и удаления уровней
|
||||
const AddLevel = () => {
|
||||
const [levelItems, setLevelItems] = useState([]);
|
||||
|
||||
//для создания нового уровня
|
||||
const [level, setLevel] = useState({level: ''});
|
||||
|
||||
//загрузка всех имеющихся уровней при старте
|
||||
useEffect(() => {
|
||||
getLevels();
|
||||
}, [])
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
//загрузка всех имеющихся уровней при старте
|
||||
const getLevels = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/getLevels`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setLevelItems(data);
|
||||
}
|
||||
|
||||
//обновить список уровней
|
||||
function CheckArray(){
|
||||
getLevels();
|
||||
}
|
||||
|
||||
//добавление нового уровня
|
||||
const addNewLevel = async function (){
|
||||
if(level.level === ''){
|
||||
return;
|
||||
}
|
||||
else {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/?Level=${level.level}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
getLevels();
|
||||
setLevel({level: ''});
|
||||
}
|
||||
}
|
||||
|
||||
//добавили условную отрисовку
|
||||
return(
|
||||
<div>
|
||||
<div className="Group_create_level">
|
||||
<h1>Генератор уровней</h1>
|
||||
|
||||
<div>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Введите уровень:
|
||||
<input
|
||||
className="add-level-input"
|
||||
value={level.level}
|
||||
onChange={e => setLevel({level: e.target.value})}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<button className='add-level-button'
|
||||
onClick={addNewLevel}
|
||||
>
|
||||
Создать уровень
|
||||
</button>
|
||||
<button className='add-level-button'
|
||||
onClick={CheckArray}
|
||||
>
|
||||
Вывести все уровни
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Card_list">
|
||||
{levelItems.length !== 0
|
||||
?
|
||||
<LevelList levelItems={levelItems}
|
||||
/>
|
||||
:
|
||||
<h1 style={{textAlign: 'center'}}>В БД отсутствуют какие-либо уровни!</h1>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddLevel;
|
14
front/premium_store/src/components/AddNation.css
Normal file
14
front/premium_store/src/components/AddNation.css
Normal file
@ -0,0 +1,14 @@
|
||||
.add-nation-input{
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border: 3px solid;
|
||||
border-radius: 10px;
|
||||
border-color: #505050;
|
||||
}
|
||||
|
||||
.add-level-button{
|
||||
border-radius: 10px;
|
||||
border-color: #505050;
|
||||
background-color: #FFE430;
|
||||
font-weight: 900;
|
||||
}
|
107
front/premium_store/src/components/AddNation.jsx
Normal file
107
front/premium_store/src/components/AddNation.jsx
Normal file
@ -0,0 +1,107 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import '../styles/App.css';
|
||||
import NationList from './items/Nation/NationList';
|
||||
import './AddNation.css';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/nation";
|
||||
|
||||
//компонент для просмотра, создания и удаления уровней
|
||||
const AddNation = () => {
|
||||
const [nationItems, setNationItems] = useState([]);
|
||||
|
||||
//для создания нового уровня
|
||||
const [nation, setNation] = useState({nation: ''});
|
||||
|
||||
//загрузка всех имеющихся уровней при старте
|
||||
useEffect(() => {
|
||||
getNations();
|
||||
}, [])
|
||||
|
||||
//обновить список уровней
|
||||
function CheckArray(){
|
||||
getNations();
|
||||
}
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const getNations = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/getNations`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setNationItems(data);
|
||||
}
|
||||
|
||||
//добавление нового уровня
|
||||
const addNewNation = async function (){
|
||||
if(nation.nation === ''){
|
||||
return;
|
||||
}
|
||||
else {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
console.log(JSON.stringify(nation));
|
||||
const requestUrl = host + `/create/?nation=${nation.nation}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
getNations();
|
||||
setNation({nation: ''});
|
||||
}
|
||||
}
|
||||
|
||||
//добавили условную отрисовку
|
||||
return(
|
||||
<div>
|
||||
<div className="Group_create_level">
|
||||
<h1>Генератор наций</h1>
|
||||
<div>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Введите нацию:
|
||||
<input
|
||||
className="add-nation-input"
|
||||
value={nation.nation}
|
||||
onChange={e => setNation({nation: e.target.value})}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<button className='add-level-button'
|
||||
onClick={addNewNation}
|
||||
>
|
||||
Создать нацию
|
||||
</button>
|
||||
<button className='add-level-button'
|
||||
onClick={CheckArray}
|
||||
>
|
||||
Вывести все нации
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Card_list">
|
||||
{nationItems.length !== 0
|
||||
?
|
||||
<NationList nationItems={nationItems}
|
||||
/>
|
||||
:
|
||||
<h1 style={{textAlign: 'center'}}>В БД отсутствуют какие-либо нации!</h1>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNation;
|
15
front/premium_store/src/components/AddTank.css
Normal file
15
front/premium_store/src/components/AddTank.css
Normal file
@ -0,0 +1,15 @@
|
||||
.add-tank-input{
|
||||
margin-top: 10px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border: 3px solid;
|
||||
border-radius: 10px;
|
||||
border-color: #505050;
|
||||
}
|
||||
|
||||
.add-level-button{
|
||||
border-radius: 10px;
|
||||
border-color: #505050;
|
||||
background-color: #FFE430;
|
||||
font-weight: 900;
|
||||
}
|
201
front/premium_store/src/components/AddTank.jsx
Normal file
201
front/premium_store/src/components/AddTank.jsx
Normal file
@ -0,0 +1,201 @@
|
||||
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 hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/";
|
||||
|
||||
const AddTank = () => {
|
||||
const [tankItems, setTankItems] = useState([]);
|
||||
|
||||
//для создания нового танка
|
||||
const [tankName, setTankName] = useState({tankName: ''});
|
||||
|
||||
const [tankCost, setTankCost] = useState({tankCost: ''});
|
||||
|
||||
const [nationItems, setNationItems] = useState([]);
|
||||
|
||||
const [levelItems, setLevelItems] = useState([]);
|
||||
|
||||
//храним выбранные нацию и уровень для нового танка
|
||||
const [chooiceNation, setChooiceNation] = useState();
|
||||
const [chooiceLevel, setChooiceLevel] = useState();
|
||||
|
||||
const [fileByte, setFileByte] = useState(null);
|
||||
const inputRef = useRef();
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
//загрузка всеГо необходимого при старте
|
||||
useEffect(() => {
|
||||
getTanks();
|
||||
getLevels();
|
||||
getNations();
|
||||
}, [])
|
||||
|
||||
//загрузка всех имеющихся танков, а также уровней и наций при старте
|
||||
const getTanks = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `tank/`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setTankItems(data);
|
||||
}
|
||||
|
||||
const getNations = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `nation/getNations`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setNationItems(data);
|
||||
}
|
||||
|
||||
//загрузка всех имеющихся уровней при старте
|
||||
const getLevels = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `level/getLevels`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setLevelItems(data);
|
||||
}
|
||||
|
||||
//обновить список танков
|
||||
function CheckArray(){
|
||||
getTanks();
|
||||
getLevels();
|
||||
getNations();
|
||||
}
|
||||
|
||||
//добавление нового танка
|
||||
async function addNewTank(){
|
||||
console.log(tankName);
|
||||
console.log(tankCost);
|
||||
console.log(chooiceNation);
|
||||
console.log(chooiceLevel);
|
||||
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `tank/create/?firstName=${tankName.tankName}&nationId=${chooiceNation}&levelId=${chooiceLevel}&cost=${tankCost.tankCost}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
CheckArray();
|
||||
setTankCost({tankCost: ''});
|
||||
setTankName({tankName: ''});
|
||||
}
|
||||
|
||||
const getChoiceNation = (newId) => {
|
||||
setChooiceNation(nationItems[newId - 1].id);
|
||||
}
|
||||
|
||||
const getChoiceLevel = (newId) => {
|
||||
setChooiceLevel(levelItems[newId - 1].id);
|
||||
}
|
||||
|
||||
//добавили условную отрисовку
|
||||
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) => getChoiceLevel(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"}}>
|
||||
Введите название:
|
||||
<input
|
||||
className="add-tank-input"
|
||||
value={tankName.tankName}
|
||||
onChange={e => setTankName({tankName: e.target.value})}
|
||||
/>
|
||||
</p>
|
||||
<p style={{fontWeight: "900"}}>
|
||||
Введите стоимость:
|
||||
<input
|
||||
className="add-tank-input"
|
||||
value={tankCost.tankCost}
|
||||
onChange={e => setTankCost({tankCost: e.target.value})}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<button className='add-level-button'
|
||||
onClick={addNewTank}
|
||||
>
|
||||
Создать танк
|
||||
</button>
|
||||
<button className='add-level-button'
|
||||
onClick={CheckArray}
|
||||
>
|
||||
Вывести все танки
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Card_list">
|
||||
{tankItems.length !== 0
|
||||
?
|
||||
<TankList tankItems={tankItems}
|
||||
/>
|
||||
:
|
||||
<h1 style={{textAlign: 'center'}}>В БД отсутствуют какие-либо танки!</h1>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddTank
|
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,9 @@
|
||||
export default class UserSignupDto {
|
||||
constructor(args) {
|
||||
this.login = args.login;
|
||||
this.email = args.email;
|
||||
this.balance = args.balance;
|
||||
this.password = args.password;
|
||||
this.passwordConfirm = args.passwordConfirm;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
.client-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;
|
||||
}
|
||||
|
||||
.client-attribute{
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.client-button-group{
|
||||
display: flex;
|
||||
width: 20%;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.client-button{
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import './ClientItem.css';
|
||||
import ModalClient from './ModalClient';
|
||||
import ModalTankNation from '../Nation/ModalTankNation';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/";
|
||||
|
||||
const ClientItem = (data) => {
|
||||
|
||||
const [client, setClient] = useState(null);
|
||||
|
||||
//состояние для контроля вызова модального окна
|
||||
const[modal, setModal] = useState(false);
|
||||
|
||||
//состояние для вызова окна показа списка танков нации
|
||||
const[modalNation, setModalNation] = useState(false);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const deleteClient = async function (id) {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/user/${id}`;
|
||||
await fetch(requestUrl, requestParams);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="client-card">
|
||||
<p className="client-attribute"> id: {data.clientItem.id} </p>
|
||||
<p className="client-attribute"> Никнейм: {data.clientItem.login} </p>
|
||||
<p className="client-attribute"> Баланс: {data.clientItem.balance} </p>
|
||||
<p className="client-attribute"> Почта: {data.clientItem.email} </p>
|
||||
<p className="client-attribute"> Роль: {data.clientItem.role} </p>
|
||||
<div className='client-button-group'>
|
||||
<button className="client-button" type="button"
|
||||
onClick={() => setModal(true)}
|
||||
>
|
||||
Редактировать
|
||||
</button>
|
||||
<button className="client-button" type="button"
|
||||
onClick={deleteClient(data.clientItem.id)}
|
||||
>
|
||||
Удалить
|
||||
</button>
|
||||
<button className="nation-button" type="button"
|
||||
onClick={() => setModalNation(true)}
|
||||
>
|
||||
Список танков
|
||||
</button>
|
||||
<ModalClient
|
||||
data={data.clientItem}
|
||||
visible={modal}
|
||||
setVisible={setModal}
|
||||
/>
|
||||
<ModalTankNation
|
||||
data={data.clientItem}
|
||||
visible={modalNation}
|
||||
setVisible={setModalNation}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClientItem
|
@ -0,0 +1,22 @@
|
||||
import React, { useEffect} from 'react';
|
||||
import ClientItem from './ClientItem';
|
||||
|
||||
const ClientList = (clients) => {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h1 style={{textAlign: 'center', fontFamily: 'courier, monospace', background: '#FF652F', borderRadius: '10px'}}>
|
||||
Список существующих клиентов:
|
||||
</h1>
|
||||
</div>
|
||||
{clients.clientItems.map((clientItem) =>
|
||||
<ClientItem
|
||||
clientItem={clientItem}
|
||||
key={clientItem.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ClientList;
|
@ -0,0 +1,116 @@
|
||||
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.login);
|
||||
|
||||
const [clientEmail, setClientEmail] = useState(data.email);
|
||||
|
||||
const [clientBalance, setClientBalance] = useState(data.balance);
|
||||
|
||||
const [clientPassword, setClientPassword] = useState(data.password);
|
||||
|
||||
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 + '?login='
|
||||
+ clientNickName + '&email=' + clientEmail + '&password=' + clientPassword + + '&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>
|
||||
<p style={{marginTop: "10px"}}>
|
||||
Пароль:
|
||||
<input
|
||||
className="add-client-input"
|
||||
value={clientPassword}
|
||||
onChange={e => setClientPassword(e.target.value)}
|
||||
/>
|
||||
</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;
|
||||
}
|
58
front/premium_store/src/components/items/Level/LevelItem.jsx
Normal file
58
front/premium_store/src/components/items/Level/LevelItem.jsx
Normal file
@ -0,0 +1,58 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import './LevelItem.css';
|
||||
import ModalLevel from './ModalLevel';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/level";
|
||||
|
||||
//отвечает за отдельно взятый уровень (вывод карточки с ним)
|
||||
const LevelItem = (data) => {
|
||||
|
||||
const [level, setLevel] = useState(null);
|
||||
|
||||
//состояние для контроля вызова модального окна
|
||||
const[modal, setModal] = useState(false);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const deleteLevel = async function (id) {
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/${id}`;
|
||||
await fetch(requestUrl, requestParams);
|
||||
}
|
||||
|
||||
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={event => deleteLevel(data.levelItem.id)}
|
||||
>
|
||||
Удалить
|
||||
</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,62 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import cl from './ModalLevel.module.css';
|
||||
import '../../AddLevel.css';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/level";
|
||||
|
||||
const ModalLevel = ({data, visible, setVisible}) => {
|
||||
|
||||
//для обновления уровня
|
||||
const [level, setLevel] = useState(data.level);
|
||||
|
||||
//для контроля видимости модалки
|
||||
const rootClasses = [cl.myModal];
|
||||
|
||||
if(visible)
|
||||
{
|
||||
rootClasses.push(cl.active);
|
||||
}
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
//добавление нового уровня
|
||||
const updateLevel = async function () {
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
console.log(level);
|
||||
const requestUrl = host + `/${data.id}?Level=${level}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
console.log("Обновление успешно!")
|
||||
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={event => 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,61 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import cl from './ModalNation.module.css';
|
||||
import '../../AddNation.css';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/nation";
|
||||
|
||||
const ModalNation = ({data, visible, setVisible}) => {
|
||||
//для обновления уровня
|
||||
const [nation, setNation] = useState(data.nation);
|
||||
|
||||
const nullId = 0;
|
||||
|
||||
//для контроля видимости модалки
|
||||
const rootClasses = [cl.myModal];
|
||||
|
||||
if(visible)
|
||||
{
|
||||
rootClasses.push(cl.active);
|
||||
}
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const updateNation = async function () {
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/${data.id}?nation=${nation}&tankId=${nullId}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
console.log("Обновление успешно!")
|
||||
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={event => updateNation()}
|
||||
>
|
||||
Сохранить
|
||||
</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("Загрузка МОДАЛКИ")
|
||||
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,75 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import './NationItem.css';
|
||||
import ModalNation from './ModalNation';
|
||||
import ModalTankNation from './ModalTankNation';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/nation";
|
||||
|
||||
//отвечает за отдельно взятую нацию (вывод карточки с ним)
|
||||
const NationItem = (data) => {
|
||||
const [nation, setNation] = useState(null);
|
||||
|
||||
//состояние для контроля вызова модального окна
|
||||
const[modal, setModal] = useState(false);
|
||||
|
||||
//состояние для вызова окна показа списка танков нации
|
||||
const[modalNation, setModalNation] = useState(false);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const deleteFecth = async function (id) {
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/${id}`;
|
||||
await fetch(requestUrl, requestParams);
|
||||
}
|
||||
|
||||
/*function deleteNation(id){
|
||||
deleteFecth(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={event => deleteFecth(data.nationItem.id)}
|
||||
>
|
||||
Удалить
|
||||
</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
|
148
front/premium_store/src/components/items/Pages/AccountPage.jsx
Normal file
148
front/premium_store/src/components/items/Pages/AccountPage.jsx
Normal file
@ -0,0 +1,148 @@
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0";
|
||||
|
||||
const AccountPage = function () {
|
||||
|
||||
const [currentUser, setCurrentUser] = useState({});
|
||||
|
||||
const loginInput = useRef();
|
||||
const emailInput = useRef();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
getUser().then(user => setCurrentUser(user));
|
||||
}, []);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const login = async function () {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(currentUser),
|
||||
};
|
||||
const requestUrl = hostURL + "/jwt/login";
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const result = await response.text();
|
||||
if (response.status === 200) {
|
||||
localStorage.setItem("token", result);
|
||||
localStorage.setItem("user", currentUser.login);
|
||||
getRole(result);
|
||||
} else {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
}
|
||||
}
|
||||
|
||||
const getRole = async function (token) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
};
|
||||
const requestUrl = hostURL + `/who_am_i?token=${token}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const result = await response.text();
|
||||
localStorage.setItem("role", result);
|
||||
window.dispatchEvent(new Event("storage"));
|
||||
}
|
||||
|
||||
const updateUser = async function () {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
},
|
||||
body: JSON.stringify(currentUser),
|
||||
};
|
||||
const requestUrl = host + `/user`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const result = await response.text();
|
||||
return result;
|
||||
}
|
||||
|
||||
const getUser = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
let login = localStorage.getItem("user");
|
||||
const requestUrl = host + `/user?login=${login}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const user = await response.json();
|
||||
return user;
|
||||
}
|
||||
|
||||
const onSubmit = function (event) {
|
||||
event.preventDefault();
|
||||
updateUser().then((result) => {
|
||||
alert(result);
|
||||
if (result === "Profile updated") {
|
||||
login();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const onInput = function (event, fieldName) {
|
||||
setCurrentUser(oldUser => ({
|
||||
...oldUser, [fieldName]: event.target.value
|
||||
}));
|
||||
}
|
||||
|
||||
const logoutButtonOnClick = function () {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
window.dispatchEvent(new Event("storage"));
|
||||
navigate("/login");
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="border-bottom pb-3 mb-3">
|
||||
<button class="btn btn-primary"
|
||||
onClick={logoutButtonOnClick}>
|
||||
Log Out
|
||||
</button>
|
||||
</div>
|
||||
<h4 className="mb-4">Update profile</h4>
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">New Login</p>
|
||||
<input className="form-control" type="text" required
|
||||
ref={loginInput} value={currentUser.login}
|
||||
onInput={(event) => onInput(event, "login")} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">New Email</p>
|
||||
<input className="form-control" type="text" required
|
||||
ref={emailInput} value={currentUser.email}
|
||||
onInput={(event) => onInput(event, "email")} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Enter password</p>
|
||||
<input className="form-control" type="password" required
|
||||
ref={emailInput}
|
||||
onInput={(event) => onInput(event, "password")} />
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountPage;
|
90
front/premium_store/src/components/items/Pages/LoginPage.jsx
Normal file
90
front/premium_store/src/components/items/Pages/LoginPage.jsx
Normal file
@ -0,0 +1,90 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useRef } from "react";
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
|
||||
const LoginPage = function () {
|
||||
|
||||
const loginInput = useRef();
|
||||
const passwordInput = useRef();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
const login = async function (login, password) {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({login: login, password: password}),
|
||||
};
|
||||
const response = await fetch(hostURL + "/jwt/login", requestParams);
|
||||
const result = await response.text();
|
||||
if (response.status === 200) {
|
||||
localStorage.setItem("token", result);
|
||||
localStorage.setItem("user", login);
|
||||
getRole(result);
|
||||
} else {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
}
|
||||
}
|
||||
|
||||
const getRole = async function (token) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
};
|
||||
const requestUrl = hostURL + `/who_am_i?token=${token}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const result = await response.text();
|
||||
localStorage.setItem("role", result);
|
||||
window.dispatchEvent(new Event("storage"));
|
||||
navigate("/index");
|
||||
}
|
||||
|
||||
const loginFormOnSubmit = function (event) {
|
||||
event.preventDefault();
|
||||
login(loginInput.current.value, passwordInput.current.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<form onSubmit={(event) => loginFormOnSubmit(event)}>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Login</p>
|
||||
<input className="form-control"
|
||||
type="text" required autofocus
|
||||
ref={loginInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Password</p>
|
||||
<input className="form-control"
|
||||
type="password" required
|
||||
ref={passwordInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<button type="submit" className="btn btn-success">
|
||||
Sing in
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span>Not a member yet? </span>
|
||||
<Link to="/singup">Sing Up here</Link>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginPage;
|
63
front/premium_store/src/components/items/Pages/MyNavbar.jsx
Normal file
63
front/premium_store/src/components/items/Pages/MyNavbar.jsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import '../../../styles/App.css';
|
||||
|
||||
const MyNavBar = function (props) {
|
||||
|
||||
const [userRole, setUserRole] = useState("NONE");
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("storage", () => {
|
||||
getUserRole();
|
||||
});
|
||||
getUserRole();
|
||||
}, [])
|
||||
|
||||
const getUserRole = function () {
|
||||
const role = localStorage.getItem("role") || "NONE";
|
||||
setUserRole(role);
|
||||
}
|
||||
|
||||
const validate = function (userGroup) {
|
||||
if ((userGroup === "AUTH" && userRole !== "NONE") ||
|
||||
(userGroup === userRole)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h1 className="Main-label">Мир танков</h1>
|
||||
</div>
|
||||
<form className="collapse navbar-collapse">
|
||||
<nav className="navbar navbar-expand-lg justify-content-around">
|
||||
<div className="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul className="Main_head navbar-nav me-auto align-items-center">
|
||||
{
|
||||
props.links.map(route => {
|
||||
if (validate(route.userGroup)) {
|
||||
return (
|
||||
<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 MyNavBar;
|
@ -0,0 +1,47 @@
|
||||
import { Outlet, Navigate, useNavigate } from 'react-router-dom';
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const PrivateRoutes = (props) => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("storage", () => {
|
||||
let token = localStorage.getItem("token");
|
||||
if (token) {
|
||||
getRole(token).then((role) => {
|
||||
if (localStorage.getItem("role") !== role) {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
window.dispatchEvent(new Event("storage"));
|
||||
navigate("/index");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [])
|
||||
|
||||
const getRole = async function (token) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
};
|
||||
const requestUrl = `http://localhost:8080/who_am_i?token=${token}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const result = await response.text();
|
||||
return result;
|
||||
}
|
||||
|
||||
let isAllowed = false;
|
||||
let userRole = localStorage.getItem("role");
|
||||
if ((props.userGroup === "AUTH" && userRole) || (props.userGroup === userRole)) {
|
||||
isAllowed = true;
|
||||
}
|
||||
|
||||
return isAllowed ? <Outlet /> : <Navigate to="/login" />;
|
||||
}
|
||||
|
||||
export default PrivateRoutes;
|
@ -0,0 +1,94 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useRef } from "react";
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
|
||||
const SingupPage = function () {
|
||||
|
||||
const loginInput = useRef();
|
||||
const emailInput = useRef();
|
||||
const passwordInput = useRef();
|
||||
const passwordConfirmInput = useRef();
|
||||
const balanceInput = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
const singup = async function (userSinginDto) {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(userSinginDto),
|
||||
};
|
||||
const response = await fetch(hostURL + "/sing_up", requestParams);
|
||||
const result = await response.text();
|
||||
alert(result);
|
||||
}
|
||||
|
||||
const singupFormOnSubmit = function (event) {
|
||||
event.preventDefault();
|
||||
const userSinginDto = {
|
||||
login: loginInput.current.value,
|
||||
email: emailInput.current.value,
|
||||
balance: balanceInput.current.value,
|
||||
password: passwordInput.current.value,
|
||||
passwordConfirm: passwordConfirmInput.current.value
|
||||
}
|
||||
singup(userSinginDto);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<form onSubmit={(event) => singupFormOnSubmit(event)}>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Login</p>
|
||||
<input className="form-control"
|
||||
type="text" required maxlength="64"
|
||||
ref={loginInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Email</p>
|
||||
<input className="form-control"
|
||||
type="text" required
|
||||
ref={emailInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Balance</p>
|
||||
<input className="form-control"
|
||||
type="text" required minlength="3" maxlength="64"
|
||||
ref={balanceInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Password</p>
|
||||
<input className="form-control"
|
||||
type="password" required minlength="3" maxlength="64"
|
||||
ref={passwordInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Confirm Password</p>
|
||||
<input className="form-control"
|
||||
type="password" required minlength="3" maxlength="64"
|
||||
ref={passwordConfirmInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<button type="submit" className="btn btn-success">
|
||||
Create account
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span>Already have an account? </span>
|
||||
<Link to="/login">Sing In here</Link>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SingupPage;
|
158
front/premium_store/src/components/items/Tank/ModalTank.jsx
Normal file
158
front/premium_store/src/components/items/Tank/ModalTank.jsx
Normal file
@ -0,0 +1,158 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import cl from './ModalTank.module.css';
|
||||
import '../../AddTank.css';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/";
|
||||
|
||||
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([]);
|
||||
|
||||
//для контроля видимости модалки
|
||||
const rootClasses = [cl.myModal];
|
||||
|
||||
if(visible)
|
||||
{
|
||||
rootClasses.push(cl.active);
|
||||
}
|
||||
|
||||
//загрузка всего необходимого при старте
|
||||
useEffect(() => {
|
||||
getLevels();
|
||||
getNations();
|
||||
}, [])
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const getNations = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `nation/getNations`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setNationItems(data);
|
||||
}
|
||||
|
||||
//загрузка всех имеющихся уровней при старте
|
||||
const getLevels = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `level/getLevels`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setLevelItems(data);
|
||||
}
|
||||
|
||||
const updateTank = async function () {
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
console.log(data);
|
||||
console.log(chooiceNation);
|
||||
const requestUrl = host + `tank/${data.id}?firstName=${tankName}&nationId=${chooiceNation}&levelId=${chooiceLevel}&cost=${tankCost}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
console.log("Обновление успешно!")
|
||||
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={event => updateTank()}
|
||||
>
|
||||
Сохранить
|
||||
</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;
|
||||
}
|
60
front/premium_store/src/components/items/Tank/TankItem.jsx
Normal file
60
front/premium_store/src/components/items/Tank/TankItem.jsx
Normal file
@ -0,0 +1,60 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import './TankItem.css';
|
||||
import ModalTank from './ModalTank';
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0/tank";
|
||||
|
||||
const TankItem = (data) => {
|
||||
|
||||
const [tank, setTank] = useState(null);
|
||||
|
||||
//состояние для контроля вызова модального окна
|
||||
const[modal, setModal] = useState(false);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const deleteTank = async function (id) {
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/${id}`;
|
||||
await fetch(requestUrl, requestParams);
|
||||
}
|
||||
|
||||
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={event => deleteTank(data.tankItem.id)}
|
||||
>
|
||||
Удалить
|
||||
</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 |
7
front/premium_store/src/index.js
Normal file
7
front/premium_store/src/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<App />
|
||||
)
|
102
front/premium_store/src/styles/App.css
Normal file
102
front/premium_store/src/styles/App.css
Normal file
@ -0,0 +1,102 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/*#505050*/
|
||||
|
||||
#root{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: #151719;
|
||||
background-image: url("../images/wot-is-4.jpg");
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.body{
|
||||
margin: 0; /* Убираем отступы */
|
||||
height: 100%; /* Высота страницы */
|
||||
}
|
||||
|
||||
.App{
|
||||
width: 1300px;
|
||||
}
|
||||
|
||||
.Group_create_level{
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
background-color: #FFE430;
|
||||
opacity: 0.8;
|
||||
border-radius: 10px;
|
||||
margin-top: 15px;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Card_list{
|
||||
padding: 15px;
|
||||
border: 5px solid;
|
||||
border-color: #14A76C;
|
||||
background-color: #151719;
|
||||
opacity: 0.9;
|
||||
border-radius: 10px;
|
||||
margin-top: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Main_head{
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
margin-top: 30px;
|
||||
border: 5px solid;
|
||||
border-color: #FF652F;
|
||||
border-radius: 10px;
|
||||
background-color: #151719;
|
||||
opacity: 0.9;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Button_Main_Group{
|
||||
padding: 5px;
|
||||
border-color: #FF652F;
|
||||
border-radius: 10px;
|
||||
background-color: #FF652F;
|
||||
font-family: Courier, monospace;
|
||||
font-weight: bold;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.add-level-button{
|
||||
padding: 10px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.Main-label{
|
||||
display: flex;
|
||||
padding-top: 16px;
|
||||
font-size: 7vw;
|
||||
font-variant: small-caps;
|
||||
font-stretch: ultra-expanded;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
color: #505050;
|
||||
-webkit-text-stroke-width: 3.0px;
|
||||
-webkit-text-stroke-color: #000000;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
/* Убираем маркеры */
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 0;
|
||||
/* Отступ слева в браузере IE и Opera */
|
||||
padding-left: 0;
|
||||
/* Отступ слева в браузере Firefox, Safari, Chrome */
|
||||
}
|
7
front/premium_store/src/vite.config.js
Normal file
7
front/premium_store/src/vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()]
|
||||
})
|
@ -1,11 +0,0 @@
|
||||
<!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>
|
@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<App />
|
||||
//document.getElementById('root'); - для работы этого надо поставит запятую после <App/>
|
||||
//и делаем import 'react-dom', вместо 'react-dom/client'
|
||||
);
|
||||
|
2
spring_online_calculator/.gitignore
vendored
2
spring_online_calculator/.gitignore
vendored
@ -35,3 +35,5 @@ out/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
*.db
|
@ -1,10 +1,10 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '2.7.8'
|
||||
id 'org.springframework.boot' version '2.6.5'
|
||||
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group = 'com.example'
|
||||
group = 'ru.ulstu.is'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
sourceCompatibility = '17'
|
||||
|
||||
@ -12,9 +12,30 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'com.h2database:h2:2.1.210'
|
||||
|
||||
implementation 'org.webjars:bootstrap:5.1.3'
|
||||
implementation 'org.webjars:jquery:3.6.0'
|
||||
implementation 'org.webjars:font-awesome:6.1.0'
|
||||
implementation 'com.auth0:java-jwt:4.4.0'
|
||||
|
||||
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
|
||||
implementation 'org.hibernate.validator:hibernate-validator'
|
||||
implementation 'org.springdoc:springdoc-openapi-ui:1.6.5'
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
|
||||
|
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
6
spring_online_calculator/gradlew
vendored
6
spring_online_calculator/gradlew
vendored
@ -205,12 +205,6 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
14
spring_online_calculator/gradlew.bat
vendored
14
spring_online_calculator/gradlew.bat
vendored
@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@ -25,7 +25,7 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@ -75,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
@ -1 +1 @@
|
||||
rootProject.name = 'spring_online_calculator'
|
||||
rootProject.name = 'premium_store'
|
||||
|
@ -1,13 +0,0 @@
|
||||
package com.example.spring_online_calculator;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringOnlineCalculatorApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringOnlineCalculatorApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package premium_store;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class PremiumStoreApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PremiumStoreApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package premium_store.configuration;
|
||||
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import premium_store.configuration.jwt.JwtFilter;
|
||||
|
||||
@Configuration
|
||||
public class OpenAPI30Configuration {
|
||||
public static final String API_PREFIX = "/api/1.0";
|
||||
|
||||
@Bean
|
||||
public OpenAPI customizeOpenAPI() {
|
||||
final String securitySchemeName = JwtFilter.TOKEN_BEGIN_STR;
|
||||
return new OpenAPI()
|
||||
.addSecurityItem(new SecurityRequirement()
|
||||
.addList(securitySchemeName))
|
||||
.components(new Components()
|
||||
.addSecuritySchemes(securitySchemeName, new SecurityScheme()
|
||||
.name(securitySchemeName)
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT")));
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package premium_store.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
public class PasswordEncoderConfiguration {
|
||||
@Bean
|
||||
public PasswordEncoder createPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package premium_store.configuration;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import premium_store.configuration.jwt.JwtFilter;
|
||||
import premium_store.controller.controller.GameClientController;
|
||||
import premium_store.model.UserRole;
|
||||
import premium_store.service.GameClientService;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity(
|
||||
securedEnabled = true
|
||||
)
|
||||
public class SecurityConfiguration {
|
||||
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||
public static final String SPA_URL_MASK = "/{path:[^\\.]*}";
|
||||
private final GameClientService userService;
|
||||
private final JwtFilter jwtFilter;
|
||||
|
||||
public SecurityConfiguration(GameClientService userService) {
|
||||
this.userService = userService;
|
||||
this.jwtFilter = new JwtFilter(userService);
|
||||
createAdminOnStartup();
|
||||
}
|
||||
|
||||
private void createAdminOnStartup() {
|
||||
final String admin = "admin";
|
||||
if (userService.findByLogin(admin) == null) {
|
||||
log.info("Admin user successfully created");
|
||||
userService.addClient(admin, "admin@gmail.com", admin, 0, admin, UserRole.ADMIN);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http.cors()
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/", SPA_URL_MASK).permitAll()
|
||||
.antMatchers(HttpMethod.POST, GameClientController.URL_LOGIN).permitAll()
|
||||
.antMatchers(HttpMethod.POST, GameClientController.URL_SING_UP).permitAll()
|
||||
.antMatchers(HttpMethod.POST, GameClientController.URL_WHO_AM_I).permitAll()
|
||||
.anyRequest()
|
||||
.authenticated()
|
||||
.and()
|
||||
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.anonymous();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
|
||||
AuthenticationManagerBuilder authenticationManagerBuilder = http
|
||||
.getSharedObject(AuthenticationManagerBuilder.class);
|
||||
authenticationManagerBuilder.userDetailsService(userService);
|
||||
return authenticationManagerBuilder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return (web) -> web.ignoring()
|
||||
.antMatchers(HttpMethod.OPTIONS, "/**")
|
||||
.antMatchers("/*.js")
|
||||
.antMatchers("/*.html")
|
||||
.antMatchers("/*.css")
|
||||
.antMatchers("/assets/**")
|
||||
.antMatchers("/favicon.ico")
|
||||
.antMatchers("/.js", "/.css")
|
||||
.antMatchers("/swagger-ui/index.html")
|
||||
.antMatchers("/webjars/**")
|
||||
.antMatchers("/swagger-resources/**")
|
||||
.antMatchers("/v3/api-docs/**")
|
||||
.antMatchers("/h2-console");
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package premium_store.configuration;
|
||||
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController(SecurityConfiguration.SPA_URL_MASK).setViewName("forward:/");
|
||||
registry.addViewController("/notFound").setViewName("forward:/");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> containerCustomizer() {
|
||||
return container -> container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedMethods("*");
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package premium_store.configuration.jwt;
|
||||
|
||||
public class JwtException extends RuntimeException {
|
||||
public JwtException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public JwtException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package premium_store.configuration.jwt;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
import premium_store.service.GameClientService;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class JwtFilter extends GenericFilterBean {
|
||||
private static final String AUTHORIZATION = "Authorization";
|
||||
public static final String TOKEN_BEGIN_STR = "Bearer ";
|
||||
|
||||
private final GameClientService userService;
|
||||
|
||||
public JwtFilter(GameClientService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
private String getTokenFromRequest(HttpServletRequest request) {
|
||||
String bearer = request.getHeader(AUTHORIZATION);
|
||||
if (StringUtils.hasText(bearer) && bearer.startsWith(TOKEN_BEGIN_STR)) {
|
||||
return bearer.substring(TOKEN_BEGIN_STR.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void raiseException(ServletResponse response, int status, String message) throws IOException {
|
||||
if (response instanceof final HttpServletResponse httpResponse) {
|
||||
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
httpResponse.setStatus(status);
|
||||
final byte[] body = new ObjectMapper().writeValueAsBytes(message);
|
||||
response.getOutputStream().write(body);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request,
|
||||
ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
if (request instanceof final HttpServletRequest httpRequest) {
|
||||
final String token = getTokenFromRequest(httpRequest);
|
||||
if (StringUtils.hasText(token)) {
|
||||
try {
|
||||
final UserDetails user = userService.loadUserByToken(token);
|
||||
final UsernamePasswordAuthenticationToken auth =
|
||||
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
} catch (JwtException e) {
|
||||
raiseException(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
raiseException(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
String.format("Internal error: %s", e.getMessage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package premium_store.configuration.jwt;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "jwt", ignoreInvalidFields = true)
|
||||
public class JwtProperties {
|
||||
private String devToken = "";
|
||||
private Boolean isDev = true;
|
||||
|
||||
public String getDevToken() {
|
||||
return devToken;
|
||||
}
|
||||
|
||||
public void setDevToken(String devToken) {
|
||||
this.devToken = devToken;
|
||||
}
|
||||
|
||||
public Boolean isDev() {
|
||||
return isDev;
|
||||
}
|
||||
|
||||
public void setDev(Boolean dev) {
|
||||
isDev = dev;
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
package premium_store.configuration.jwt;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.JWTVerifier;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class JwtProvider {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(JwtProvider.class);
|
||||
|
||||
private final static byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
|
||||
private final static String ISSUER = "auth0";
|
||||
|
||||
private final Algorithm algorithm;
|
||||
private final JWTVerifier verifier;
|
||||
|
||||
public JwtProvider(JwtProperties jwtProperties) {
|
||||
if (!jwtProperties.isDev()) {
|
||||
LOG.info("Generate new JWT key for prod");
|
||||
try {
|
||||
final MessageDigest salt = MessageDigest.getInstance("SHA-256");
|
||||
salt.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));
|
||||
LOG.info("Use generated JWT key for prod \n{}", bytesToHex(salt.digest()));
|
||||
algorithm = Algorithm.HMAC256(bytesToHex(salt.digest()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new JwtException(e);
|
||||
}
|
||||
} else {
|
||||
LOG.info("Use default JWT key for dev \n{}", jwtProperties.getDevToken());
|
||||
algorithm = Algorithm.HMAC256(jwtProperties.getDevToken());
|
||||
}
|
||||
verifier = JWT.require(algorithm)
|
||||
.withIssuer(ISSUER)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
byte[] hexChars = new byte[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public String generateToken(String login) {
|
||||
final Date issueDate = Date.from(LocalDate.now()
|
||||
.atStartOfDay(ZoneId.systemDefault())
|
||||
.toInstant());
|
||||
final Date expireDate = Date.from(LocalDate.now()
|
||||
.plusDays(15)
|
||||
.atStartOfDay(ZoneId.systemDefault())
|
||||
.toInstant());
|
||||
return JWT.create()
|
||||
.withIssuer(ISSUER)
|
||||
.withIssuedAt(issueDate)
|
||||
.withExpiresAt(expireDate)
|
||||
.withSubject(login)
|
||||
.sign(algorithm);
|
||||
}
|
||||
|
||||
private DecodedJWT validateToken(String token) {
|
||||
try {
|
||||
return verifier.verify(token);
|
||||
} catch (JWTVerificationException e) {
|
||||
throw new JwtException(String.format("Token verification error: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTokenValid(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
validateToken(token);
|
||||
return true;
|
||||
} catch (JwtException e) {
|
||||
LOG.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<String> getLoginFromToken(String token) {
|
||||
try {
|
||||
return Optional.ofNullable(validateToken(token).getSubject());
|
||||
} catch (JwtException e) {
|
||||
LOG.error(e.getMessage());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import premium_store.model.GameClient;
|
||||
import premium_store.model.UserRole;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
//класс, который соединяет танки клиента в одну строчку (нам так захотелось)
|
||||
public class ClientDTO {
|
||||
private long id;
|
||||
private String login;
|
||||
private String password;
|
||||
private String email;
|
||||
private Integer balance;
|
||||
private List<TankDTO> tanks = new ArrayList<>();
|
||||
private UserRole role;
|
||||
|
||||
public ClientDTO(){ }
|
||||
|
||||
public ClientDTO(GameClient gameClient){
|
||||
this.id = gameClient.getId();
|
||||
this.login = gameClient.getLogin();
|
||||
this.password = gameClient.getPassword();
|
||||
this.email = gameClient.getEmail();
|
||||
this.balance = gameClient.getBalance();
|
||||
|
||||
if(gameClient.getTanks() != null && gameClient.getTanks().size() > 0){
|
||||
this.tanks = gameClient.getTanks().stream()
|
||||
.map(TankDTO::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
this.role = gameClient.getRole();
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLogin(){
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail(){
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Integer getBalance(){
|
||||
return balance;
|
||||
}
|
||||
|
||||
public void setBalance(Integer balance) {
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
public List<TankDTO> getTanks(){
|
||||
return tanks;
|
||||
}
|
||||
|
||||
public Long getIdLastTanks(){
|
||||
if(tanks.size() > 0){
|
||||
return tanks.get(tanks.size() - 1).getId();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setTanks(List<TankDTO> tanks) {
|
||||
this.tanks = tanks;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(UserRole role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import premium_store.model.Nation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FullNationDTO {
|
||||
public Long id;
|
||||
public String nation;
|
||||
public List<TankDTO> tanks;
|
||||
|
||||
public FullNationDTO(){ }
|
||||
|
||||
public FullNationDTO(Nation nation){
|
||||
this.id = nation.getId();
|
||||
this.nation = nation.getNation();
|
||||
this.tanks = nation.getTanksOfThisNation().stream()
|
||||
.map(TankDTO::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getNation() {
|
||||
return nation;
|
||||
}
|
||||
|
||||
public void setNation(String nation) {
|
||||
this.nation = nation;
|
||||
}
|
||||
|
||||
public List<TankDTO> getTanksOfThisNation() {
|
||||
return tanks;
|
||||
}
|
||||
|
||||
public void setTanks(List<TankDTO> tanks) {
|
||||
this.tanks = tanks;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import premium_store.model.TankLevel;
|
||||
|
||||
public class LevelDTO {
|
||||
private Long id;
|
||||
private int level;
|
||||
|
||||
public LevelDTO(){}
|
||||
|
||||
public LevelDTO(TankLevel level){
|
||||
this.id = level.getId();
|
||||
this.level = level.getLevel();
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getLevel(){
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import premium_store.model.Nation;
|
||||
|
||||
public class SimpleNationDTO {
|
||||
private Long id;
|
||||
private String nation;
|
||||
|
||||
public SimpleNationDTO(){}
|
||||
|
||||
public SimpleNationDTO(Nation nation){
|
||||
this.id = nation.getId();
|
||||
this.nation = nation.getNation();
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getNation(){
|
||||
return nation;
|
||||
}
|
||||
|
||||
public void setNation(String nation){
|
||||
this.nation = nation;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
public class SortDTO {
|
||||
private String nation;
|
||||
private int firstLevel;
|
||||
private int secondLevel;
|
||||
|
||||
public SortDTO(){}
|
||||
|
||||
public SortDTO(String nation, int firstLevel, int secondLevel){
|
||||
this.nation = nation;
|
||||
this.firstLevel = firstLevel;
|
||||
this.secondLevel = secondLevel;
|
||||
}
|
||||
|
||||
public String getNation(){
|
||||
return nation;
|
||||
}
|
||||
|
||||
public int getFirstLevel() {
|
||||
return firstLevel;
|
||||
}
|
||||
|
||||
public int getSecondLevel() {
|
||||
return secondLevel;
|
||||
}
|
||||
|
||||
public void setNation(String nation){
|
||||
this.nation = nation;
|
||||
}
|
||||
|
||||
public void setFirstLevel(int firstLevel){
|
||||
this.firstLevel = firstLevel;
|
||||
}
|
||||
|
||||
public void setSecondLevel(int secondLevel) {
|
||||
this.secondLevel = secondLevel;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import premium_store.model.GameClient;
|
||||
import premium_store.model.UserRole;
|
||||
|
||||
//класс, который соединяет танки клиента в одну строчку (нам так захотелось)
|
||||
public class SupportClientDTO {
|
||||
private long id;
|
||||
private String login;
|
||||
private String password;
|
||||
private String email;
|
||||
private Integer balance;
|
||||
private Long tankId;
|
||||
private UserRole role;
|
||||
|
||||
public SupportClientDTO(){ }
|
||||
|
||||
public SupportClientDTO(GameClient gameClient){
|
||||
this.id = gameClient.getId();
|
||||
this.login = gameClient.getLogin();
|
||||
this.password = gameClient.getPassword();
|
||||
this.email = gameClient.getEmail();
|
||||
this.balance = gameClient.getBalance();
|
||||
this.role = gameClient.getRole();
|
||||
if(gameClient.getTanks().size() >= 1){
|
||||
this.tankId = gameClient.getTanks().get(gameClient.getTanks().size() - 1).getId();
|
||||
}
|
||||
else {
|
||||
this.tankId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLogin(){
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getPassword(){
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail(){
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Integer getBalance(){
|
||||
return balance;
|
||||
}
|
||||
|
||||
public void setBalance(Integer balance) {
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
public Long getTankId(){
|
||||
return tankId;
|
||||
}
|
||||
|
||||
public void setTankId(Long tankId) {
|
||||
this.tankId = tankId;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(UserRole role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import premium_store.model.Tank;
|
||||
|
||||
public class SupportTankDTO {
|
||||
private long id;
|
||||
private String name;
|
||||
private Long nationId;
|
||||
private Long levelId;
|
||||
private int cost;
|
||||
|
||||
public SupportTankDTO(){}
|
||||
|
||||
public SupportTankDTO(Tank tank){
|
||||
this.id = tank.getId();
|
||||
this.nationId = tank.getNation().getId();
|
||||
this.levelId = tank.getLevel().getId();
|
||||
this.name = tank.getName();
|
||||
this.cost = tank.getCost();
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getNationId(){
|
||||
return nationId;
|
||||
}
|
||||
|
||||
public void setNationId(Long nationId) {
|
||||
this.nationId = nationId;
|
||||
}
|
||||
|
||||
public Long getLevelId(){
|
||||
return levelId;
|
||||
}
|
||||
|
||||
public void setLevelId(Long levelId) {
|
||||
this.levelId = levelId;
|
||||
}
|
||||
|
||||
public int getCost(){
|
||||
return cost;
|
||||
}
|
||||
|
||||
public void setCost(int cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import premium_store.model.Tank;
|
||||
|
||||
public class TankDTO {
|
||||
private long id;
|
||||
private String name;
|
||||
private SimpleNationDTO nation;
|
||||
private LevelDTO level;
|
||||
private int cost;
|
||||
|
||||
public TankDTO(){}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public SimpleNationDTO getNation(){
|
||||
return nation;
|
||||
}
|
||||
|
||||
public void setNation(SimpleNationDTO nation) {
|
||||
this.nation = nation;
|
||||
}
|
||||
|
||||
public LevelDTO getLevel(){
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(LevelDTO level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int getCost(){
|
||||
return cost;
|
||||
}
|
||||
|
||||
public void setCost(int cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package premium_store.controller.DTO;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class UserSignupDto {
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 64)
|
||||
private String login;
|
||||
|
||||
@NotBlank
|
||||
@Size(min = 6, max = 64)
|
||||
private String password;
|
||||
|
||||
@NotBlank
|
||||
private String email;
|
||||
|
||||
@NotBlank
|
||||
private String balance;
|
||||
|
||||
@NotBlank
|
||||
@Size(min = 6, max = 64)
|
||||
private String passwordConfirm;
|
||||
|
||||
public void setLogin(String login){
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public void setPassword(String password){
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setPasswordConfirm(String passwordConfirm){
|
||||
this.passwordConfirm = passwordConfirm;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public void setBalance(String balance) {
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
public String getLogin(){
|
||||
return login;
|
||||
}
|
||||
|
||||
public String getPassword(){
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm(){
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String getBalance() {
|
||||
return balance;
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import premium_store.configuration.OpenAPI30Configuration;
|
||||
import premium_store.controller.DTO.ClientDTO;
|
||||
import premium_store.controller.DTO.UserSignupDto;
|
||||
import premium_store.model.GameClient;
|
||||
import premium_store.model.UserRole;
|
||||
import premium_store.service.GameClientService;
|
||||
import premium_store.service.TankService;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.ValidationException;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@RestController
|
||||
public class GameClientController {
|
||||
public static final String URL_LOGIN = "/jwt/login";
|
||||
public static final String URL_SING_UP = "/sing_up";
|
||||
public static final String URL_WHO_AM_I = "/who_am_i";
|
||||
|
||||
private final GameClientService gameClientService;
|
||||
private final TankService tankService;
|
||||
|
||||
public GameClientController(GameClientService gameClientService, TankService tankService){
|
||||
this.gameClientService = gameClientService;
|
||||
this.tankService = tankService;
|
||||
}
|
||||
|
||||
@PostMapping(URL_LOGIN)
|
||||
public String login(@RequestBody @Valid ClientDTO userDto) {
|
||||
return gameClientService.loginAndGetToken(userDto);
|
||||
}
|
||||
|
||||
@PostMapping(URL_SING_UP)
|
||||
public String singUp(@RequestBody @Valid UserSignupDto userSignupDto) {
|
||||
try {
|
||||
final GameClient user = gameClientService.addClient(userSignupDto.getLogin(), userSignupDto.getEmail(), userSignupDto.getPassword(),
|
||||
Integer.parseInt(userSignupDto.getBalance()), userSignupDto.getPasswordConfirm(), UserRole.USER);
|
||||
|
||||
return "created " + user.getLogin();
|
||||
} catch (ValidationException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
//аннотация PathVariable связывает значения id из URL и Long id
|
||||
@GetMapping(OpenAPI30Configuration.API_PREFIX + "/")
|
||||
public ClientDTO getClient(@PathVariable Long id) {
|
||||
return new ClientDTO(gameClientService.findClient(id));
|
||||
}
|
||||
|
||||
@PutMapping(OpenAPI30Configuration.API_PREFIX + "/{id}")
|
||||
@Secured({UserRole.AsString.USER})
|
||||
public ClientDTO updateClient(@PathVariable Long id,
|
||||
@RequestParam("login") String login,
|
||||
@RequestParam("password") String password,
|
||||
@RequestParam("email") String email,
|
||||
@RequestParam("balance") Integer balance,
|
||||
@RequestParam("tankId") Long tankId) {
|
||||
return new ClientDTO(gameClientService.updateClient(id, login, password, email, balance, tankService.findTank(tankId)));
|
||||
}
|
||||
|
||||
@PostMapping(OpenAPI30Configuration.API_PREFIX + "/{id}")
|
||||
@Secured({UserRole.AsString.USER})
|
||||
public ClientDTO deleteClient(@PathVariable Long id) {
|
||||
return new ClientDTO(gameClientService.deleteClient(id));
|
||||
}
|
||||
|
||||
@GetMapping(OpenAPI30Configuration.API_PREFIX + "/users")
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public Pair<Page<ClientDTO>, List<Integer>> getClients(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "5") int size) {
|
||||
final Page<ClientDTO> users = gameClientService.findAllPages(page, size)
|
||||
.map(ClientDTO::new);
|
||||
|
||||
final int totalPages = users.getTotalPages();
|
||||
|
||||
final List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
|
||||
.boxed()
|
||||
.toList();
|
||||
|
||||
return new Pair<>(users, pageNumbers);
|
||||
}
|
||||
|
||||
@GetMapping(URL_WHO_AM_I)
|
||||
public String whoAmI(@RequestParam("token") String token) {
|
||||
UserDetails userDetails = gameClientService.loadUserByToken(token);
|
||||
GameClient user = gameClientService.findByLogin(userDetails.getUsername());
|
||||
return user.getRole().toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.controller.DTO.*;
|
||||
import premium_store.model.UserRole;
|
||||
import premium_store.service.GameClientService;
|
||||
import premium_store.service.TankService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/clients")
|
||||
public class GameClientMvcController {
|
||||
private final GameClientService gameClientService;
|
||||
private final TankService tankService;
|
||||
|
||||
public GameClientMvcController(GameClientService gameClientService, TankService tankService){
|
||||
this.gameClientService = gameClientService;
|
||||
this.tankService = tankService;
|
||||
}
|
||||
|
||||
@GetMapping({"/showUpdate"})
|
||||
public String showUpdateUserForm(Principal principal, Model model) {
|
||||
ClientDTO clientDTO = new ClientDTO(gameClientService.findByLogin(principal.getName()));
|
||||
model.addAttribute("clientDTO", clientDTO);
|
||||
return "client-edit";
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public String getClients(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "5") int size,
|
||||
Principal principal, Model model) {
|
||||
final Page<ClientDTO> users = gameClientService.findAllPages(page, size)
|
||||
.map(ClientDTO::new);
|
||||
|
||||
model.addAttribute("users", users);
|
||||
|
||||
final int totalPages = users.getTotalPages();
|
||||
|
||||
final List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
|
||||
.boxed()
|
||||
.toList();
|
||||
|
||||
model.addAttribute("pages", pageNumbers);
|
||||
model.addAttribute("totalPages", totalPages);
|
||||
|
||||
return "clients";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/edit", "/edit/{id}"})
|
||||
public String editClient(@PathVariable(required = false) Long id, Model model) {
|
||||
if(id == null || id <= 0){
|
||||
model.addAttribute("supportClientDTO", new SupportClientDTO());
|
||||
}
|
||||
else {
|
||||
model.addAttribute("clientId", id);
|
||||
model.addAttribute("supportClientDTO", new SupportClientDTO(gameClientService.findClient(id)));
|
||||
}
|
||||
|
||||
List<TankDTO> tanks = tankService.findAllTanks().stream()
|
||||
.map(TankDTO::new)
|
||||
.toList();
|
||||
|
||||
model.addAttribute("tanks", tanks);
|
||||
|
||||
/*if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "client-edit";
|
||||
}
|
||||
|
||||
try {
|
||||
gameClientService.updateClient(userDto);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<SimpleGrantedAuthority> nowAuthorities =
|
||||
(Collection<SimpleGrantedAuthority>) SecurityContextHolder.getContext()
|
||||
.getAuthentication()
|
||||
.getAuthorities();
|
||||
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(userDto.getLogin(), nowAuthorities);
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
} catch (ValidationException e) {
|
||||
model.addAttribute("errors", e.getMessage());
|
||||
}*/
|
||||
|
||||
return "client-edit";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/tanksOfClient", "/tanksOfClient/{id}"})
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public String editTanksOfClient(@PathVariable(required = false) Long id, Model model){
|
||||
if(id == null || id <= 0){
|
||||
model.addAttribute("clientDTO", new ClientDTO());
|
||||
}
|
||||
else {
|
||||
model.addAttribute("clientId", id);
|
||||
model.addAttribute("clientDTO", new ClientDTO(gameClientService.findClient(id)));
|
||||
}
|
||||
|
||||
return "tanks-of-client-edit";
|
||||
}
|
||||
|
||||
@PostMapping(value = {"", "/{id}"})
|
||||
public String saveClient(@PathVariable(required = false) Long id,
|
||||
@ModelAttribute @Valid SupportClientDTO clientDTO,
|
||||
BindingResult bindingResult,
|
||||
Model model){
|
||||
if(bindingResult.hasErrors()){
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "client-edit";
|
||||
}
|
||||
|
||||
//тут два раза прокидываем пароль, так как метод требует на вход строку с подтверждением пароля
|
||||
if(id == null || id <= 0){
|
||||
gameClientService.addClient(clientDTO.getLogin(), clientDTO.getPassword(), clientDTO.getEmail(), clientDTO.getBalance(), clientDTO.getPassword(), clientDTO.getRole());
|
||||
} else {
|
||||
gameClientService.updateClient(clientDTO);
|
||||
}
|
||||
|
||||
return "redirect:/clients";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public String deleteClient(Principal principal, Model model,
|
||||
@PathVariable Long id){
|
||||
gameClientService.deleteClient(id);
|
||||
return "redirect:/clients";
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.controller.DTO.LevelDTO;
|
||||
import premium_store.service.TankLevelService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/level")
|
||||
public class LevelMvcController {
|
||||
private final TankLevelService levelService;
|
||||
|
||||
public LevelMvcController(TankLevelService levelService){
|
||||
this.levelService = levelService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getLevels(Model model){
|
||||
model.addAttribute("levels",
|
||||
levelService.findAllLevels().stream()
|
||||
.map(LevelDTO::new)
|
||||
.toList());
|
||||
return "level";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/edit", "/edit/{id}"})
|
||||
public String editLevel(@PathVariable(required = false) Long id, Model model){
|
||||
if(id == null || id <= 0){
|
||||
model.addAttribute("levelDTO", new LevelDTO());
|
||||
}
|
||||
else {
|
||||
model.addAttribute("levelId", id);
|
||||
model.addAttribute("levelDTO", new LevelDTO(levelService.findLevel(id)));
|
||||
}
|
||||
|
||||
return "level-edit";
|
||||
}
|
||||
|
||||
@PostMapping(value = {"", "/{id}"})
|
||||
public String saveLevel(@PathVariable(required = false) Long id,
|
||||
@ModelAttribute @Valid LevelDTO levelDTO,
|
||||
BindingResult bindingResult,
|
||||
Model model){
|
||||
if(bindingResult.hasErrors()){
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "level-edit";
|
||||
}
|
||||
|
||||
if(id == null || id <= 0){
|
||||
levelService.addLevel(levelDTO.getLevel());
|
||||
} else {
|
||||
levelService.updateLevel(id, levelDTO.getLevel());
|
||||
}
|
||||
|
||||
return "redirect:/level";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteLevel(@PathVariable Long id){
|
||||
levelService.deleteLevel(id);
|
||||
return "redirect:/level";
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.configuration.OpenAPI30Configuration;
|
||||
import premium_store.configuration.WebConfiguration;
|
||||
import premium_store.controller.DTO.FullNationDTO;
|
||||
import premium_store.model.UserRole;
|
||||
import premium_store.service.NationService;
|
||||
import premium_store.service.TankService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//привязываем наш контроллер к придуманному корневому URL благодаря аннотациям
|
||||
//здесь происходит внедрение зависимости нашего сервиса
|
||||
//так же здесь прописываем вызовы методов CRUD в привязке к URL
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/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("/getNations")
|
||||
public List<FullNationDTO> getNations() {
|
||||
return nationService.findAllNations().stream()
|
||||
.map(FullNationDTO::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
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,82 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import net.bytebuddy.implementation.bind.MethodDelegationBinder;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.controller.DTO.FullNationDTO;
|
||||
import premium_store.controller.DTO.SimpleNationDTO;
|
||||
import premium_store.service.NationService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/nation")
|
||||
public class NationMvcController {
|
||||
private final NationService nationService;
|
||||
|
||||
public NationMvcController(NationService nationService){
|
||||
this.nationService = nationService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getNations(Model model){
|
||||
model.addAttribute("nations",
|
||||
nationService.findAllNations().stream()
|
||||
.map(FullNationDTO::new)
|
||||
.toList());
|
||||
return "nation";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/edit", "/edit/{id}"})
|
||||
public String editNation(@PathVariable(required = false) Long id, Model model){
|
||||
if(id == null || id <= 0){
|
||||
model.addAttribute("simpleNationDTO", new SimpleNationDTO());
|
||||
}
|
||||
else {
|
||||
model.addAttribute("nationId", id);
|
||||
model.addAttribute("simpleNationDTO", new SimpleNationDTO(nationService.findNation(id)));
|
||||
}
|
||||
|
||||
return "nation-edit";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/editTank", "/editTank/{id}"})
|
||||
public String editTankNation(@PathVariable(required = false) Long id, Model model){
|
||||
if(id == null || id <= 0){
|
||||
model.addAttribute("fullNationDTO", new FullNationDTO());
|
||||
}
|
||||
else {
|
||||
model.addAttribute("nationId", id);
|
||||
model.addAttribute("fullNationDTO", new FullNationDTO(nationService.findNation(id)));
|
||||
}
|
||||
|
||||
return "nation-tank-edit";
|
||||
}
|
||||
|
||||
@PostMapping(value = {"", "/{id}"})
|
||||
public String saveNation(@PathVariable(required = false) Long id,
|
||||
@ModelAttribute @Valid SimpleNationDTO simpleNationDTO,
|
||||
BindingResult bindingResult,
|
||||
Model model){
|
||||
if(bindingResult.hasErrors()){
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "nation-edit";
|
||||
}
|
||||
|
||||
if(id == null || id <= 0){
|
||||
nationService.addNation(simpleNationDTO.getNation());
|
||||
} else {
|
||||
nationService.updateNation(id, simpleNationDTO.getNation(), null);
|
||||
}
|
||||
|
||||
return "redirect:/nation";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteNation(@PathVariable Long id){
|
||||
nationService.deleteNation(id);
|
||||
return "redirect:/nation";
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
public class Pair<F, S> {
|
||||
private final F first;
|
||||
private final S second;
|
||||
|
||||
public Pair(F first, S second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public F getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public S getSecond() {
|
||||
return second;
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.configuration.OpenAPI30Configuration;
|
||||
import premium_store.configuration.WebConfiguration;
|
||||
import premium_store.controller.DTO.TankDTO;
|
||||
import premium_store.model.UserRole;
|
||||
import premium_store.service.NationService;
|
||||
import premium_store.service.TankLevelService;
|
||||
import premium_store.service.TankService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/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("/create")
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
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,52 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.configuration.OpenAPI30Configuration;
|
||||
import premium_store.configuration.WebConfiguration;
|
||||
import premium_store.controller.DTO.LevelDTO;
|
||||
import premium_store.service.TankLevelService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//привязываем наш контроллер к придуманному корневому URL благодаря аннотациям
|
||||
//здесь происходит внедрение зависимости нашего сервиса
|
||||
//так же здесь прописываем вызовы методов CRUD в привязке к URL
|
||||
@RestController
|
||||
@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/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("/getLevels")
|
||||
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,113 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import premium_store.controller.DTO.*;
|
||||
import premium_store.service.NationService;
|
||||
import premium_store.service.TankLevelService;
|
||||
import premium_store.service.TankService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/tank")
|
||||
public class TankMvcController {
|
||||
private final TankService tankService;
|
||||
private final NationService nationService;
|
||||
private final TankLevelService tankLevelService;
|
||||
|
||||
public TankMvcController(TankService tankService, NationService nationService, TankLevelService tankLevelService){
|
||||
this.tankService = tankService;
|
||||
this.nationService = nationService;
|
||||
this.tankLevelService = tankLevelService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getTanks(Model model){
|
||||
model.addAttribute("tanks",
|
||||
tankService.findAllTanks().stream()
|
||||
.map(TankDTO::new)
|
||||
.toList());
|
||||
|
||||
List<SimpleNationDTO> nations = nationService.findAllNations().stream()
|
||||
.map(SimpleNationDTO::new)
|
||||
.toList();
|
||||
|
||||
model.addAttribute("nations", nations);
|
||||
|
||||
List<LevelDTO> levels = tankLevelService.findAllLevels().stream()
|
||||
.map(LevelDTO::new)
|
||||
.toList();
|
||||
|
||||
model.addAttribute("levels", levels);
|
||||
|
||||
model.addAttribute("sortDTO", new SortDTO());
|
||||
|
||||
return "tank";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/edit", "/edit/{id}"})
|
||||
public String editTank(@PathVariable(required = false) Long id, Model model){
|
||||
if(id == null || id <= 0){
|
||||
model.addAttribute("supportTankDTO", new SupportTankDTO());
|
||||
}
|
||||
else {
|
||||
model.addAttribute("tankId", id);
|
||||
model.addAttribute("supportTankDTO", new SupportTankDTO(tankService.findTank(id)));
|
||||
}
|
||||
|
||||
List<SimpleNationDTO> nations = nationService.findAllNations().stream()
|
||||
.map(SimpleNationDTO::new)
|
||||
.toList();
|
||||
|
||||
model.addAttribute("nations", nations);
|
||||
|
||||
List<LevelDTO> levels = tankLevelService.findAllLevels().stream()
|
||||
.map(LevelDTO::new)
|
||||
.toList();
|
||||
|
||||
model.addAttribute("levels", levels);
|
||||
|
||||
return "tank-edit";
|
||||
}
|
||||
|
||||
@GetMapping("/filteredList")
|
||||
public String getFilteredTanks(@ModelAttribute SortDTO sortDTO,
|
||||
Model model) {
|
||||
List<TankDTO> tanks = tankService.findListTank(sortDTO.getNation(), sortDTO.getFirstLevel(), sortDTO.getSecondLevel()).stream()
|
||||
.map(TankDTO::new)
|
||||
.toList();
|
||||
|
||||
model.addAttribute("tanks", tanks);
|
||||
|
||||
return "filter";
|
||||
}
|
||||
|
||||
@PostMapping(value = {"", "/{id}"})
|
||||
public String saveTank(@PathVariable(required = false) Long id,
|
||||
@ModelAttribute @Valid SupportTankDTO tankDTO,
|
||||
BindingResult bindingResult,
|
||||
Model model){
|
||||
if(bindingResult.hasErrors()){
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "tank-edit";
|
||||
}
|
||||
|
||||
if(id == null || id <= 0){
|
||||
tankService.addTank(tankDTO);
|
||||
} else {
|
||||
tankService.updateTank(tankDTO);
|
||||
}
|
||||
|
||||
return "redirect:/tank";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteTank(@PathVariable Long id){
|
||||
tankService.deleteTank(id);
|
||||
return "redirect:/tank";
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package premium_store.controller.controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import premium_store.controller.DTO.UserSignupDto;
|
||||
import premium_store.model.GameClient;
|
||||
import premium_store.model.UserRole;
|
||||
import premium_store.service.GameClientService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.ValidationException;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(UserSignupMvcController.SIGNUP_URL)
|
||||
public class UserSignupMvcController {
|
||||
public static final String SIGNUP_URL = "/signup";
|
||||
|
||||
private final GameClientService clientService;
|
||||
|
||||
public UserSignupMvcController(GameClientService clientService) {
|
||||
this.clientService = clientService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String showSignupForm(Model model) {
|
||||
model.addAttribute("clientDTO", new UserSignupDto());
|
||||
|
||||
return "signup";
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String signup(@ModelAttribute("clientDTO") @Valid UserSignupDto userSignupDto,
|
||||
BindingResult bindingResult, Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "signup";
|
||||
}
|
||||
try {
|
||||
final GameClient client = clientService.addClient(userSignupDto.getLogin(), userSignupDto.getEmail(), userSignupDto.getPassword(), Integer.parseInt(userSignupDto.getBalance()), userSignupDto.getPasswordConfirm(), UserRole.USER);
|
||||
return "redirect:/login?created=" + client.getLogin();
|
||||
} catch (ValidationException e) {
|
||||
model.addAttribute("errors", e.getMessage());
|
||||
return "signup";
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user