diff --git a/api/app/domain/entities/user.py b/api/app/domain/entities/user.py
index 8ab8c37..ba2347b 100644
--- a/api/app/domain/entities/user.py
+++ b/api/app/domain/entities/user.py
@@ -8,7 +8,7 @@ class UserEntity(BaseModel):
first_name: str = Field(..., example='Ivan')
last_name: str = Field(..., example='Ivanov')
patronymic: Optional[str] = Field(None, example='Ivanov')
- login: str = Field(..., example='user@example.com')
+ login: str = Field(..., example='ivanov74')
role_id: int = Field(..., example=1)
diff --git a/web-app/package-lock.json b/web-app/package-lock.json
index 4b92f5e..0680458 100644
--- a/web-app/package-lock.json
+++ b/web-app/package-lock.json
@@ -12,6 +12,7 @@
"@react-buddy/palette-antd": "^5.3.0",
"antd": "^5.23.1",
"axios": "^1.7.9",
+ "prop-types": "^15.8.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.1.1"
@@ -3617,7 +3618,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -3888,7 +3888,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
@@ -4552,7 +4551,6 @@
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/react-router": {
diff --git a/web-app/package.json b/web-app/package.json
index e811463..c1149c1 100644
--- a/web-app/package.json
+++ b/web-app/package.json
@@ -10,13 +10,14 @@
"preview": "vite preview"
},
"dependencies": {
+ "@react-buddy/ide-toolbox": "^2.4.0",
+ "@react-buddy/palette-antd": "^5.3.0",
"antd": "^5.23.1",
"axios": "^1.7.9",
+ "prop-types": "^15.8.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "react-router-dom": "^7.1.1",
- "@react-buddy/ide-toolbox": "^2.4.0",
- "@react-buddy/palette-antd": "^5.3.0"
+ "react-router-dom": "^7.1.1"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
diff --git a/web-app/src/App.jsx b/web-app/src/App.jsx
index f240c23..506895e 100644
--- a/web-app/src/App.jsx
+++ b/web-app/src/App.jsx
@@ -1,10 +1,13 @@
-function App() {
+import {BrowserRouter as Router} from "react-router-dom";
+import AppRouter from "./AppRouter.jsx";
+import {AuthProvider} from "./AuthContext.jsx";
- return (
- <>
-
1234
- >
- )
-}
+const App = () => (
+
+
+
+
+
+);
export default App
diff --git a/web-app/src/AppRouter.jsx b/web-app/src/AppRouter.jsx
new file mode 100644
index 0000000..7b6487d
--- /dev/null
+++ b/web-app/src/AppRouter.jsx
@@ -0,0 +1,15 @@
+import {Routes, Route} from "react-router-dom";
+import PrivateRoute from "./components/PrivateRoute.jsx";
+
+
+const AppRouter = () => (
+
+
+
+ }>
+
+
+
+)
+
+export default AppRouter;
\ No newline at end of file
diff --git a/web-app/src/AuthContext.jsx b/web-app/src/AuthContext.jsx
new file mode 100644
index 0000000..c7ea3c7
--- /dev/null
+++ b/web-app/src/AuthContext.jsx
@@ -0,0 +1,45 @@
+import {createContext, useState, useContext, useEffect} from "react";
+import PropTypes from "prop-types";
+import {loginUser} from "./api/LoginRequest.jsx";
+
+const AuthContext = createContext(undefined);
+
+export const AuthProvider = ({children}) => {
+ const [user, setUser] = useState(null);
+
+ useEffect(() => {
+ const token = localStorage.getItem("access_token");
+ if (token) {
+ setUser({token});
+ }
+ }, []);
+
+ const login = async (loginData) => {
+ try {
+ const token = await loginUser(loginData);
+ localStorage.setItem("access_token", token);
+ setUser({ token });
+ } catch (error) {
+ console.error("Login failed", error);
+ }
+ };
+
+ const logout = () => {
+ localStorage.removeItem("access_token");
+ setUser(null);
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+AuthProvider.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
+export const useAuth = () => {
+ return useContext(AuthContext);
+};
diff --git a/web-app/src/api/LoginRequest.jsx b/web-app/src/api/LoginRequest.jsx
new file mode 100644
index 0000000..15a6bd5
--- /dev/null
+++ b/web-app/src/api/LoginRequest.jsx
@@ -0,0 +1,13 @@
+import axios from "axios";
+import CONFIG from "../core/Config.jsx";
+
+export const loginUser = async (loginData) => {
+ try {
+ const response = await axios.post(`${CONFIG.BASE_URL}/login/`, loginData, {
+ withCredentials: true,
+ });
+ return response.data.access_token;
+ } catch (error) {
+ throw new Error("Login failed: " + error.message);
+ }
+};
diff --git a/web-app/src/components/PrivateRoute.jsx b/web-app/src/components/PrivateRoute.jsx
new file mode 100644
index 0000000..e9553ad
--- /dev/null
+++ b/web-app/src/components/PrivateRoute.jsx
@@ -0,0 +1,14 @@
+import {Navigate, Outlet} from "react-router-dom";
+import {useAuth} from "../AuthContext.jsx";
+
+const PrivateRoute = () => {
+ const {user} = useAuth();
+
+ if (!user) {
+ return ;
+ }
+
+ return ;
+};
+
+export default PrivateRoute;
diff --git a/web-app/src/core/Config.jsx b/web-app/src/core/Config.jsx
new file mode 100644
index 0000000..78ec13c
--- /dev/null
+++ b/web-app/src/core/Config.jsx
@@ -0,0 +1,5 @@
+const CONFIG = {
+ BASE_URL: 'http://localhost:8080/api/v1/',
+};
+
+export default CONFIG;
\ No newline at end of file