сделал просмотр информации о приеме
This commit is contained in:
parent
2a1f6a9989
commit
c2a846f433
@ -1,15 +1,18 @@
|
||||
import { Button, FloatButton, Result, Tabs, Typography } from "antd";
|
||||
import { Splitter } from "antd";
|
||||
import { CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import {Button, FloatButton, Result, Tabs, Typography} from "antd";
|
||||
import {Splitter} from "antd";
|
||||
import {CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined, PlusOutlined} from "@ant-design/icons";
|
||||
import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/AppointmentsCalendarTab.jsx";
|
||||
import AppointmentsTableTab from "./Components/AppointmentTableTab/AppointmentsTableTab.jsx";
|
||||
import useAppointmentsUI from "./useAppointmentsUI.js";
|
||||
import useAppointments from "./useAppointments.js";
|
||||
import dayjs from 'dayjs';
|
||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||
import AppointmentFormModal from "./Components/AppointmentCalendarTab/Components/AppointmentFormModal/AppointmentFormModal.jsx";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { closeModal, openModal } from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import AppointmentFormModal
|
||||
from "./Components/AppointmentFormModal/AppointmentFormModal.jsx";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {closeModal, openModal} from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import AppointmentViewModal
|
||||
from "./Components/AppointmentViewModal/AppointmentViewModal.jsx";
|
||||
|
||||
const AppointmentsPage = () => {
|
||||
const appointmentsData = useAppointments();
|
||||
@ -24,14 +27,14 @@ const AppointmentsPage = () => {
|
||||
{
|
||||
key: "1",
|
||||
label: "Календарь приемов",
|
||||
children: <AppointmentsCalendarTab />,
|
||||
icon: <CalendarOutlined />,
|
||||
children: <AppointmentsCalendarTab/>,
|
||||
icon: <CalendarOutlined/>,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
label: "Таблица приемов",
|
||||
children: <AppointmentsTableTab />,
|
||||
icon: <TableOutlined />,
|
||||
children: <AppointmentsTableTab/>,
|
||||
icon: <TableOutlined/>,
|
||||
},
|
||||
];
|
||||
|
||||
@ -46,7 +49,7 @@ const AppointmentsPage = () => {
|
||||
return (
|
||||
<>
|
||||
{appointmentsData.isLoading ? (
|
||||
<LoadingIndicator />
|
||||
<LoadingIndicator/>
|
||||
) : (
|
||||
<>
|
||||
<Splitter
|
||||
@ -62,7 +65,7 @@ const AppointmentsPage = () => {
|
||||
min="25%"
|
||||
max="90%"
|
||||
>
|
||||
<Tabs defaultActiveKey="1" items={items} />
|
||||
<Tabs defaultActiveKey="1" items={items}/>
|
||||
</Splitter.Panel>
|
||||
|
||||
{appointmentsPageUI.showSplitterPanel && (
|
||||
@ -100,7 +103,7 @@ const AppointmentsPage = () => {
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={appointmentsPageUI.handleToggleSider}
|
||||
icon={appointmentsPageUI.collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
icon={appointmentsPageUI.collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
|
||||
style={appointmentsPageUI.siderButtonStyle}
|
||||
>
|
||||
{appointmentsPageUI.siderButtonText}
|
||||
@ -109,16 +112,16 @@ const AppointmentsPage = () => {
|
||||
<FloatButton.Group
|
||||
trigger="hover"
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
icon={<PlusOutlined/>}
|
||||
tooltip="Создать"
|
||||
>
|
||||
<FloatButton
|
||||
icon={<PlusOutlined />}
|
||||
icon={<PlusOutlined/>}
|
||||
onClick={() => dispatch(openModal())}
|
||||
tooltip="Прием"
|
||||
/>
|
||||
<FloatButton
|
||||
icon={<CalendarOutlined />}
|
||||
icon={<CalendarOutlined/>}
|
||||
onClick={appointmentsPageUI.openCreateScheduledAppointmentModal}
|
||||
tooltip="Запланированный прием"
|
||||
/>
|
||||
@ -128,6 +131,11 @@ const AppointmentsPage = () => {
|
||||
visible={appointmentsPageUI.modalVisible}
|
||||
onCancel={handleCancelModal}
|
||||
/>
|
||||
|
||||
<AppointmentViewModal
|
||||
visible={appointmentsPageUI.selectedAppointment !== null}
|
||||
onCancel={appointmentsPageUI.handleCancelViewModal}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {Calendar, Modal, Form, Input, DatePicker, Button} from "antd";
|
||||
import {Calendar} from "antd";
|
||||
import 'dayjs/locale/ru';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
@ -6,12 +6,6 @@ import timezone from 'dayjs/plugin/timezone';
|
||||
import CalendarCell from "../../../../Widgets/CalendarCell.jsx";
|
||||
import useAppointments from "../../useAppointments.js";
|
||||
import useAppointmentCalendarUI from "./useAppointmentCalendarUI.js";
|
||||
import {
|
||||
closeModal,
|
||||
openModal,
|
||||
setSelectedAppointment,
|
||||
setSelectedScheduledAppointment,
|
||||
} from "../../../../../Redux/Slices/appointmentsSlice.js"
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
@ -40,14 +34,7 @@ const AppointmentsCalendarTab = () => {
|
||||
appointments={appointmentsForDate}
|
||||
scheduledAppointments={scheduledForDate}
|
||||
onCellClick={() => appointmentsCalendarUI.onSelect(value)}
|
||||
onItemClick={(appointment) => {
|
||||
if (appointment.appointment_datetime) {
|
||||
dispatch(setSelectedAppointment(appointment));
|
||||
} else {
|
||||
dispatch(setSelectedScheduledAppointment(appointment));
|
||||
}
|
||||
dispatch(openModal());
|
||||
}}
|
||||
onItemClick={appointmentsCalendarUI.onOpenAppointmentModal}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -53,91 +53,14 @@ const useAppointmentCalendarUI = (appointments, scheduledAppointments) => {
|
||||
dispatch(openModal());
|
||||
};
|
||||
|
||||
const onFinish = async (values) => {
|
||||
try {
|
||||
const appointmentTime = values.appointmentTime;
|
||||
const isScheduled = !(selectedAppointment?.appointment_datetime);
|
||||
|
||||
const conflictingAppointments = isScheduled
|
||||
? scheduledAppointments
|
||||
: appointments;
|
||||
const hasConflict = conflictingAppointments.some(app =>
|
||||
dayjs(app.appointment_datetime || app.scheduled_datetime)
|
||||
.tz('Europe/Moscow')
|
||||
.isSame(appointmentTime, 'minute')
|
||||
);
|
||||
|
||||
if (hasConflict) {
|
||||
notification.error({
|
||||
message: "Выбранное время уже занято",
|
||||
description: "Выбранное время уже занято",
|
||||
placement: "topRight",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
patient: {
|
||||
last_name: values.patientName.split(' ')[0] || '',
|
||||
first_name: values.patientName.split(' ')[1] || '',
|
||||
},
|
||||
...(isScheduled
|
||||
? { scheduled_datetime: appointmentTime.toISOString() }
|
||||
: { appointment_datetime: appointmentTime.toISOString() }),
|
||||
reason: values.reason,
|
||||
};
|
||||
|
||||
if (selectedAppointment || selectedScheduledAppointment) {
|
||||
if (selectedAppointment?.appointment_datetime) {
|
||||
await updateAppointment({ id: selectedAppointment.id, data }).unwrap();
|
||||
notification.success({
|
||||
message: "Прием успешно обновлен",
|
||||
description: "Прием успешно обновлен",
|
||||
placement: "topRight",
|
||||
});
|
||||
} else {
|
||||
await updateScheduledAppointment({ id: selectedScheduledAppointment.id, data }).unwrap();
|
||||
notification.success({
|
||||
message: "Запланированный прием успешно обновлен",
|
||||
description: "Запланированный прием успешно обновлен",
|
||||
placement: "topRight",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (isScheduled) {
|
||||
await createScheduledAppointment(data).unwrap();
|
||||
notification.success({
|
||||
message: "Запланированный прием успешно создан",
|
||||
description: "Запланированный прием успешно создан",
|
||||
placement: "topRight",
|
||||
});
|
||||
} else {
|
||||
await createAppointment(data).unwrap();
|
||||
notification.success({
|
||||
message: "Прием успешно создан",
|
||||
description: "Прием успешно создан",
|
||||
placement: "topRight",
|
||||
});
|
||||
}
|
||||
}
|
||||
dispatch(closeModal());
|
||||
form.resetFields();
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: "Ошибка при сохранении приема",
|
||||
description: error.data?.message || "Не удалось сохранить прием",
|
||||
placement: "topRight",
|
||||
});
|
||||
const onOpenAppointmentModal = (appointment) => {
|
||||
if (appointment.appointment_datetime) {
|
||||
dispatch(setSelectedAppointment(appointment));
|
||||
} else {
|
||||
dispatch(setSelectedScheduledAppointment(appointment));
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateAppointment = () => {
|
||||
dispatch(setSelectedAppointment(null));
|
||||
dispatch(setSelectedScheduledAppointment(null));
|
||||
form.resetFields();
|
||||
dispatch(openModal());
|
||||
};
|
||||
|
||||
const getAppointmentsByListAndDate = (list, value, isScheduled = false) => {
|
||||
const date = value.tz('Europe/Moscow').format('YYYY-MM-DD');
|
||||
return list.filter(app =>
|
||||
@ -153,6 +76,7 @@ const useAppointmentCalendarUI = (appointments, scheduledAppointments) => {
|
||||
calendarContainerStyle,
|
||||
onSelect,
|
||||
getAppointmentsByListAndDate,
|
||||
onOpenAppointmentModal,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
} from "antd";
|
||||
import useAppointmentFormModal from "./useAppointmentFormModal.js";
|
||||
import useAppointmentFormModalUI from "./useAppointmentFormModalUI.js";
|
||||
import LoadingIndicator from "../../../../../../Widgets/LoadingIndicator.jsx";
|
||||
import LoadingIndicator from "../../../../Widgets/LoadingIndicator.jsx";
|
||||
import {useMemo} from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {useGetPatientsQuery} from "../../../../../../../Api/patientsApi.js";
|
||||
import {useGetAppointmentTypesQuery} from "../../../../../../../Api/appointmentTypesApi.js";
|
||||
import {useCreateAppointmentMutation, useUpdateAppointmentMutation} from "../../../../../../../Api/appointmentsApi.js";
|
||||
import {useGetPatientsQuery} from "../../../../../Api/patientsApi.js";
|
||||
import {useGetAppointmentTypesQuery} from "../../../../../Api/appointmentTypesApi.js";
|
||||
import {useCreateAppointmentMutation, useUpdateAppointmentMutation} from "../../../../../Api/appointmentsApi.js";
|
||||
|
||||
const useAppointmentFormModal = () => {
|
||||
const {
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, notification} from "antd";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {closeModal} from "../../../../../../../Redux/Slices/appointmentsSlice.js";
|
||||
import {closeModal} from "../../../../../Redux/Slices/appointmentsSlice.js";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import dayjs from "dayjs";
|
||||
import {useGetAppointmentsQuery} from "../../../../../../../Api/appointmentsApi.js";
|
||||
import {useGetAppointmentsQuery} from "../../../../../Api/appointmentsApi.js";
|
||||
import {Grid} from "antd";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
@ -0,0 +1,72 @@
|
||||
import {Button, Modal, Row, Typography} from "antd";
|
||||
import useAppointmentViewUI from "./useAppointmentViewUI.js";
|
||||
import PropTypes from "prop-types";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
|
||||
const AppointmentViewModal = ({visible, onCancel}) => {
|
||||
const appointmentViewModalUI = useAppointmentViewUI(visible, onCancel);
|
||||
|
||||
if (!appointmentViewModalUI.selectedAppointment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="Просмотр приема"
|
||||
open={visible}
|
||||
onCancel={onCancel}
|
||||
footer={null}
|
||||
width={appointmentViewModalUI.modalWidth}
|
||||
>
|
||||
<div style={appointmentViewModalUI.blockStyle}>
|
||||
<Typography.Title level={4}>Информация о приеме</Typography.Title>
|
||||
<p>
|
||||
<b>Пациент:</b>{" "}
|
||||
{appointmentViewModalUI.selectedAppointment.patient ? `${appointmentViewModalUI.selectedAppointment.patient.last_name} ${appointmentViewModalUI.selectedAppointment.patient.first_name}` : "Не указан"}
|
||||
</p>
|
||||
<p>
|
||||
<b>Дата рождения:</b>{" "}
|
||||
{appointmentViewModalUI.selectedAppointment.patient ? appointmentViewModalUI.getDateString(appointmentViewModalUI.selectedAppointment.patient.birthday) : "Не указан"}
|
||||
</p>
|
||||
<p>
|
||||
<b>Email:</b> {appointmentViewModalUI.selectedAppointment.patient?.email || "Не указан"}
|
||||
</p>
|
||||
<p>
|
||||
<b>Телефон:</b> {appointmentViewModalUI.selectedAppointment.patient?.phone || "Не указан"}
|
||||
</p>
|
||||
<p>
|
||||
<b>Тип приема:</b> {appointmentViewModalUI.selectedAppointment.type?.title || "Не указан"}
|
||||
</p>
|
||||
<p>
|
||||
<b>Время приема:</b>{" "}
|
||||
{appointmentViewModalUI.selectedAppointment.appointment_datetime
|
||||
? dayjs(appointmentViewModalUI.selectedAppointment.appointment_datetime).format("DD.MM.YYYY HH:mm")
|
||||
: "Не указано"}
|
||||
</p>
|
||||
<p>
|
||||
<b>Дней до следующего приема:</b>{" "}
|
||||
{appointmentViewModalUI.selectedAppointment.days_until_the_next_appointment || "Не указано"}
|
||||
</p>
|
||||
<p>
|
||||
<b>Результаты приема:</b>
|
||||
</p>
|
||||
<div dangerouslySetInnerHTML={{__html: appointmentViewModalUI.selectedAppointment.results || "Не указаны"}}/>
|
||||
</div>
|
||||
<Row justify="end" style={appointmentViewModalUI.footerRowStyle}>
|
||||
<Button style={appointmentViewModalUI.footerButtonStyle} onClick={onCancel}>
|
||||
Закрыть
|
||||
</Button>
|
||||
</Row>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
AppointmentViewModal.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AppointmentViewModal;
|
||||
@ -0,0 +1,29 @@
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
|
||||
const useAppointmentViewUI = () => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
selectedAppointment,
|
||||
} = useSelector(state => state.appointmentsUI);
|
||||
|
||||
const modalWidth = 700;
|
||||
const blockStyle = {marginBottom: 16};
|
||||
const footerRowStyle = {marginTop: 16};
|
||||
const footerButtonStyle = {marginRight: 8};
|
||||
|
||||
|
||||
const getDateString = (date) => {
|
||||
return new Date(date).toLocaleDateString('ru-RU');
|
||||
};
|
||||
|
||||
return {
|
||||
modalWidth,
|
||||
blockStyle,
|
||||
footerRowStyle,
|
||||
footerButtonStyle,
|
||||
selectedAppointment,
|
||||
getDateString,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAppointmentViewUI;
|
||||
@ -1,6 +1,11 @@
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Grid } from "antd";
|
||||
import { setHovered, toggleSider } from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import {
|
||||
setHovered,
|
||||
setSelectedAppointment,
|
||||
setSelectedScheduledAppointment,
|
||||
toggleSider
|
||||
} from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
@ -12,6 +17,7 @@ const useAppointmentsUI = (appointments, scheduledAppointments) => {
|
||||
collapsed,
|
||||
siderWidth,
|
||||
hovered,
|
||||
selectedAppointment,
|
||||
} = useSelector(state => state.appointmentsUI);
|
||||
const screens = useBreakpoint();
|
||||
|
||||
@ -45,6 +51,14 @@ const useAppointmentsUI = (appointments, scheduledAppointments) => {
|
||||
const handleHoverSider = () => dispatch(setHovered(true));
|
||||
const handleLeaveSider = () => dispatch(setHovered(false));
|
||||
|
||||
const handleCancelViewModal = () => {
|
||||
if (selectedAppointment) {
|
||||
dispatch(setSelectedAppointment(null));
|
||||
} else {
|
||||
dispatch(setSelectedScheduledAppointment(null));
|
||||
}
|
||||
};
|
||||
|
||||
const openCreateScheduledAppointmentModal = () => {
|
||||
// Логика для запланированных приемов будет добавлена позже
|
||||
console.log('Открыть модальное окно для запланированного приема');
|
||||
@ -71,6 +85,8 @@ const useAppointmentsUI = (appointments, scheduledAppointments) => {
|
||||
siderButtonContainerStyle,
|
||||
siderButtonStyle,
|
||||
upcomingEvents,
|
||||
selectedAppointment,
|
||||
handleCancelViewModal,
|
||||
handleToggleSider,
|
||||
handleHoverSider,
|
||||
handleLeaveSider,
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const initialState = {
|
||||
collapsed: true,
|
||||
siderWidth: 250,
|
||||
hovered: false,
|
||||
modalVisible: false,
|
||||
selectedAppointments: [],
|
||||
selectedAppointment: null,
|
||||
scheduledAppointments: [],
|
||||
selectedScheduledAppointment: null,
|
||||
@ -34,9 +32,6 @@ const appointmentsSlice = createSlice({
|
||||
closeModal: (state) => {
|
||||
state.modalVisible = false;
|
||||
},
|
||||
setSelectedAppointments: (state, action) => {
|
||||
state.selectedAppointments = action.payload;
|
||||
},
|
||||
setSelectedAppointment: (state, action) => {
|
||||
state.selectedAppointment = action.payload;
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user