feat: Добавлена админ-панель и API пользователей

Добавлены компоненты для админ-панели и API для управления пользователями.
This commit is contained in:
Андрей Дувакин 2025-06-02 21:48:28 +05:00
parent 49e4e2f3f1
commit b8b57e451b
10 changed files with 101 additions and 7 deletions

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Линза </title>
<title>Visus </title>
<link rel="apple-touch-icon" sizes="57x57" href="/favicons/favicon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/favicons/favicon-60x60.png">

View File

@ -7,6 +7,11 @@ export const usersApi = createApi({
baseQuery: baseQueryWithAuth,
tagTypes: ['User'],
endpoints: (builder) => ({
getAllUsers: builder.query({
query: () => '/users/',
providesTags: ['User'],
refetchOnMountOrArgChange: 5,
}),
getAuthenticatedUserData: builder.query({
query: () => '/users/my-data/',
providesTags: ['User'],
@ -34,4 +39,5 @@ export const {
useGetAuthenticatedUserDataQuery,
useChangePasswordMutation,
useUpdateUserMutation,
useGetAllUsersQuery,
} = usersApi;

View File

@ -1,10 +1,30 @@
import { Navigate, Outlet } from "react-router-dom";
import { useSelector } from "react-redux";
import {useGetAuthenticatedUserDataQuery} from "../Api/usersApi.js";
import LoadingIndicator from "../Components/Widgets/LoadingIndicator/LoadingIndicator.jsx";
import {Alert} from "antd";
const AdminRoute = () => {
const { user } = useSelector((state) => state.auth);
const {
data: user,
isLoading: isUserLoading,
isError: isUserError,
} = useGetAuthenticatedUserDataQuery(undefined, {
pollingInterval: 60000,
});
if (!user || user.role.title !== "Администратор") {
if (isUserLoading) {
return <LoadingIndicator />;
}
if (isUserError) {
return <Alert message="Ошибка загрузки данных пользователя" type="error" showIcon />;
}
if (!user) {
return <Navigate to="/login" />;
}
if (!user.role || user.role.title !== "Администратор") {
return <Navigate to="/" />;
}

View File

@ -9,6 +9,7 @@ import IssuesPage from "../Components/Pages/IssuesPage/IssuesPage.jsx";
import AppointmentsPage from "../Components/Pages/AppointmentsPage/AppointmentsPage.jsx";
import ProfilePage from "../Components/Pages/ProfilePage/ProfilePage.jsx";
import AdminRoute from "./AdminRoute.jsx";
import AdminPage from "../Components/Pages/AdminPage/AdminPage.jsx";
const AppRouter = () => (

View File

@ -0,0 +1,28 @@
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
import {Typography} from "antd";
import useAdminPage from "./useAdminPage.js";
import {ControlOutlined} from "@ant-design/icons";
import useAdminPageUI from "./useAdminPageUI.js";
const AdminPage = () => {
const adminPageData = useAdminPage();
const adminPageUI = useAdminPageUI();
return (
<div style={adminPageUI.containerStyle}>
{adminPageData.isLoading ? (
<LoadingIndicator/>
) : (
<>
<Typography.Title level={1}>
<ControlOutlined/> Панель администратора
</Typography.Title>
</>
)}
</div>
)
};
export default AdminPage;

View File

@ -0,0 +1,21 @@
import {useGetAllUsersQuery} from "../../../Api/usersApi.js";
const useAdminPage = () => {
const {
data: users = [],
isLoading,
isError,
} = useGetAllUsersQuery(undefined, {
pollingInterval: 10000,
});
return {
users,
isLoading,
isError,
};
};
export default useAdminPage;

View File

@ -0,0 +1,16 @@
import {Grid} from "antd";
const {useBreakpoint} = Grid;
const useAdminPageUI = () => {
const screens = useBreakpoint();
const containerStyle = {padding: screens.xs ? 16 : 24};
return {
containerStyle,
};
};
export default useAdminPageUI;

View File

@ -32,7 +32,6 @@ ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend)
const HomePage = () => {
const homePageData = useHomePage();
const homePageUI = useHomePageUI(homePageData.appointments, homePageData.scheduledAppointments, homePageData.patients);
// const selectedScheduledAppointment = useSelector((state) => state.appointmentsUI.selectedScheduledAppointment);
if (homePageData.isError) {
return (

View File

@ -4,7 +4,7 @@ import dayjs from "dayjs";
import isBetween from "dayjs/plugin/isBetween";
import {useSelector} from "react-redux"; // Import isBetween plugin
dayjs.extend(isBetween); // Extend dayjs with isBetween
dayjs.extend(isBetween);
const { useBreakpoint } = Grid;

View File

@ -1,5 +1,5 @@
import {Button, Card, Col, Form, Input, Modal, Row, Space, Typography, Result} from "antd";
import {EditOutlined} from "@ant-design/icons";
import {EditOutlined, UserOutlined} from "@ant-design/icons";
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
import useProfilePage from "./useProfilePage.js";
import useProfilePageUI from "./useProfilePageUI.js";
@ -44,6 +44,9 @@ const ProfilePage = () => {
<LoadingIndicator/>
) : (
<>
<Typography.Title level={1}>
<UserOutlined/> Главная страница
</Typography.Title>
<Typography.Title level={1}>Профиль</Typography.Title>
<Card style={cardStyle}>
<Row gutter={[16, 16]}>