refactor: Перенос логики UI в useMainLayout

Удален useMainLayoutUI и перенесена логика в useMainLayout.
This commit is contained in:
Андрей Дувакин 2025-06-28 18:21:53 +05:00
parent ceee769100
commit c3d77738a7
7 changed files with 86 additions and 77 deletions

29
.gitignore vendored
View File

@ -1 +1,30 @@
/api/.env /api/.env
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.env
*/.dist/*
*/.idea/*
*/uploads/*

View File

@ -1,4 +1,4 @@
import {Alert, Layout, Menu, Result} from "antd"; import {Layout, Menu, Result} from "antd";
import {Outlet} from "react-router-dom"; import {Outlet} from "react-router-dom";
import { import {
HomeOutlined, HomeOutlined,
@ -11,33 +11,31 @@ import {
MessageOutlined, ControlOutlined MessageOutlined, ControlOutlined
} from "@ant-design/icons"; } from "@ant-design/icons";
import useMainLayout from "./useMainLayout.js"; import useMainLayout from "./useMainLayout.js";
import useMainLayoutUI from "./useMainLayoutUI.js";
import LoadingIndicator from "../Widgets/LoadingIndicator/LoadingIndicator.jsx"; import LoadingIndicator from "../Widgets/LoadingIndicator/LoadingIndicator.jsx";
const {Content, Footer, Sider} = Layout; const {Content, Footer, Sider} = Layout;
const MainLayout = () => { const MainLayout = () => {
const mainLayoutData = useMainLayout(); const mainLayoutData = useMainLayout();
const mainLayoutUI = useMainLayoutUI(mainLayoutData.user);
const menuItems = [ const menuItems = [
mainLayoutUI.getItem("Главная", "/", <HomeOutlined/>), mainLayoutData.getItem("Главная", "/", <HomeOutlined/>),
mainLayoutUI.getItem("Приёмы", "/appointments", <CalendarOutlined/>), mainLayoutData.getItem("Приёмы", "/appointments", <CalendarOutlined/>),
mainLayoutUI.getItem("Выдачи линз", "/issues", <DatabaseOutlined/>), mainLayoutData.getItem("Выдачи линз", "/issues", <DatabaseOutlined/>),
mainLayoutUI.getItem("Линзы и наборы", "/Lenses", <FolderViewOutlined/>), mainLayoutData.getItem("Линзы и наборы", "/Lenses", <FolderViewOutlined/>),
mainLayoutUI.getItem("Пациенты", "/Patients", <TeamOutlined/>), mainLayoutData.getItem("Пациенты", "/Patients", <TeamOutlined/>),
mainLayoutUI.getItem("Рассылки", "/mailing", <MessageOutlined/>), mainLayoutData.getItem("Рассылки", "/mailing", <MessageOutlined/>),
{type: "divider"} {type: "divider"}
]; ];
if (mainLayoutData.user?.role.title === "Администратор") { if (mainLayoutData.user?.role.title === "Администратор") {
menuItems.push(mainLayoutUI.getItem("Панель администратора", "/admin", <ControlOutlined/>)); menuItems.push(mainLayoutData.getItem("Панель администратора", "/admin", <ControlOutlined/>));
} }
menuItems.push( menuItems.push(
mainLayoutUI.getItem("Мой профиль", "profile", <UserOutlined/>, [ mainLayoutData.getItem("Мой профиль", "profile", <UserOutlined/>, [
mainLayoutUI.getItem("Перейти в профиль", "/profile", <UserOutlined/>), mainLayoutData.getItem("Перейти в профиль", "/profile", <UserOutlined/>),
mainLayoutUI.getItem("Выйти", "logout", <LogoutOutlined/>) mainLayoutData.getItem("Выйти", "logout", <LogoutOutlined/>)
]) ])
); );
@ -48,16 +46,16 @@ const MainLayout = () => {
return ( return (
<Layout style={{minHeight: "100vh"}}> <Layout style={{minHeight: "100vh"}}>
<Sider <Sider
collapsible={!mainLayoutUI.screens.xs} collapsible={!mainLayoutData.screens.xs}
collapsed={mainLayoutUI.collapsed} collapsed={mainLayoutData.collapsed}
onCollapse={mainLayoutUI.setCollapsed} onCollapse={mainLayoutData.setCollapsed}
style={{height: "100vh", position: "fixed", left: 0, overflow: "auto"}} style={{height: "100vh", position: "fixed", left: 0, overflow: "auto"}}
> >
<div style={{display: "flex", justifyContent: "center", padding: 16}}> <div style={{display: "flex", justifyContent: "center", padding: 16}}>
<img <img
src="/logo_rounded.png" src="/logo_rounded.png"
alt="Логотип" alt="Логотип"
style={{width: mainLayoutUI.collapsed ? 40 : 80, transition: "width 0.2s"}} style={{width: mainLayoutData.collapsed ? 40 : 80, transition: "width 0.2s"}}
/> />
</div> </div>
<Menu <Menu
@ -65,12 +63,12 @@ const MainLayout = () => {
selectedKeys={[location.pathname]} selectedKeys={[location.pathname]}
mode="inline" mode="inline"
items={menuItems} items={menuItems}
onClick={mainLayoutUI.handleMenuClick} onClick={mainLayoutData.handleMenuClick}
/> />
</Sider> </Sider>
<Layout <Layout
style={{marginLeft: mainLayoutUI.collapsed ? 80 : 200, transition: "margin-left 0.2s"}} style={{marginLeft: mainLayoutData.collapsed ? 80 : 200, transition: "margin-left 0.2s"}}
> >
<Content style={{ <Content style={{
margin: "0 16px", margin: "0 16px",

View File

@ -1,13 +1,33 @@
import { useSelector } from "react-redux"; import {useState} from "react";
import {Grid} from "antd";
import {useLocation, useNavigate} from "react-router-dom";
import useAuthUtils from "../../Hooks/useAuthUtils.js";
import {useSelector} from "react-redux";
const {useBreakpoint} = Grid;
const useMainLayout = () => { const useMainLayout = () => {
const screens = useBreakpoint();
const [collapsed, setCollapsed] = useState(true);
const navigate = useNavigate();
const location = useLocation();
const {logoutAndRedirect} = useAuthUtils();
const { userData: user, isLoading: isUserLoading, error: isUserError } = useSelector((state) => state.auth); const { userData: user, isLoading: isUserLoading, error: isUserError } = useSelector((state) => state.auth);
return { const handleMenuClick = ({key}) => {
user, if (key === "logout") {
isUserLoading, logoutAndRedirect();
isUserError, return;
}
navigate(key);
}; };
const getItem = (label, key, icon, children) => ({key, icon, children, label});
return {screens, collapsed, setCollapsed, location, user, isUserLoading, isUserError, handleMenuClick, getItem};
}; };
export default useMainLayout; export default useMainLayout;

View File

@ -1,30 +0,0 @@
import {useState} from "react";
import {Grid} from "antd";
import {useLocation, useNavigate} from "react-router-dom";
import useAuthUtils from "../../Hooks/useAuthUtils.js";
const {useBreakpoint} = Grid;
const useMainLayoutUI = () => {
const screens = useBreakpoint();
const [collapsed, setCollapsed] = useState(true);
const navigate = useNavigate();
const location = useLocation();
const {logoutAndRedirect} = useAuthUtils();
const handleMenuClick = ({key}) => {
if (key === "logout") {
logoutAndRedirect();
return;
}
navigate(key);
};
const getItem = (label, key, icon, children) => ({key, icon, children, label});
return {screens, collapsed, setCollapsed, location, handleMenuClick, getItem};
};
export default useMainLayoutUI;

View File

@ -1,5 +1,5 @@
import {Table, Button, Result, Typography, Switch} from "antd"; import {Table, Button, Result, Typography, Switch, Row, Input, Col, Tooltip, FloatButton} from "antd";
import {ControlOutlined} from "@ant-design/icons"; import {ControlOutlined, PlusOutlined} from "@ant-design/icons";
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
import useAdminPage from "./useAdminPage.js"; import useAdminPage from "./useAdminPage.js";
import useAdminPageUI from "./useAdminPageUI.js"; import useAdminPageUI from "./useAdminPageUI.js";
@ -45,11 +45,11 @@ const AdminPage = () => {
title: "Заблокирован", title: "Заблокирован",
dataIndex: ["is_blocked"], dataIndex: ["is_blocked"],
key: "is_blocked", key: "is_blocked",
render: (value, record) => ( render: (value) => (
<Switch <Switch
value={value} value={value}
checkedChildren={"Заблокирован"} checkedChildren={"Заблокирован"}
unCheckedChildren={"Разблокирован"} unCheckedChildren={"Не заблокирован"}
/> />
) )
}, },
@ -64,9 +64,8 @@ const AdminPage = () => {
}, },
]; ];
if (adminPageData.isError) { if (adminPageData.isError) {
return <Result status="500" title="500" subTitle="Произошла ошибка при загрузке данных"/> return <Result status="500" title="500" subTitle="Произошла ошибка при загрузке данных"/>;
} }
return ( return (
@ -78,13 +77,11 @@ const AdminPage = () => {
<Typography.Title level={1}> <Typography.Title level={1}>
<ControlOutlined/> Панель администратора <ControlOutlined/> Панель администратора
</Typography.Title> </Typography.Title>
<Button <Input
type="primary" placeholder="Введите фамилию, имя или отчество"
onClick={adminPageUI.openCreateModal} onSearch={adminPageUI.handleSearch}
style={{marginBottom: 16}} style={{marginBottom: 12}}
> />
Создать пользователя
</Button>
<Table <Table
columns={columns} columns={columns}
dataSource={adminPageData.users} dataSource={adminPageData.users}
@ -93,6 +90,10 @@ const AdminPage = () => {
loading={adminPageData.isLoading} loading={adminPageData.isLoading}
/> />
<Tooltip title="Добавить пользователя">
<FloatButton onClick={adminPageUI.openCreateModal} icon={<PlusOutlined/>} type={"primary"}/>
</Tooltip>
<CreateUserModalForm/> <CreateUserModalForm/>
<UpdateUserModalForm/> <UpdateUserModalForm/>
</> </>

View File

@ -12,9 +12,6 @@ const useAdminPage = () => {
pollingInterval: 60000, pollingInterval: 60000,
}); });
// const [updateUser, { isLoading: isUpdating, isError: isUpdateError }] = useUpdateUserMutation();
// const [createUser, { isLoading: isCreating, isError: isCreateError }] = useCreateUserMutation();
return { return {
users, users,
roles, roles,

View File

@ -9,10 +9,4 @@ const getCachedInfo = (key) => {
return JSON.parse(data); return JSON.parse(data);
}; };
const getCacheTimestamp = (key) => { export {cacheInfo, getCachedInfo};
const timestamp = localStorage.getItem(`${key}Timestamp`);
if (!timestamp) return null;
return parseInt(timestamp);
};
export {cacheInfo, getCachedInfo, getCacheTimestamp};