сделал заготовку под навигацию
This commit is contained in:
parent
8e85ea7b23
commit
df5809232b
@ -1,7 +1,7 @@
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class AuthEntity(BaseModel):
|
class AuthEntity(BaseModel):
|
||||||
login: str = Field(..., example='user@example.com')
|
login: str
|
||||||
password: str = Field(..., min_length=5, example='strongpassword')
|
password: str
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,9 @@ from pydantic import BaseModel, Field
|
|||||||
|
|
||||||
|
|
||||||
class RegisterEntity(BaseModel):
|
class RegisterEntity(BaseModel):
|
||||||
first_name: str = Field(..., example='Ivan')
|
first_name: str
|
||||||
last_name: str = Field(..., example='Ivanov')
|
last_name: str
|
||||||
patronymic: Optional[str] = Field(None, example='Ivanov')
|
patronymic: Optional[str]
|
||||||
role_id: int = Field(..., example=1)
|
role_id: int
|
||||||
login: str = Field(..., example='user@example.com')
|
login: str
|
||||||
password: str = Field(..., min_length=5, example='strongpassword')
|
password: str
|
||||||
|
|||||||
@ -4,11 +4,10 @@ from pydantic import BaseModel, Field
|
|||||||
|
|
||||||
|
|
||||||
class UserEntity(BaseModel):
|
class UserEntity(BaseModel):
|
||||||
id: int = Field(..., example=1)
|
id: int
|
||||||
first_name: str = Field(..., example='Ivan')
|
first_name: str
|
||||||
last_name: str = Field(..., example='Ivanov')
|
last_name: str
|
||||||
patronymic: Optional[str] = Field(None, example='Ivanov')
|
patronymic: Optional[str]
|
||||||
login: str = Field(..., example='ivanov74')
|
login: str
|
||||||
|
|
||||||
role_id: int = Field(..., example=1)
|
|
||||||
|
|
||||||
|
role_id: int
|
||||||
|
|||||||
7
web-app/package-lock.json
generated
7
web-app/package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "web-app",
|
"name": "web-app",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^5.6.1",
|
||||||
"@react-buddy/ide-toolbox": "^2.4.0",
|
"@react-buddy/ide-toolbox": "^2.4.0",
|
||||||
"@react-buddy/palette-antd": "^5.3.0",
|
"@react-buddy/palette-antd": "^5.3.0",
|
||||||
"antd": "^5.23.1",
|
"antd": "^5.23.1",
|
||||||
@ -86,9 +87,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ant-design/icons": {
|
"node_modules/@ant-design/icons": {
|
||||||
"version": "5.5.2",
|
"version": "5.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz",
|
||||||
"integrity": "sha512-xc53rjVBl9v2BqFxUjZGti/RfdDeA8/6KYglmInM2PNqSXc/WfuGDTifJI/ZsokJK0aeKvOIbXc9y2g8ILAhEA==",
|
"integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "^7.0.0",
|
"@ant-design/colors": "^7.0.0",
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^5.6.1",
|
||||||
"@react-buddy/ide-toolbox": "^2.4.0",
|
"@react-buddy/ide-toolbox": "^2.4.0",
|
||||||
"@react-buddy/palette-antd": "^5.3.0",
|
"@react-buddy/palette-antd": "^5.3.0",
|
||||||
"antd": "^5.23.1",
|
"antd": "^5.23.1",
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
|
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<browserconfig>
|
<browserconfig>
|
||||||
<msapplication>
|
<msapplication>
|
||||||
<tile>
|
<tile>
|
||||||
<square70x70logo src="/favicon-70x70.png"/>
|
<square70x70logo src="/favicon-70x70.png"/>
|
||||||
<square150x150logo src="/favicon-150x150.png"/>
|
<square150x150logo src="/favicon-150x150.png"/>
|
||||||
<square310x310logo src="/favicon-310x310.png"/>
|
<square310x310logo src="/favicon-310x310.png"/>
|
||||||
<TileColor>#ffffff</TileColor>
|
<TileColor>#ffffff</TileColor>
|
||||||
</tile>
|
</tile>
|
||||||
</msapplication>
|
</msapplication>
|
||||||
</browserconfig>
|
</browserconfig>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import {Routes, Route} from "react-router-dom";
|
import {Routes, Route} from "react-router-dom";
|
||||||
import PrivateRoute from "./components/PrivateRoute.jsx";
|
import PrivateRoute from "./components/PrivateRoute.jsx";
|
||||||
import LoginPage from "./pages/LoginPage.jsx";
|
import LoginPage from "./pages/LoginPage.jsx";
|
||||||
|
import MainLayout from "./layouts/MainLayout.jsx";
|
||||||
|
|
||||||
|
|
||||||
const AppRouter = () => (
|
const AppRouter = () => (
|
||||||
@ -8,7 +9,9 @@ const AppRouter = () => (
|
|||||||
<Route path="/login" element={<LoginPage/>}/>
|
<Route path="/login" element={<LoginPage/>}/>
|
||||||
|
|
||||||
<Route element={<PrivateRoute/>}>
|
<Route element={<PrivateRoute/>}>
|
||||||
<Route path={"/"} element={<p>1234</p>}/>
|
<Route element={<MainLayout/>}>
|
||||||
|
<Route path={"/"} element={<p>1234</p>}/>
|
||||||
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
)
|
||||||
|
|||||||
66
web-app/src/layouts/MainLayout.jsx
Normal file
66
web-app/src/layouts/MainLayout.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import {useState} from "react";
|
||||||
|
import {Layout, Menu} from "antd";
|
||||||
|
import {Outlet, useLocation, useNavigate} from "react-router-dom";
|
||||||
|
import {
|
||||||
|
DesktopOutlined,
|
||||||
|
FileOutlined,
|
||||||
|
PieChartOutlined,
|
||||||
|
TeamOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
|
const {Content, Footer, Sider} = Layout;
|
||||||
|
|
||||||
|
const getItem = (label, key, icon, children) => ({
|
||||||
|
key,
|
||||||
|
icon,
|
||||||
|
children,
|
||||||
|
label,
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
getItem("Главная", "/", <PieChartOutlined/>),
|
||||||
|
getItem("Настройки", "/settings", <DesktopOutlined/>),
|
||||||
|
getItem("Пользователи", "sub1", <UserOutlined/>, [
|
||||||
|
getItem("Том", "/users/tom"),
|
||||||
|
getItem("Билл", "/users/bill"),
|
||||||
|
getItem("Алекс", "/users/alex"),
|
||||||
|
]),
|
||||||
|
getItem("Команда", "sub2", <TeamOutlined/>, [
|
||||||
|
getItem("Команда 1", "/team/1"),
|
||||||
|
getItem("Команда 2", "/team/2"),
|
||||||
|
]),
|
||||||
|
getItem("Файлы", "/files", <FileOutlined/>),
|
||||||
|
];
|
||||||
|
|
||||||
|
const MainLayout = () => {
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout style={{minHeight: "100vh"}}>
|
||||||
|
<Sider collapsible collapsed={collapsed} onCollapse={setCollapsed}>
|
||||||
|
<Menu
|
||||||
|
theme="dark"
|
||||||
|
defaultSelectedKeys={[location.pathname]}
|
||||||
|
mode="inline"
|
||||||
|
items={items}
|
||||||
|
onClick={({key}) => navigate(key)}
|
||||||
|
/>
|
||||||
|
</Sider>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<Content style={{margin: "0 16px"}}>
|
||||||
|
<div style={{padding: 24, minHeight: 360, background: "#fff", borderRadius: 8}}>
|
||||||
|
<Outlet/>
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Footer style={{textAlign: "center"}}>Линза+ © {new Date().getFullYear()}</Footer>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MainLayout;
|
||||||
@ -1,20 +1,30 @@
|
|||||||
import {Form, Input, Button, Row, Col, Typography} from 'antd';
|
import {Form, Input, Button, Row, Col, Typography} from 'antd';
|
||||||
import {useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {useAuth} from "../AuthContext.jsx";
|
import {useAuth} from "../AuthContext.jsx";
|
||||||
|
import {useNavigate} from "react-router-dom";
|
||||||
|
|
||||||
const {Title} = Typography;
|
const {Title} = Typography;
|
||||||
|
|
||||||
const LoginPage = () => {
|
const LoginPage = () => {
|
||||||
const {login} = useAuth();
|
const {user, login} = useAuth();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user) {
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
}, [user, navigate]);
|
||||||
|
|
||||||
const onFinish = async (values) => {
|
const onFinish = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await login(values);
|
await login(values);
|
||||||
|
navigate("/");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(`Ошибка при входе: ${error.message}`);
|
setError(`Ошибка при входе: ${error.message}`);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user