feat: Добавлена страница администрирования пользователей
Добавлены функции создания пользователей. Исправлены ошибки при загрузке данных.
This commit is contained in:
parent
d0d5bc2798
commit
c14ecf1767
20
web-app/src/Api/registerApi.js
Normal file
20
web-app/src/Api/registerApi.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import {baseQueryWithAuth} from "./baseQuery.js";
|
||||||
|
import {createApi} from "@reduxjs/toolkit/query/react";
|
||||||
|
|
||||||
|
|
||||||
|
export const registerApi = createApi({
|
||||||
|
reducerPath: 'registerApi',
|
||||||
|
baseQuery: baseQueryWithAuth,
|
||||||
|
tagTypes: ['Register'],
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
register: builder.mutation({
|
||||||
|
query: (credentials) => ({
|
||||||
|
url: '/register/',
|
||||||
|
method: 'POST',
|
||||||
|
body: credentials,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {useRegisterMutation} = registerApi;
|
||||||
18
web-app/src/Api/rolesApi.js
Normal file
18
web-app/src/Api/rolesApi.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {createApi} from "@reduxjs/toolkit/query/react";
|
||||||
|
import {baseQueryWithAuth} from "./baseQuery.js";
|
||||||
|
|
||||||
|
|
||||||
|
export const rolesApi = createApi({
|
||||||
|
reducerPath: 'rolesApi',
|
||||||
|
baseQuery: baseQueryWithAuth,
|
||||||
|
tagTypes: ['Roles'],
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
getRoles: builder.query({
|
||||||
|
query: () => '/roles/',
|
||||||
|
providesTags: ['Roles'],
|
||||||
|
refetchOnMountOrArgChange: 5,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {useGetRolesQuery} = rolesApi;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { Navigate, Outlet } from "react-router-dom";
|
import {Navigate, Outlet} from "react-router-dom";
|
||||||
import {useGetAuthenticatedUserDataQuery} from "../Api/usersApi.js";
|
import {useGetAuthenticatedUserDataQuery} from "../Api/usersApi.js";
|
||||||
import LoadingIndicator from "../Components/Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
import LoadingIndicator from "../Components/Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import {Alert} from "antd";
|
import {Result} from "antd";
|
||||||
|
|
||||||
const AdminRoute = () => {
|
const AdminRoute = () => {
|
||||||
const {
|
const {
|
||||||
@ -13,22 +13,22 @@ const AdminRoute = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (isUserLoading) {
|
if (isUserLoading) {
|
||||||
return <LoadingIndicator />;
|
return <LoadingIndicator/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUserError) {
|
if (isUserError) {
|
||||||
return <Alert message="Ошибка загрузки данных пользователя" type="error" showIcon />;
|
return <Result status="500" title="500" subTitle="Произошла ошибка при загрузке данных пользователя"/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return <Navigate to="/login" />;
|
return <Navigate to="/login"/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.role || user.role.title !== "Администратор") {
|
if (!user.role || user.role.title !== "Администратор") {
|
||||||
return <Navigate to="/" />;
|
return <Navigate to="/"/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Outlet />;
|
return <Outlet/>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AdminRoute;
|
export default AdminRoute;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import {Alert, Layout, Menu} from "antd";
|
import {Alert, Layout, Menu, Result} from "antd";
|
||||||
import {Outlet} from "react-router-dom";
|
import {Outlet} from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
HomeOutlined,
|
HomeOutlined,
|
||||||
@ -42,7 +42,7 @@ const MainLayout = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (mainLayoutData.isUserError) {
|
if (mainLayoutData.isUserError) {
|
||||||
return <Alert message="Произошла ошибка" type="error" showIcon/>
|
return <Result status="500" title="500" subTitle="Произошла ошибка при загрузке данных"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,14 +1,93 @@
|
|||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
import {useState} from "react";
|
||||||
import {Typography} from "antd";
|
import {Table, Typography, Button, Modal, Form, Input, Select, Alert, Result} from "antd";
|
||||||
import useAdminPage from "./useAdminPage.js";
|
|
||||||
import {ControlOutlined} from "@ant-design/icons";
|
import {ControlOutlined} from "@ant-design/icons";
|
||||||
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
|
import useAdminPage from "./useAdminPage.js";
|
||||||
import useAdminPageUI from "./useAdminPageUI.js";
|
import useAdminPageUI from "./useAdminPageUI.js";
|
||||||
|
import CreateUserModalForm from "./Components/CreateUserModalForm/CreateUserModalForm.jsx";
|
||||||
|
|
||||||
|
const {Title} = Typography;
|
||||||
|
const {Option} = Select;
|
||||||
|
|
||||||
const AdminPage = () => {
|
const AdminPage = () => {
|
||||||
const adminPageData = useAdminPage();
|
const adminPageData = useAdminPage();
|
||||||
const adminPageUI = useAdminPageUI();
|
const adminPageUI = useAdminPageUI();
|
||||||
|
const [errorMessage, setErrorMessage] = useState(null);
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "ID",
|
||||||
|
dataIndex: "id",
|
||||||
|
key: "id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Фамилия",
|
||||||
|
dataIndex: "last_name",
|
||||||
|
key: "lastName",
|
||||||
|
sorter: (a, b) => a.last_name.localeCompare(b.last_name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Имя",
|
||||||
|
dataIndex: "first_name",
|
||||||
|
key: "firstName",
|
||||||
|
sorter: (a, b) => a.first_name.localeCompare(b.first_name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Отчество",
|
||||||
|
dataIndex: "patronymic",
|
||||||
|
key: "patronymic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Роль",
|
||||||
|
dataIndex: ["role", "title"],
|
||||||
|
key: "role",
|
||||||
|
filters: adminPageData.roles.map(role => ({text: role.title, value: role.title})),
|
||||||
|
onFilter: (value, record) => record.role.title === value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Действия",
|
||||||
|
key: "actions",
|
||||||
|
render: (_, record) => (
|
||||||
|
<Button type="link" onClick={() => adminPageUI.openEditModal(record)}>
|
||||||
|
Редактировать
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleEditSubmit = async (values) => {
|
||||||
|
try {
|
||||||
|
await adminPageData.updateUser({
|
||||||
|
id: adminPageUI.selectedUser.id,
|
||||||
|
fullName: values.fullName,
|
||||||
|
password: values.password || undefined, // Отправляем пароль только если он указан
|
||||||
|
role: {title: values.role},
|
||||||
|
});
|
||||||
|
adminPageUI.closeEditModal();
|
||||||
|
setErrorMessage(null);
|
||||||
|
} catch (error) {
|
||||||
|
setErrorMessage("Ошибка при обновлении пользователя");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateSubmit = async (values) => {
|
||||||
|
try {
|
||||||
|
await adminPageData.createUser({
|
||||||
|
fullName: values.fullName,
|
||||||
|
email: values.email,
|
||||||
|
password: values.password,
|
||||||
|
role: {title: values.role},
|
||||||
|
});
|
||||||
|
adminPageUI.closeCreateModal();
|
||||||
|
setErrorMessage(null);
|
||||||
|
} catch (error) {
|
||||||
|
setErrorMessage("Ошибка при создании пользователя");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (adminPageData.isError) {
|
||||||
|
return <Result status="500" title="500" subTitle="Произошла ошибка при загрузке данных"/>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={adminPageUI.containerStyle}>
|
<div style={adminPageUI.containerStyle}>
|
||||||
@ -16,13 +95,29 @@ const AdminPage = () => {
|
|||||||
<LoadingIndicator/>
|
<LoadingIndicator/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Typography.Title level={1}>
|
<Title level={1}>
|
||||||
<ControlOutlined/> Панель администратора
|
<ControlOutlined/> Панель администратора
|
||||||
</Typography.Title>
|
</Title>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={adminPageUI.openCreateModal}
|
||||||
|
style={{marginBottom: 16}}
|
||||||
|
>
|
||||||
|
Создать пользователя
|
||||||
|
</Button>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={adminPageData.users}
|
||||||
|
rowKey="id"
|
||||||
|
pagination={{pageSize: 10}}
|
||||||
|
loading={adminPageData.isLoading}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CreateUserModalForm/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AdminPage;
|
export default AdminPage;
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
import {Button, Form, Input, Modal, Select, Tooltip, Typography} from "antd";
|
||||||
|
import useCreateUserModalForm from "./useCreateUserModalForm.js";
|
||||||
|
import useCreateUserModalFormUI from "./useCreateUserModalFormUI.js";
|
||||||
|
import {InfoCircleOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
|
|
||||||
|
const CreateUserModalForm = () => {
|
||||||
|
const createUserModalFormData = useCreateUserModalForm();
|
||||||
|
const createUserModalFormUI = useCreateUserModalFormUI(createUserModalFormData.registerUser);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="Создать пользователя"
|
||||||
|
open={createUserModalFormUI.modalVisible}
|
||||||
|
onCancel={createUserModalFormUI.handleCancel}
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={createUserModalFormUI.form}
|
||||||
|
onFinish={createUserModalFormUI.handleFinish}
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<Form.Item label="Фамилия" name="last_name" rules={[{required: true, message: "Введите фамилию"}]}>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Имя" name="first_name" rules={[{required: true, message: "Введите имя"}]}>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Отчество" name="patronymic">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Логин" name="login" rules={[{required: true, message: "Введите логин"}]}>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="role_id"
|
||||||
|
label="Роль"
|
||||||
|
rules={[{required: true, message: "Выберите роль"}]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{createUserModalFormData.roles.map((role) => (
|
||||||
|
<Select.Option key={role.id} value={role.id}>
|
||||||
|
{role.title}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Tooltip
|
||||||
|
title="Пароль должен содержать не менее 8 символов, включая хотя бы одну букву и одну цифру и один специальный символ"
|
||||||
|
>
|
||||||
|
<Typography.Title level={3} style={{width: 30}}>
|
||||||
|
<InfoCircleOutlined/>
|
||||||
|
</Typography.Title>
|
||||||
|
</Tooltip>
|
||||||
|
<Form.Item label="Пароль" name="password" rules={[{required: true, message: "Введите пароль"}]}>
|
||||||
|
<Input.Password/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Подтвердите пароль" name="confirm_password"
|
||||||
|
rules={[{required: true, message: "Подтвердите пароль"}, ({getFieldValue}) => ({
|
||||||
|
validator(_, value) {
|
||||||
|
if (!value || getFieldValue("password") === value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error("Пароли не совпадают"));
|
||||||
|
},
|
||||||
|
}),]}>
|
||||||
|
<Input.Password/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Button type="primary" htmlType="submit" loading={createUserModalFormData.isLoading}>
|
||||||
|
Создать
|
||||||
|
</Button>
|
||||||
|
<Button onClick={createUserModalFormUI.handleCancel} style={{marginLeft: 8}}>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateUserModalForm;
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import {useGetRolesQuery} from "../../../../../Api/rolesApi.js";
|
||||||
|
import {useRegisterMutation} from "../../../../../Api/registerApi.js";
|
||||||
|
|
||||||
|
|
||||||
|
const useCreateUserModalForm = () => {
|
||||||
|
const {
|
||||||
|
data: roles = [],
|
||||||
|
isLoading: isLoadingRoles,
|
||||||
|
isError: isErrorRoles,
|
||||||
|
} = useGetRolesQuery(undefined, {
|
||||||
|
pollingInterval: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [registerUser] = useRegisterMutation();
|
||||||
|
|
||||||
|
return {
|
||||||
|
roles,
|
||||||
|
isLoadingRoles,
|
||||||
|
isErrorRoles,
|
||||||
|
registerUser,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCreateUserModalForm;
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
|
import {closeModal} from "../../../../../Redux/Slices/adminSlice.js";
|
||||||
|
import {Form, notification} from "antd";
|
||||||
|
|
||||||
|
const useCreateUserModalFormUI = (registerUser) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalVisible,
|
||||||
|
} = useSelector(state => state.adminUI);
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
dispatch(closeModal());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFinish = async () => {
|
||||||
|
const values = form.getFieldsValue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(values);
|
||||||
|
await registerUser(values).unwrap();
|
||||||
|
notification.success({
|
||||||
|
message: "Пользователь зарегистрирован",
|
||||||
|
description: "Пользователь успешно зарегистрирован",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
form.resetFields();
|
||||||
|
handleCancel();
|
||||||
|
} catch (error) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка регистрации пользователя",
|
||||||
|
description: error?.data?.detail || "Не удалось зарегистрировать пользователя",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
modalVisible,
|
||||||
|
handleCancel,
|
||||||
|
handleFinish,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCreateUserModalFormUI;
|
||||||
@ -1,20 +1,28 @@
|
|||||||
import {useGetAllUsersQuery} from "../../../Api/usersApi.js";
|
import {useGetAllUsersQuery} from "../../../Api/usersApi.js";
|
||||||
|
import {useGetRolesQuery} from "../../../Api/rolesApi.js";
|
||||||
|
|
||||||
const useAdminPage = () => {
|
const useAdminPage = () => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: users = [],
|
data: users = [], isLoading, isError,
|
||||||
isLoading,
|
|
||||||
isError,
|
|
||||||
} = useGetAllUsersQuery(undefined, {
|
} = useGetAllUsersQuery(undefined, {
|
||||||
pollingInterval: 10000,
|
pollingInterval: 10000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const {data: roles = [], isLoading: isLoadingRoles, isError: isErrorRoles} = useGetRolesQuery(undefined, {
|
||||||
|
pollingInterval: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// const [updateUser, { isLoading: isUpdating, isError: isUpdateError }] = useUpdateUserMutation();
|
||||||
|
// const [createUser, { isLoading: isCreating, isError: isCreateError }] = useCreateUserMutation();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
users,
|
users,
|
||||||
isLoading,
|
roles,
|
||||||
isError,
|
isLoading: isLoading || isLoadingRoles,
|
||||||
|
isError: isError || isErrorRoles,
|
||||||
|
updateUser: () => {
|
||||||
|
}, isUpdating: false, isUpdateError: false, createUser: () => {
|
||||||
|
}, isCreating: false, isCreateError: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,57 @@
|
|||||||
import {Grid} from "antd";
|
import {useState} from "react";
|
||||||
|
import {Grid, Form} from "antd";
|
||||||
|
import {useDispatch} from "react-redux";
|
||||||
|
import {openModal} from "../../../Redux/Slices/adminSlice.js";
|
||||||
|
|
||||||
const {useBreakpoint} = Grid;
|
const {useBreakpoint} = Grid;
|
||||||
|
|
||||||
const useAdminPageUI = () => {
|
const useAdminPageUI = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const screens = useBreakpoint();
|
const screens = useBreakpoint();
|
||||||
|
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||||
|
const [createModalVisible, setCreateModalVisible] = useState(false);
|
||||||
|
const [selectedUser, setSelectedUser] = useState(null);
|
||||||
|
const [editForm] = Form.useForm();
|
||||||
|
const [createForm] = Form.useForm();
|
||||||
|
|
||||||
const containerStyle = {padding: screens.xs ? 16 : 24};
|
const containerStyle = {padding: screens.xs ? 16 : 24};
|
||||||
|
|
||||||
|
const openEditModal = (user) => {
|
||||||
|
setSelectedUser(user);
|
||||||
|
editForm.setFieldsValue({
|
||||||
|
fullName: user.fullName,
|
||||||
|
role: user.role?.title || "",
|
||||||
|
});
|
||||||
|
setEditModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeEditModal = () => {
|
||||||
|
setEditModalVisible(false);
|
||||||
|
setSelectedUser(null);
|
||||||
|
editForm.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCreateModal = () => {
|
||||||
|
dispatch(openModal());
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeCreateModal = () => {
|
||||||
|
setCreateModalVisible(false);
|
||||||
|
createForm.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
containerStyle,
|
containerStyle,
|
||||||
|
editModalVisible,
|
||||||
|
createModalVisible,
|
||||||
|
selectedUser,
|
||||||
|
editForm,
|
||||||
|
createForm,
|
||||||
|
openEditModal,
|
||||||
|
closeEditModal,
|
||||||
|
openCreateModal,
|
||||||
|
closeCreateModal,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,6 @@ const useCalendarCellUI = () => {
|
|||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Styles
|
|
||||||
const containerStyle = {
|
const containerStyle = {
|
||||||
height: "100%",
|
height: "100%",
|
||||||
cursor: isCompressed ? "pointer" : "default",
|
cursor: isCompressed ? "pointer" : "default",
|
||||||
@ -61,14 +60,12 @@ const useCalendarCellUI = () => {
|
|||||||
color: "#1890ff",
|
color: "#1890ff",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Static configuration
|
|
||||||
const labels = {
|
const labels = {
|
||||||
pastAppointment: "Прошедший прием",
|
pastAppointment: "Прошедший прием",
|
||||||
scheduledAppointment: "Запланированный прием",
|
scheduledAppointment: "Запланированный прием",
|
||||||
notSpecified: "Не указан",
|
notSpecified: "Не указан",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Formatting functions
|
|
||||||
const getAppointmentTime = (datetime) => {
|
const getAppointmentTime = (datetime) => {
|
||||||
return datetime ? dayjs(datetime).format("HH:mm") : labels.notSpecified;
|
return datetime ? dayjs(datetime).format("HH:mm") : labels.notSpecified;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -70,10 +70,9 @@ const useLensIssueFormUI = (visible, onCancel, onSubmit, patients, lenses) => {
|
|||||||
setSelectedLens(null);
|
setSelectedLens(null);
|
||||||
setIssueDate(dayjs(new Date()));
|
setIssueDate(dayjs(new Date()));
|
||||||
} catch (errorInfo) {
|
} catch (errorInfo) {
|
||||||
console.error("Validation Failed:", errorInfo);
|
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка валидации",
|
message: "Ошибка валидации",
|
||||||
description: "Проверьте правильность заполнения полей.",
|
description: errorInfo?.data?.detail || "Проверьте правильность заполнения полей.",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ const useIssues = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка выдачи линзы",
|
message: "Ошибка выдачи линзы",
|
||||||
description: error.data?.message || "Не удалось выдать линзу пациенту.",
|
description: error?.data?.detail || "Не удалось выдать линзу пациенту.",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {useEffect} from "react";
|
import {useEffect} from "react";
|
||||||
import {Form} from "antd";
|
import {Form, notification} from "antd";
|
||||||
|
|
||||||
|
|
||||||
const useLensFormUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
const useLensFormUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
||||||
@ -19,7 +19,7 @@ const useLensFormUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
|||||||
const modalStyle = {
|
const modalStyle = {
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
};
|
};
|
||||||
const formItemStyle = { width: "100%" };
|
const formItemStyle = {width: "100%"};
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
try {
|
try {
|
||||||
@ -27,6 +27,11 @@ const useLensFormUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
|||||||
onSubmit(values);
|
onSubmit(values);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка",
|
||||||
|
description: error?.data?.detail || lens ? "Не удалось обновить линзу" : "Не удалось создать линзу",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
console.log("Validation Failed:", error);
|
console.log("Validation Failed:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -35,7 +40,7 @@ const useLensFormUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
|||||||
form.resetFields();
|
form.resetFields();
|
||||||
onCancel();
|
onCancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
form,
|
form,
|
||||||
modalStyle,
|
modalStyle,
|
||||||
|
|||||||
@ -35,7 +35,7 @@ const useLenses = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка удаления",
|
message: "Ошибка удаления",
|
||||||
description: error.data?.message || "Не удалось удалить линзу",
|
description: error?.data?.detail || "Не удалось удалить линзу",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ const useLenses = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка",
|
message: "Ошибка",
|
||||||
description: error.data?.message || "Произошла ошибка при сохранении",
|
description: error?.data?.detail || "Произошла ошибка при сохранении",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const useLoginPage = () => {
|
|||||||
localStorage.setItem("access_token", token);
|
localStorage.setItem("access_token", token);
|
||||||
dispatch(setUser({ token }));
|
dispatch(setUser({ token }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error?.data?.message || "Не удалось войти";
|
const errorMessage = error?.data?.detail || "Не удалось войти";
|
||||||
dispatch(setError(errorMessage));
|
dispatch(setError(errorMessage));
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка при входе",
|
message: "Ошибка при входе",
|
||||||
|
|||||||
@ -22,7 +22,7 @@ const usePatients = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка удаления",
|
message: "Ошибка удаления",
|
||||||
description: error.data?.message || "Не удалось удалить пациента",
|
description: error?.data?.detail || "Не удалось удалить пациента",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ const useProfilePage = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка",
|
message: "Ошибка",
|
||||||
description: error?.data?.message || "Не удалось обновить профиль.",
|
description: error?.data?.detail || "Не удалось обновить профиль.",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ const useProfilePage = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка",
|
message: "Ошибка",
|
||||||
description: error?.data?.message || "Не удалось изменить пароль.",
|
description: error?.data?.detail || "Не удалось изменить пароль.",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,7 @@ const useScheduledAppointmentsViewModalUI = (cancelAppointment) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка",
|
message: "Ошибка",
|
||||||
description: error.data?.message || "Не удалось отменить прием.",
|
description: error?.data?.detail || "Не удалось отменить прием.",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
30
web-app/src/Redux/Slices/adminSlice.js
Normal file
30
web-app/src/Redux/Slices/adminSlice.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {createSlice} from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
modalVisible: false,
|
||||||
|
selectedUser: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const adminSlice = createSlice({
|
||||||
|
name: 'adminUI',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setSelectedUser(state, action) {
|
||||||
|
state.selectedUser = action.payload;
|
||||||
|
},
|
||||||
|
openModal(state) {
|
||||||
|
state.modalVisible = true;
|
||||||
|
},
|
||||||
|
closeModal(state) {
|
||||||
|
state.modalVisible = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
openModal,
|
||||||
|
closeModal,
|
||||||
|
setSelectedUser,
|
||||||
|
} = adminSlice.actions;
|
||||||
|
|
||||||
|
export default adminSlice.reducer;
|
||||||
@ -17,6 +17,9 @@ import {usersApi} from "../Api/usersApi.js";
|
|||||||
import usersReducer from "./Slices/usersSlice.js";
|
import usersReducer from "./Slices/usersSlice.js";
|
||||||
import {authApi} from "../Api/authApi.js";
|
import {authApi} from "../Api/authApi.js";
|
||||||
import authReducer from "./Slices/authSlice.js";
|
import authReducer from "./Slices/authSlice.js";
|
||||||
|
import {rolesApi} from "../Api/rolesApi.js";
|
||||||
|
import adminReducer from "./Slices/adminSlice.js";
|
||||||
|
import {registerApi} from "../Api/registerApi.js";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@ -48,6 +51,12 @@ export const store = configureStore({
|
|||||||
|
|
||||||
auth: authReducer,
|
auth: authReducer,
|
||||||
[authApi.reducerPath]: authApi.reducer,
|
[authApi.reducerPath]: authApi.reducer,
|
||||||
|
|
||||||
|
adminUI: adminReducer,
|
||||||
|
|
||||||
|
[rolesApi.reducerPath]: rolesApi.reducer,
|
||||||
|
|
||||||
|
[registerApi.reducerPath]: registerApi.reducer
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) => (
|
middleware: (getDefaultMiddleware) => (
|
||||||
getDefaultMiddleware().concat(
|
getDefaultMiddleware().concat(
|
||||||
@ -62,6 +71,8 @@ export const store = configureStore({
|
|||||||
appointmentTypesApi.middleware,
|
appointmentTypesApi.middleware,
|
||||||
usersApi.middleware,
|
usersApi.middleware,
|
||||||
authApi.middleware,
|
authApi.middleware,
|
||||||
|
rolesApi.middleware,
|
||||||
|
registerApi.middleware,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user