feat: Обновление UI и логики форм и страниц
Обновлены компоненты форм, страницы авторизации и другие. Добавлены индикаторы загрузки и улучшены обработка ошибок.
This commit is contained in:
parent
333a471f9d
commit
6cfa2dcca2
@ -331,7 +331,7 @@ const AppointmentFormModal = () => {
|
|||||||
<Button
|
<Button
|
||||||
style={appointmentFormModalUI.footerButtonStyle}
|
style={appointmentFormModalUI.footerButtonStyle}
|
||||||
onClick={appointmentFormModalUI.handleClickBackButton}
|
onClick={appointmentFormModalUI.handleClickBackButton}
|
||||||
disabled={appointmentFormModalUI.disableBackButton || appointmentFormModalUI.isUploadingFile || appointmentFormModalData.isCreating}
|
disabled={appointmentFormModalUI.disableBackButton || appointmentFormModalUI.isUploadingFile || appointmentFormModalData.isProcessed}
|
||||||
>
|
>
|
||||||
Назад
|
Назад
|
||||||
</Button>
|
</Button>
|
||||||
@ -339,7 +339,7 @@ const AppointmentFormModal = () => {
|
|||||||
type="primary"
|
type="primary"
|
||||||
onClick={appointmentFormModalUI.handleClickNextButton}
|
onClick={appointmentFormModalUI.handleClickNextButton}
|
||||||
disabled={appointmentFormModalUI.disableNextButton}
|
disabled={appointmentFormModalUI.disableNextButton}
|
||||||
loading={appointmentFormModalData.isCreating || appointmentFormModalUI.isUploadingFile}
|
loading={appointmentFormModalData.isProcessed || appointmentFormModalUI.isUploadingFile}
|
||||||
>
|
>
|
||||||
{appointmentFormModalUI.nextButtonText}
|
{appointmentFormModalUI.nextButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { useGetPatientsQuery } from "../../../Api/patientsApi.js";
|
import {useGetPatientsQuery} from "../../../Api/patientsApi.js";
|
||||||
import { useGetAppointmentTypesQuery } from "../../../Api/appointmentTypesApi.js";
|
import {useGetAppointmentTypesQuery} from "../../../Api/appointmentTypesApi.js";
|
||||||
import {
|
import {
|
||||||
useCreateAppointmentMutation,
|
useCreateAppointmentMutation,
|
||||||
useGetByPatientIdQuery,
|
useGetByPatientIdQuery,
|
||||||
} from "../../../Api/appointmentsApi.js";
|
} from "../../../Api/appointmentsApi.js";
|
||||||
import { useCancelScheduledAppointmentMutation } from "../../../Api/scheduledAppointmentsApi.js";
|
import {useCancelScheduledAppointmentMutation} from "../../../Api/scheduledAppointmentsApi.js";
|
||||||
|
|
||||||
const useAppointmentFormModal = () => {
|
const useAppointmentFormModal = () => {
|
||||||
const {
|
const {
|
||||||
@ -18,8 +18,11 @@ const useAppointmentFormModal = () => {
|
|||||||
isError: isErrorAppointmentTypes,
|
isError: isErrorAppointmentTypes,
|
||||||
} = useGetAppointmentTypesQuery(undefined);
|
} = useGetAppointmentTypesQuery(undefined);
|
||||||
|
|
||||||
const [createAppointment, { isLoading: isCreating, isError: isErrorCreating }] = useCreateAppointmentMutation();
|
const [createAppointment, {isLoading: isCreating, isError: isErrorCreating}] = useCreateAppointmentMutation();
|
||||||
const [cancelAppointment] = useCancelScheduledAppointmentMutation();
|
const [cancelAppointment, {
|
||||||
|
isLoading: isCanceling,
|
||||||
|
isError: isErrorCanceling
|
||||||
|
}] = useCancelScheduledAppointmentMutation();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
patients,
|
patients,
|
||||||
@ -27,9 +30,9 @@ const useAppointmentFormModal = () => {
|
|||||||
createAppointment,
|
createAppointment,
|
||||||
cancelAppointment,
|
cancelAppointment,
|
||||||
useGetByPatientIdQuery,
|
useGetByPatientIdQuery,
|
||||||
isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating,
|
isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating || isCanceling,
|
||||||
isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating,
|
isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating || isErrorCanceling,
|
||||||
isCreating,
|
isProcessed: isCreating || isCanceling,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,7 @@ const PatientFormModal = () => {
|
|||||||
open={patientFormModalUI.isModalVisible}
|
open={patientFormModalUI.isModalVisible}
|
||||||
onCancel={patientFormModalUI.handleCancel}
|
onCancel={patientFormModalUI.handleCancel}
|
||||||
onOk={patientFormModalUI.handleOk}
|
onOk={patientFormModalUI.handleOk}
|
||||||
|
confirmLoading={patientFormModalData.isOperationLoading}
|
||||||
okText={"Сохранить"}
|
okText={"Сохранить"}
|
||||||
cancelText={"Отмена"}
|
cancelText={"Отмена"}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import {useAddPatientMutation, useUpdatePatientMutation} from "../../../Api/pati
|
|||||||
const usePatientFormModal = () => {
|
const usePatientFormModal = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const [addPatient] = useAddPatientMutation();
|
const [addPatient, { isLoading: isAdding }] = useAddPatientMutation();
|
||||||
const [updatePatient] = useUpdatePatientMutation();
|
const [updatePatient, { isLoading: isUpdating }] = useUpdatePatientMutation();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selectedPatient,
|
selectedPatient,
|
||||||
@ -44,6 +44,7 @@ const usePatientFormModal = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
handleModalSubmit,
|
handleModalSubmit,
|
||||||
|
isOperationLoading: isAdding || isUpdating
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -96,6 +96,7 @@ const ScheduledAppointmentFormModal = () => {
|
|||||||
disabled={scheduledAppointmentFormModalUI.selectedPatient === null || scheduledAppointmentFormModalUI.selectedDateTime === null || scheduledAppointmentFormModalUI.selectedAppointmentType === null}
|
disabled={scheduledAppointmentFormModalUI.selectedPatient === null || scheduledAppointmentFormModalUI.selectedDateTime === null || scheduledAppointmentFormModalUI.selectedAppointmentType === null}
|
||||||
onClick={scheduledAppointmentFormModalUI.handleCreateScheduledAppointment}
|
onClick={scheduledAppointmentFormModalUI.handleCreateScheduledAppointment}
|
||||||
style={scheduledAppointmentFormModalUI.buttonStyle}
|
style={scheduledAppointmentFormModalUI.buttonStyle}
|
||||||
|
loading={scheduledAppointmentFormModalData.isProcessing}
|
||||||
>
|
>
|
||||||
Запланировать
|
Запланировать
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -28,6 +28,7 @@ const useScheduledAppointmentFormModal = () => {
|
|||||||
createScheduledAppointment,
|
createScheduledAppointment,
|
||||||
isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating,
|
isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating,
|
||||||
isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating,
|
isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating,
|
||||||
|
isProcessing: isCreating
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import useLensForm from "./useLensForm.js";
|
|||||||
import useLensFormUI from "./useLensFormUI.js";
|
import useLensFormUI from "./useLensFormUI.js";
|
||||||
|
|
||||||
|
|
||||||
const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
const LensFormModal = ({visible, onCancel, onSubmit, lens, isProcessing}) => {
|
||||||
const lensFormData = useLensForm();
|
const lensFormData = useLensForm();
|
||||||
const lensFormUI = useLensFormUI(lensFormData.lensTypes, visible, onCancel, onSubmit, lens);
|
const lensFormUI = useLensFormUI(lensFormData.lensTypes, visible, onCancel, onSubmit, lens);
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
open={visible}
|
open={visible}
|
||||||
onCancel={lensFormUI.handleCancel}
|
onCancel={lensFormUI.handleCancel}
|
||||||
onOk={lensFormUI.handleOk}
|
onOk={lensFormUI.handleOk}
|
||||||
|
confirmLoading={isProcessing}
|
||||||
okText={"Сохранить"}
|
okText={"Сохранить"}
|
||||||
cancelText={"Отмена"}
|
cancelText={"Отмена"}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
@ -156,6 +157,7 @@ LensFormModal.propTypes = {
|
|||||||
onCancel: PropTypes.func.isRequired,
|
onCancel: PropTypes.func.isRequired,
|
||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
lens: LensPropType,
|
lens: LensPropType,
|
||||||
|
isProcessing: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LensFormModal;
|
export default LensFormModal;
|
||||||
@ -124,7 +124,12 @@ const LensesTab = () => {
|
|||||||
okText="Да, удалить"
|
okText="Да, удалить"
|
||||||
cancelText="Отмена"
|
cancelText="Отмена"
|
||||||
>
|
>
|
||||||
<Button danger>Удалить</Button>
|
<Button
|
||||||
|
loading={lensesData.isProcessing}
|
||||||
|
danger
|
||||||
|
>
|
||||||
|
Удалить
|
||||||
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@ -327,6 +332,7 @@ const LensesTab = () => {
|
|||||||
onCancel={lensesUI.handleCloseModal}
|
onCancel={lensesUI.handleCloseModal}
|
||||||
onSubmit={lensesData.handleModalSubmit}
|
onSubmit={lensesData.handleModalSubmit}
|
||||||
lens={lensesUI.selectedLens}
|
lens={lensesUI.selectedLens}
|
||||||
|
isProcessing={lensesData.isProcessing}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -20,9 +20,9 @@ const useLenses = () => {
|
|||||||
pollingInterval: 20000,
|
pollingInterval: 20000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [addLens] = useAddLensMutation();
|
const [addLens, {isLoading: isAdding}] = useAddLensMutation();
|
||||||
const [updateLens] = useUpdateLensMutation();
|
const [updateLens, {isLoading: isUpdating}] = useUpdateLensMutation();
|
||||||
const [deleteLens] = useDeleteLensMutation();
|
const [deleteLens, {isLoading: isDeleting}] = useDeleteLensMutation();
|
||||||
|
|
||||||
const handleDeleteLens = async (lensId) => {
|
const handleDeleteLens = async (lensId) => {
|
||||||
try {
|
try {
|
||||||
@ -73,6 +73,7 @@ const useLenses = () => {
|
|||||||
lenses,
|
lenses,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
|
isProcessing: isAdding || isUpdating || isDeleting,
|
||||||
handleDeleteLens,
|
handleDeleteLens,
|
||||||
handleModalSubmit,
|
handleModalSubmit,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,40 @@
|
|||||||
import {Form, Input, Button, Row, Col, Typography} from "antd";
|
import {Form, Input, Button, Row, Col, Typography, Image, Space} from "antd";
|
||||||
import {useSelector} from "react-redux";
|
|
||||||
import useLoginPage from "./useLoginPage.js";
|
import useLoginPage from "./useLoginPage.js";
|
||||||
import useLoginPageUI from "./useLoginPageUI.js";
|
import useLoginPageUI from "./useLoginPageUI.js";
|
||||||
|
|
||||||
const {Title} = Typography;
|
const {Title} = Typography;
|
||||||
|
|
||||||
const LoginPage = () => {
|
const LoginPage = () => {
|
||||||
const {error} = useSelector((state) => state.auth);
|
|
||||||
|
|
||||||
const {onFinish, isLoading} = useLoginPage();
|
const {onFinish, isLoading} = useLoginPage();
|
||||||
const {containerStyle, formContainerStyle, titleStyle, errorStyle, labels} = useLoginPageUI();
|
const {
|
||||||
|
containerStyle,
|
||||||
|
formContainerStyle,
|
||||||
|
titleStyle,
|
||||||
|
logoBlockStyle,
|
||||||
|
logoStyle,
|
||||||
|
appNameStyle,
|
||||||
|
labels
|
||||||
|
} = useLoginPageUI();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row justify="center" align="middle" style={containerStyle}>
|
<Row justify="center" align="middle" style={containerStyle}>
|
||||||
<Col xs={24} sm={18} md={12} lg={8} xl={6}>
|
<Col xs={24} sm={18} md={12} lg={8} xl={6}>
|
||||||
|
<Space direction="horizontal" style={logoBlockStyle} size="large">
|
||||||
|
<Image
|
||||||
|
src="/logo_rounded.png"
|
||||||
|
style={logoStyle}
|
||||||
|
preview={false}
|
||||||
|
alt="Visus API Logo"
|
||||||
|
/>
|
||||||
|
<Title level={1} style={appNameStyle}>
|
||||||
|
Visus+
|
||||||
|
</Title>
|
||||||
|
</Space>
|
||||||
<div style={formContainerStyle}>
|
<div style={formContainerStyle}>
|
||||||
<Title level={2} style={titleStyle}>
|
<Title level={2} style={titleStyle}>
|
||||||
{labels.title}
|
{labels.title}
|
||||||
</Title>
|
</Title>
|
||||||
|
|
||||||
{error && (
|
|
||||||
<div style={errorStyle}>
|
|
||||||
{labels.errorPrefix}
|
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form name="login" initialValues={{remember: true}} onFinish={onFinish}>
|
<Form name="login" initialValues={{remember: true}} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="login"
|
name="login"
|
||||||
|
|||||||
@ -13,14 +13,14 @@ const useLoginPage = () => {
|
|||||||
const response = await loginUser(loginData).unwrap();
|
const response = await loginUser(loginData).unwrap();
|
||||||
const token = response.access_token || response.token;
|
const token = response.access_token || response.token;
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error("Токен не получен от сервера");
|
throw new Error("Сервер не вернул токен авторизации");
|
||||||
}
|
}
|
||||||
localStorage.setItem("access_token", token);
|
localStorage.setItem("access_token", token);
|
||||||
dispatch(setUser({ token }));
|
dispatch(setUser({ token }));
|
||||||
|
|
||||||
await dispatch(checkAuth()).unwrap();
|
await dispatch(checkAuth()).unwrap();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error?.data?.detail || "Не удалось войти";
|
const errorMessage = error?.data?.detail || "Не удалось войти. Проверьте логин и пароль.";
|
||||||
console.error(error);
|
console.error(error);
|
||||||
dispatch(setError(errorMessage));
|
dispatch(setError(errorMessage));
|
||||||
notification.error({
|
notification.error({
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import {useEffect, useRef} from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import {useNavigate} from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import {useSelector} from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import {Grid} from "antd";
|
import { Grid } from "antd";
|
||||||
|
|
||||||
const {useBreakpoint} = Grid;
|
const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
const useLoginPageUI = () => {
|
const useLoginPageUI = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const {user, userData, isLoading} = useSelector((state) => state.auth);
|
const { user, userData, isLoading } = useSelector((state) => state.auth);
|
||||||
const screens = useBreakpoint();
|
const screens = useBreakpoint();
|
||||||
const hasRedirected = useRef(false);
|
const hasRedirected = useRef(false);
|
||||||
|
|
||||||
@ -19,15 +19,32 @@ const useLoginPageUI = () => {
|
|||||||
padding: screens.xs ? 10 : 20,
|
padding: screens.xs ? 10 : 20,
|
||||||
border: "1px solid #ddd",
|
border: "1px solid #ddd",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
|
textAlign: "center",
|
||||||
};
|
};
|
||||||
|
|
||||||
const titleStyle = {
|
const titleStyle = {
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
|
marginBottom: 20,
|
||||||
};
|
};
|
||||||
|
|
||||||
const errorStyle = {
|
const logoStyle = {
|
||||||
color: "red",
|
width: 80,
|
||||||
marginBottom: 15,
|
marginBottom: 10,
|
||||||
|
borderRadius: 20,
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
};
|
||||||
|
|
||||||
|
const appNameStyle = {
|
||||||
|
textAlign: "center",
|
||||||
|
color: "#1890ff",
|
||||||
|
marginBottom: 40,
|
||||||
|
};
|
||||||
|
|
||||||
|
const logoBlockStyle = {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
};
|
};
|
||||||
|
|
||||||
const labels = {
|
const labels = {
|
||||||
@ -37,7 +54,6 @@ const useLoginPageUI = () => {
|
|||||||
submitButton: "Войти",
|
submitButton: "Войти",
|
||||||
loginRequired: "Пожалуйста, введите логин",
|
loginRequired: "Пожалуйста, введите логин",
|
||||||
passwordRequired: "Пожалуйста, введите пароль",
|
passwordRequired: "Пожалуйста, введите пароль",
|
||||||
errorPrefix: "Ошибка при входе: ",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -52,8 +68,10 @@ const useLoginPageUI = () => {
|
|||||||
containerStyle,
|
containerStyle,
|
||||||
formContainerStyle,
|
formContainerStyle,
|
||||||
titleStyle,
|
titleStyle,
|
||||||
errorStyle,
|
logoStyle,
|
||||||
|
appNameStyle,
|
||||||
labels,
|
labels,
|
||||||
|
logoBlockStyle,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user