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