376 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import JoditEditor from "jodit-react";
import dayjs from "dayjs";
import {
Button,
Collapse,
DatePicker,
Form,
Input,
InputNumber,
Modal,
Result,
Row,
Select,
Spin,
Steps,
Typography,
Drawer,
Upload,
List,
} from "antd";
import { UploadOutlined } from '@ant-design/icons';
import useAppointmentFormModal from "./useAppointmentFormModal.js";
import useAppointmentFormModalUI from "./useAppointmentFormModalUI.js";
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
import {useMemo, useCallback, useRef} from "react";
const AppointmentFormModal = () => {
const appointmentFormModalData = useAppointmentFormModal();
const appointmentFormModalUI = useAppointmentFormModalUI(
appointmentFormModalData.createAppointment,
appointmentFormModalData.patients,
appointmentFormModalData.cancelAppointment,
appointmentFormModalData.useGetByPatientIdQuery
);
const cursorPositionRef = useRef(null);
const saveCursorPosition = useCallback(() => {
if (appointmentFormModalUI.editor.current) {
const editor = appointmentFormModalUI.editor.current.editor;
const selection = editor.selection;
if (selection) {
cursorPositionRef.current = selection.getBookmark();
}
}
}, [appointmentFormModalUI.editor]);
const restoreCursorPosition = useCallback(() => {
if (appointmentFormModalUI.editor.current && cursorPositionRef.current) {
const editor = appointmentFormModalUI.editor.current.editor;
const selection = editor.selection;
if (selection && cursorPositionRef.current) {
selection.moveToBookmark(cursorPositionRef.current);
}
}
}, [appointmentFormModalUI.editor]);
const handleEditorBlur = useCallback(
(newContent) => {
saveCursorPosition();
appointmentFormModalUI.form.setFieldsValue({results: newContent});
setTimeout(restoreCursorPosition, 0);
},
[appointmentFormModalUI.form, saveCursorPosition, restoreCursorPosition]
);
const patientsItems = useMemo(() =>
appointmentFormModalUI.filteredPatients.map((patient) => ({
key: patient.id,
label: `${patient.last_name} ${patient.first_name} (${appointmentFormModalUI.getDateString(patient.birthday)})`,
children: (
<div>
<p>
<b>Пациент:</b> {patient.last_name} {patient.first_name}
</p>
<p>
<b>Дата рождения:</b> {appointmentFormModalUI.getDateString(patient.birthday)}
</p>
<p>
<b>Диагноз:</b> {patient.diagnosis || "Не указан"}
</p>
<p>
<b>Email:</b> {patient.email || "Не указан"}
</p>
<p>
<b>Телефон:</b> {patient.phone || ""}
</p>
<Button type="primary" onClick={() => appointmentFormModalUI.setSelectedPatient(patient)}>
Выбрать
</Button>
</div>
),
})),
[appointmentFormModalUI.filteredPatients, appointmentFormModalUI.getDateString, appointmentFormModalUI.setSelectedPatient]
);
const SelectPatientStep = useMemo(() => {
return appointmentFormModalUI.selectedPatient ? (
<div style={appointmentFormModalUI.blockStepStyle}>
<Typography.Text strong>
{appointmentFormModalUI.selectedPatient.last_name} {appointmentFormModalUI.selectedPatient.first_name}
</Typography.Text>
<p>
<b>Дата рождения:</b> {appointmentFormModalUI.getSelectedPatientBirthdayString()}
</p>
<p>
<b>Email:</b> {appointmentFormModalUI.selectedPatient.email || "Не указан"}
</p>
<p>
<b>Телефон:</b> {appointmentFormModalUI.selectedPatient.phone || ""}
</p>
<Button type="primary" onClick={appointmentFormModalUI.resetPatient} danger>
Выбрать другого пациента
</Button>
</div>
) : (
<>
<Input
placeholder="Поиск пациента"
value={appointmentFormModalUI.searchPatientString}
onChange={appointmentFormModalUI.handleSetSearchPatientString}
style={appointmentFormModalUI.searchInputStyle}
allowClear
/>
<div style={appointmentFormModalUI.chooseContainerStyle}>
<Collapse items={patientsItems}/>
</div>
</>
);
}, [
appointmentFormModalUI.selectedPatient,
appointmentFormModalUI.searchPatientString,
appointmentFormModalUI.blockStepStyle,
appointmentFormModalUI.chooseContainerStyle,
appointmentFormModalUI.searchInputStyle,
appointmentFormModalUI.getSelectedPatientBirthdayString,
appointmentFormModalUI.resetPatient,
appointmentFormModalUI.handleSetSearchPatientString,
patientsItems,
]);
const AppointmentStep = useMemo(() => {
return (
<div>
<Button
type="primary"
onClick={appointmentFormModalUI.showDrawer}
style={{marginBottom: 16}}
disabled={!appointmentFormModalUI.selectedPatient}
>
Показать прошлые приемы
</Button>
<Form
form={appointmentFormModalUI.form}
onFinish={appointmentFormModalUI.handleOk}
initialValues={{
patient_id: appointmentFormModalUI.selectedPatient?.id,
}}
layout="vertical"
>
<Form.Item name="type_id" label="Тип приема"
rules={[{required: true, message: "Выберите тип приема"}]}>
<Select placeholder="Выберите тип приема">
{appointmentFormModalData.appointmentTypes.map((type) => (
<Select.Option key={type.id} value={type.id}>
{type.title}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name="appointment_datetime"
label="Время приема"
rules={[{required: true, message: "Выберите время"}]}
>
<DatePicker
maxDate={dayjs(new Date()).add(1, "day")}
showTime
format="DD.MM.YYYY HH:mm"
style={{width: "100%"}}
/>
</Form.Item>
<Form.Item
name="days_until_the_next_appointment"
label="Дней до следующего приема"
rules={[{type: "number", min: 0, message: "Введите неотрицательное число"}]}
>
<InputNumber min={0} style={{width: "100%"}}/>
</Form.Item>
<Form.Item name="results" label="Результаты приема">
<div className="jodit-container">
<JoditEditor
ref={appointmentFormModalUI.editor}
value={appointmentFormModalUI.form.getFieldValue("results") || ""}
config={appointmentFormModalUI.joditConfig}
onBlur={handleEditorBlur}
/>
</div>
</Form.Item>
<Form.Item name="files" label="Прикрепить файлы">
<Upload
fileList={appointmentFormModalUI.draftFiles}
beforeUpload={(file) => {
appointmentFormModalUI.handleAddFile(file);
return false; // Prevent auto-upload
}}
onRemove={(file) => appointmentFormModalUI.handleRemoveFile(file)}
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"
multiple
>
<Button icon={<UploadOutlined />}>Выбрать файлы</Button>
</Upload>
</Form.Item>
</Form>
</div>
);
}, [
appointmentFormModalUI.form,
appointmentFormModalUI.selectedPatient,
appointmentFormModalUI.showDrawer,
appointmentFormModalUI.editor,
appointmentFormModalUI.draftFiles,
appointmentFormModalUI.handleAddFile,
appointmentFormModalUI.handleRemoveFile,
appointmentFormModalData.appointmentTypes,
appointmentFormModalUI.joditConfig,
handleEditorBlur,
]);
const ConfirmStep = useMemo(() => {
const values = appointmentFormModalUI.form.getFieldsValue();
const patient = appointmentFormModalData.patients.find((p) => p.id === values.patient_id);
const appointmentType = appointmentFormModalData.appointmentTypes.find((t) => t.id === values.type_id);
return (
<div style={appointmentFormModalUI.blockStepStyle}>
<Typography.Title level={4}>Подтверждение</Typography.Title>
<p>
<b>Пациент:</b> {patient ? `${patient.last_name} ${patient.first_name}` : "Не указан"}
</p>
<p>
<b>Тип приема:</b> {appointmentType ? appointmentType.title : "Не указан"}
</p>
<p>
<b>Время приема:</b>{" "}
{values.appointment_datetime ? dayjs(values.appointment_datetime).format("DD.MM.YYYY HH:mm") : "Не указано"}
</p>
<p>
<b>Дней до следующего приема:</b> {values.days_until_the_next_appointment || "Не указано"}
</p>
<p>
<b>Результаты приема:</b>
</p>
<div dangerouslySetInnerHTML={{__html: values.results || "Не указаны"}}/>
<p>
<b>Прикрепленные файлы:</b>
</p>
{appointmentFormModalUI.draftFiles.length > 0 ? (
<List
dataSource={appointmentFormModalUI.draftFiles}
renderItem={(file) => (
<List.Item>
{file.name} ({(file.size / 1024 / 1024).toFixed(2)} MB)
</List.Item>
)}
/>
) : (
<p>Файлы не прикреплены</p>
)}
</div>
);
}, [appointmentFormModalUI.form, appointmentFormModalData.patients, appointmentFormModalData.appointmentTypes, appointmentFormModalUI.draftFiles, appointmentFormModalUI.blockStepStyle]);
const steps = useMemo(() => [
{
title: "Выбор пациента",
content: SelectPatientStep,
},
{
title: "Заполнение информации о приеме",
content: AppointmentStep,
},
{
title: "Подтверждение",
content: ConfirmStep,
},
], [SelectPatientStep, AppointmentStep, ConfirmStep]);
if (appointmentFormModalData.isError) {
return (
<Result
status="error"
title="Ошибка"
subTitle="Произошла ошибка в работе страницы"
/>
);
}
return (
<>
{appointmentFormModalData.isLoading ? (
<LoadingIndicator/>
) : (
<>
<Modal
title={"Создать прием"}
open={appointmentFormModalUI.modalVisible}
onCancel={appointmentFormModalUI.handleCancel}
footer={null}
width={appointmentFormModalUI.modalWidth}
>
{appointmentFormModalData.isLoading ? (
<div style={appointmentFormModalUI.loadingContainerStyle}>
<Spin size="large"/>
</div>
) : (
<div
style={appointmentFormModalUI.stepsContentStyle}>{steps[appointmentFormModalUI.currentStep].content}</div>
)}
{!appointmentFormModalUI.screenXS && (
<Steps
current={appointmentFormModalUI.currentStep}
items={steps}
style={appointmentFormModalUI.stepsIndicatorStyle}
direction={appointmentFormModalUI.direction}
/>
)}
<Row justify="end" style={appointmentFormModalUI.footerRowStyle} gutter={[8, 8]}>
<Button
style={appointmentFormModalUI.footerButtonStyle}
onClick={appointmentFormModalUI.handleClickBackButton}
disabled={appointmentFormModalUI.disableBackButton}
>
Назад
</Button>
<Button
type="primary"
onClick={appointmentFormModalUI.handleClickNextButton}
disabled={appointmentFormModalUI.disableNextButton}
>
{appointmentFormModalUI.nextButtonText}
</Button>
</Row>
</Modal>
<Drawer
title="Прошлые приемы"
placement="right"
onClose={appointmentFormModalUI.closeDrawer}
open={appointmentFormModalUI.isDrawerVisible}
width={400}
>
<Input
placeholder="Поиск по результатам приема"
value={appointmentFormModalUI.searchPreviousAppointments}
onChange={appointmentFormModalUI.handleSetSearchPreviousAppointments}
style={{marginBottom: 16}}
allowClear
/>
<Collapse
items={appointmentFormModalUI.filteredPreviousAppointments.map((appointment) => ({
key: appointment.id,
label: `Прием ${dayjs(appointment.appointment_datetime).format("DD.MM.YYYY HH:mm")}`,
children: <div
dangerouslySetInnerHTML={{__html: appointment.results || "Результаты не указаны"}}/>,
}))}
/>
</Drawer>
</>
)}
</>
);
};
export default AppointmentFormModal;