сделал просмотр результатов прошлых приемов при создании нового
This commit is contained in:
parent
b541bef7aa
commit
a9c5f87104
@ -40,7 +40,7 @@ async def get_all_appointments_by_doctor_id(
|
||||
|
||||
@router.get(
|
||||
"/patient/{patient_id}/",
|
||||
response_model=AppointmentEntity,
|
||||
response_model=list[AppointmentEntity],
|
||||
summary="Get all appointments for patient",
|
||||
description="Returns a list of appointments for patient",
|
||||
)
|
||||
|
||||
@ -18,6 +18,11 @@ export const appointmentsApi = createApi({
|
||||
providesTags: ['Appointment'],
|
||||
refetchOnMountOrArgChange: 5,
|
||||
}),
|
||||
getByPatientId: builder.query({
|
||||
query: (id) => `/appointments/patient/${id}/`,
|
||||
providesTags: ['Appointment'],
|
||||
refetchOnMountOrArgChange: 5,
|
||||
}),
|
||||
createAppointment: builder.mutation({
|
||||
query: (data) => ({
|
||||
url: '/appointments/',
|
||||
@ -39,6 +44,7 @@ export const appointmentsApi = createApi({
|
||||
|
||||
export const {
|
||||
useGetAppointmentsQuery,
|
||||
useGetByPatientIdQuery,
|
||||
useCreateAppointmentMutation,
|
||||
useUpdateAppointmentMutation,
|
||||
} = appointmentsApi;
|
||||
@ -1,18 +1,29 @@
|
||||
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, List, Result, Space, Typography} from "antd";
|
||||
import {Splitter} from "antd";
|
||||
import {
|
||||
CalendarOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
PlusOutlined,
|
||||
ClockCircleOutlined
|
||||
} 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/AppointmentFormModal/AppointmentFormModal.jsx";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { closeModal, openModal } from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {
|
||||
closeModal,
|
||||
openModal,
|
||||
setSelectedAppointment,
|
||||
setSelectedScheduledAppointment
|
||||
} from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import AppointmentViewModal from "./Components/AppointmentViewModal/AppointmentViewModal.jsx";
|
||||
import ScheduledAppointmentFormModal from "./Components/ScheduledAppintmentFormModal/ScheduledAppointmentFormModal.jsx";
|
||||
import ScheduledAppointmentsViewModal from "./Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx";
|
||||
import ScheduledAppointmentsViewModal
|
||||
from "./Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx";
|
||||
import AppointmentsListModal from "./Components/AppointmentsListModal/AppointmentsListModal.jsx";
|
||||
|
||||
const AppointmentsPage = () => {
|
||||
@ -24,20 +35,13 @@ const AppointmentsPage = () => {
|
||||
dispatch(closeModal());
|
||||
};
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: "1",
|
||||
label: "Календарь приемов",
|
||||
children: <AppointmentsCalendarTab />,
|
||||
icon: <CalendarOutlined />,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
label: "Таблица приемов",
|
||||
children: <AppointmentsTableTab />,
|
||||
icon: <TableOutlined />,
|
||||
},
|
||||
];
|
||||
const handleEventClick = (event) => {
|
||||
if (event.appointment_datetime) {
|
||||
dispatch(setSelectedAppointment(event));
|
||||
} else {
|
||||
dispatch(setSelectedScheduledAppointment(event));
|
||||
}
|
||||
};
|
||||
|
||||
if (appointmentsData.isError) return (
|
||||
<Result
|
||||
@ -50,7 +54,7 @@ const AppointmentsPage = () => {
|
||||
return (
|
||||
<>
|
||||
{appointmentsData.isLoading ? (
|
||||
<LoadingIndicator />
|
||||
<LoadingIndicator/>
|
||||
) : (
|
||||
<>
|
||||
<Splitter
|
||||
@ -66,7 +70,7 @@ const AppointmentsPage = () => {
|
||||
min="25%"
|
||||
max="90%"
|
||||
>
|
||||
<Tabs defaultActiveKey="1" items={items} />
|
||||
<AppointmentsCalendarTab/>
|
||||
</Splitter.Panel>
|
||||
|
||||
{appointmentsPageUI.showSplitterPanel && (
|
||||
@ -80,17 +84,53 @@ const AppointmentsPage = () => {
|
||||
Предстоящие события
|
||||
</Typography.Title>
|
||||
{appointmentsPageUI.upcomingEvents.length ? (
|
||||
<ul>
|
||||
{appointmentsPageUI.upcomingEvents.map((app) => (
|
||||
<li key={app.id}>
|
||||
{dayjs(app.appointment_datetime || app.scheduled_datetime)
|
||||
.format('DD.MM.YYYY HH:mm')} -
|
||||
{app.appointment_datetime ? 'Прием' : 'Запланировано'}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<List
|
||||
dataSource={appointmentsPageUI.upcomingEvents.sort((a, b) =>
|
||||
dayjs(a.appointment_datetime || a.scheduled_datetime).diff(
|
||||
dayjs(b.appointment_datetime || b.scheduled_datetime)
|
||||
)
|
||||
)}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
onClick={() => handleEventClick(item)}
|
||||
style={{
|
||||
padding: "12px",
|
||||
marginBottom: "8px",
|
||||
borderRadius: "4px",
|
||||
background: item.appointment_datetime ? "#e6f7ff" : "#f6ffed",
|
||||
cursor: "pointer",
|
||||
transition: "background 0.3s",
|
||||
}}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.background = item.appointment_datetime ? "#d9efff" : "#efffdb")}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.background = item.appointment_datetime ? "#e6f7ff" : "#f6ffed")}
|
||||
>
|
||||
<Space direction="vertical" size={2}>
|
||||
<Space>
|
||||
{item.appointment_datetime ? (
|
||||
<ClockCircleOutlined style={{color: "#1890ff"}}/>
|
||||
) : (
|
||||
<CalendarOutlined style={{color: "#52c41a"}}/>
|
||||
)}
|
||||
<Typography.Text strong>
|
||||
{dayjs(item.appointment_datetime || item.scheduled_datetime).format('DD.MM.YYYY HH:mm')}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Typography.Text>
|
||||
{item.appointment_datetime ? 'Прием' : 'Запланировано'}
|
||||
{item.patient ? ` - ${item.patient.last_name} ${item.patient.first_name}` : ''}
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary">
|
||||
Тип: {item.type?.title || 'Не указан'}
|
||||
</Typography.Text>
|
||||
{dayjs(item.appointment_datetime || item.scheduled_datetime).isSame(dayjs(), 'day') && (
|
||||
<Typography.Text type="warning">Сегодня</Typography.Text>
|
||||
)}
|
||||
</Space>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<p>Нет предстоящих событий</p>
|
||||
<Typography.Text type="secondary">Нет предстоящих событий</Typography.Text>
|
||||
)}
|
||||
</Splitter.Panel>
|
||||
)}
|
||||
@ -103,7 +143,7 @@ const AppointmentsPage = () => {
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={appointmentsPageUI.handleToggleSider}
|
||||
icon={appointmentsPageUI.collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
icon={appointmentsPageUI.collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
|
||||
style={appointmentsPageUI.siderButtonStyle}
|
||||
>
|
||||
{appointmentsPageUI.siderButtonText}
|
||||
@ -113,29 +153,29 @@ const AppointmentsPage = () => {
|
||||
placement={"left"}
|
||||
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="Запланированный прием"
|
||||
/>
|
||||
</FloatButton.Group>
|
||||
|
||||
<AppointmentFormModal onCancel={handleCancelModal} />
|
||||
<AppointmentFormModal onCancel={handleCancelModal}/>
|
||||
<AppointmentViewModal
|
||||
visible={appointmentsPageUI.selectedAppointment !== null}
|
||||
onCancel={appointmentsPageUI.handleCancelViewModal}
|
||||
/>
|
||||
<ScheduledAppointmentFormModal />
|
||||
<ScheduledAppointmentsViewModal />
|
||||
<AppointmentsListModal />
|
||||
<ScheduledAppointmentFormModal/>
|
||||
<ScheduledAppointmentsViewModal/>
|
||||
<AppointmentsListModal/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -4,6 +4,7 @@ import CalendarCell from "../../../../Widgets/CalendarCell.jsx";
|
||||
import useAppointments from "../../useAppointments.js";
|
||||
import useAppointmentCalendarUI from "./useAppointmentCalendarUI.js";
|
||||
import AppointmentsListModal from "../AppointmentsListModal/AppointmentsListModal.jsx";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const AppointmentsCalendarTab = () => {
|
||||
const appointmentsData = useAppointments();
|
||||
@ -23,10 +24,15 @@ const AppointmentsCalendarTab = () => {
|
||||
true
|
||||
);
|
||||
|
||||
const allAppointments = [...appointmentsForDate, ...scheduledForDate].sort((a, b) => {
|
||||
const timeA = a.appointment_datetime || a.scheduled_datetime;
|
||||
const timeB = b.appointment_datetime || b.scheduled_datetime;
|
||||
return dayjs(timeA).diff(dayjs(timeB));
|
||||
});
|
||||
|
||||
return (
|
||||
<CalendarCell
|
||||
appointments={appointmentsForDate}
|
||||
scheduledAppointments={scheduledForDate}
|
||||
allAppointments={allAppointments}
|
||||
onCellClick={() => appointmentsCalendarUI.onSelect(value)}
|
||||
onItemClick={appointmentsCalendarUI.onOpenAppointmentModal}
|
||||
/>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import JoditEditor from 'jodit-react';
|
||||
import {useRef} from 'react';
|
||||
import JoditEditor from "jodit-react";
|
||||
import { useRef } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
Button,
|
||||
@ -14,36 +14,47 @@ import {
|
||||
Select,
|
||||
Spin,
|
||||
Steps,
|
||||
Typography
|
||||
Typography,
|
||||
Drawer,
|
||||
} from "antd";
|
||||
import useAppointmentFormModal from "./useAppointmentFormModal.js";
|
||||
import useAppointmentFormModalUI from "./useAppointmentFormModalUI.js";
|
||||
import LoadingIndicator from "../../../../Widgets/LoadingIndicator.jsx";
|
||||
import {useMemo} from "react";
|
||||
import { useMemo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const AppointmentFormModal = ({onCancel}) => {
|
||||
const AppointmentFormModal = ({ onCancel }) => {
|
||||
const appointmentFormModalData = useAppointmentFormModal();
|
||||
const appointmentFormModalUI = useAppointmentFormModalUI(
|
||||
onCancel,
|
||||
appointmentFormModalData.createAppointment,
|
||||
appointmentFormModalData.patients,
|
||||
appointmentFormModalData.cancelAppointment,
|
||||
appointmentFormModalData.useGetByPatientIdQuery
|
||||
);
|
||||
|
||||
const editor = useRef(null);
|
||||
|
||||
|
||||
const patientsItems = 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>
|
||||
<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>
|
||||
@ -57,14 +68,16 @@ const AppointmentFormModal = ({onCancel}) => {
|
||||
<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
|
||||
>
|
||||
<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>
|
||||
@ -78,7 +91,7 @@ const AppointmentFormModal = ({onCancel}) => {
|
||||
allowClear
|
||||
/>
|
||||
<div style={appointmentFormModalUI.chooseContainerStyle}>
|
||||
<Collapse items={patientsItems}/>
|
||||
<Collapse items={patientsItems} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
@ -86,95 +99,107 @@ const AppointmentFormModal = ({onCancel}) => {
|
||||
|
||||
const AppointmentStep = useMemo(() => {
|
||||
return (
|
||||
<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: 'Выберите тип приема'}]}
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={appointmentFormModalUI.showDrawer}
|
||||
style={{ marginBottom: 16 }}
|
||||
disabled={!appointmentFormModalUI.selectedPatient}
|
||||
>
|
||||
<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: 'Выберите время'}]}
|
||||
Показать прошлые приемы
|
||||
</Button>
|
||||
<Form
|
||||
form={appointmentFormModalUI.form}
|
||||
onFinish={appointmentFormModalUI.handleOk}
|
||||
initialValues={{
|
||||
patient_id: appointmentFormModalUI.selectedPatient?.id,
|
||||
}}
|
||||
layout="vertical"
|
||||
>
|
||||
<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="Результаты приема"
|
||||
>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={appointmentFormModalUI.results}
|
||||
config={{
|
||||
readonly: false,
|
||||
height: 150,
|
||||
}}
|
||||
onBlur={appointmentFormModalUI.handleResultsChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
<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="Результаты приема">
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={appointmentFormModalUI.results}
|
||||
config={{
|
||||
readonly: false,
|
||||
height: 150,
|
||||
}}
|
||||
onBlur={appointmentFormModalUI.handleResultsChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}, [
|
||||
appointmentFormModalData,
|
||||
appointmentFormModalUI,
|
||||
]);
|
||||
}, [appointmentFormModalData, appointmentFormModalUI]);
|
||||
|
||||
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);
|
||||
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.name : 'Не выбран'}</p>
|
||||
<p><b>Время
|
||||
приема:</b> {values.appointment_datetime ? dayjs(values.appointment_datetime).format('DD.MM.YYYY HH:mm') : 'Не указано'}
|
||||
<p>
|
||||
<b>Пациент:</b> {patient ? `${patient.last_name} ${patient.first_name}` : "Не выбран"}
|
||||
</p>
|
||||
<p><b>Дней до следующего приема:</b> {values.days_until_the_next_appointment || 'Не указано'}</p>
|
||||
<p><b>Результаты приема:</b></p>
|
||||
<div dangerouslySetInnerHTML={{__html: values.results || 'Не указаны'}}/>
|
||||
<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 || "Не указаны" }} />
|
||||
</div>
|
||||
);
|
||||
}, [appointmentFormModalUI, appointmentFormModalData]);
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: 'Выбор пациента',
|
||||
title: "Выбор пациента",
|
||||
content: SelectPatientStep,
|
||||
},
|
||||
{
|
||||
title: 'Заполнение информации о приеме',
|
||||
title: "Заполнение информации о приеме",
|
||||
content: AppointmentStep,
|
||||
},
|
||||
{
|
||||
title: 'Подтверждение',
|
||||
title: "Подтверждение",
|
||||
content: ConfirmStep,
|
||||
},
|
||||
];
|
||||
@ -192,55 +217,73 @@ const AppointmentFormModal = ({onCancel}) => {
|
||||
return (
|
||||
<>
|
||||
{appointmentFormModalData.isLoading ? (
|
||||
<LoadingIndicator/>
|
||||
<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]}
|
||||
<>
|
||||
<Modal
|
||||
title={"Создать прием"}
|
||||
open={appointmentFormModalUI.modalVisible}
|
||||
onCancel={appointmentFormModalUI.handleCancel}
|
||||
footer={null}
|
||||
width={appointmentFormModalUI.modalWidth}
|
||||
>
|
||||
<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>
|
||||
{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>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import {useGetPatientsQuery} from "../../../../../Api/patientsApi.js";
|
||||
import {useGetAppointmentTypesQuery} from "../../../../../Api/appointmentTypesApi.js";
|
||||
import {useCreateAppointmentMutation, useUpdateAppointmentMutation} from "../../../../../Api/appointmentsApi.js";
|
||||
import {useCancelScheduledAppointmentMutation} from "../../../../../Api/scheduledAppointmentsApi.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";
|
||||
|
||||
const useAppointmentFormModal = () => {
|
||||
const {
|
||||
@ -19,7 +22,7 @@ const useAppointmentFormModal = () => {
|
||||
pollingInterval: 20000,
|
||||
});
|
||||
|
||||
const [createAppointment, {isLoading: isCreating, isError: isErrorCreating}] = useCreateAppointmentMutation();
|
||||
const [createAppointment, { isLoading: isCreating, isError: isErrorCreating }] = useCreateAppointmentMutation();
|
||||
const [cancelAppointment] = useCancelScheduledAppointmentMutation();
|
||||
|
||||
return {
|
||||
@ -27,6 +30,7 @@ const useAppointmentFormModal = () => {
|
||||
appointmentTypes,
|
||||
createAppointment,
|
||||
cancelAppointment,
|
||||
useGetByPatientIdQuery,
|
||||
isLoading: isLoadingPatients || isLoadingAppointmentTypes || isCreating,
|
||||
isError: isErrorPatients || isErrorAppointmentTypes || isErrorCreating,
|
||||
};
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import {Form, notification} from "antd";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {closeModal, setSelectedScheduledAppointment} from "../../../../../Redux/Slices/appointmentsSlice.js";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import { Form, notification } from "antd";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { closeModal, setSelectedScheduledAppointment } from "../../../../../Redux/Slices/appointmentsSlice.js";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import {useGetAppointmentsQuery} from "../../../../../Api/appointmentsApi.js";
|
||||
import {Grid} from "antd";
|
||||
import { useGetAppointmentsQuery } from "../../../../../Api/appointmentsApi.js";
|
||||
import { Grid } from "antd";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancelAppointment) => {
|
||||
const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancelAppointment, useGetByPatientIdQuery) => {
|
||||
const dispatch = useDispatch();
|
||||
const {modalVisible, scheduledData} = useSelector(state => state.appointmentsUI);
|
||||
const { modalVisible, scheduledData } = useSelector((state) => state.appointmentsUI);
|
||||
const [form] = Form.useForm();
|
||||
const screens = useBreakpoint();
|
||||
|
||||
@ -19,31 +19,54 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
const [appointmentDate, setAppointmentDate] = useState(dayjs(new Date()));
|
||||
const [searchPatientString, setSearchPatientString] = useState("");
|
||||
const [formValues, setFormValues] = useState({});
|
||||
const [results, setResults] = useState('');
|
||||
const [results, setResults] = useState("");
|
||||
const [isDrawerVisible, setIsDrawerVisible] = useState(false);
|
||||
const [searchPreviousAppointments, setSearchPreviousAppointments] = useState("");
|
||||
|
||||
const {data: appointments = []} = useGetAppointmentsQuery(undefined, {
|
||||
const { data: appointments = [] } = useGetAppointmentsQuery(undefined, {
|
||||
pollingInterval: 20000,
|
||||
});
|
||||
|
||||
const blockStepStyle = {marginBottom: 16};
|
||||
const searchInputStyle = {marginBottom: 16};
|
||||
const chooseContainerStyle = {maxHeight: 400, overflowY: "auto"};
|
||||
const loadingContainerStyle = {display: "flex", justifyContent: "center", alignItems: "center", height: 200};
|
||||
const stepsContentStyle = {marginBottom: 16};
|
||||
const stepsIndicatorStyle = {marginBottom: 16};
|
||||
const footerRowStyle = {marginTop: 16};
|
||||
const footerButtonStyle = {marginRight: 8};
|
||||
const {
|
||||
data: previousAppointments = [],
|
||||
isLoading: isLoadingPreviousAppointments,
|
||||
isError: isErrorPreviousAppointments,
|
||||
} = useGetByPatientIdQuery(selectedPatient?.id, {
|
||||
pollingInterval: 20000,
|
||||
skip: !selectedPatient,
|
||||
});
|
||||
|
||||
const blockStepStyle = { marginBottom: 16 };
|
||||
const searchInputStyle = { marginBottom: 16 };
|
||||
const chooseContainerStyle = { maxHeight: 400, overflowY: "auto" };
|
||||
const loadingContainerStyle = { display: "flex", justifyContent: "center", alignItems: "center", height: 200 };
|
||||
const stepsContentStyle = { marginBottom: 16 };
|
||||
const stepsIndicatorStyle = { marginBottom: 16 };
|
||||
const footerRowStyle = { marginTop: 16 };
|
||||
const footerButtonStyle = { marginRight: 8 };
|
||||
|
||||
const screenXS = !screens.sm;
|
||||
const direction = screenXS ? "vertical" : "horizontal";
|
||||
|
||||
const filteredPatients = useMemo(() => patients.filter((patient) => {
|
||||
const searchLower = searchPatientString.toLowerCase();
|
||||
const filteredPatients = useMemo(
|
||||
() =>
|
||||
patients.filter((patient) => {
|
||||
const searchLower = searchPatientString.toLowerCase();
|
||||
return Object.values(patient)
|
||||
.filter((value) => typeof value === "string")
|
||||
.some((value) => value.toLowerCase().includes(searchLower));
|
||||
}),
|
||||
[patients, searchPatientString]
|
||||
);
|
||||
|
||||
return Object.values(patient)
|
||||
.filter(value => typeof value === "string")
|
||||
.some(value => value.toLowerCase().includes(searchLower));
|
||||
}), [patients, searchPatientString]);
|
||||
const filteredPreviousAppointments = useMemo(
|
||||
() =>
|
||||
previousAppointments.filter((appointment) => {
|
||||
const searchLower = searchPreviousAppointments.toLowerCase();
|
||||
return appointment.results?.toLowerCase().includes(searchLower);
|
||||
}),
|
||||
[previousAppointments, searchPreviousAppointments]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (modalVisible) {
|
||||
@ -52,9 +75,11 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
setCurrentStep(0);
|
||||
setSearchPatientString("");
|
||||
setFormValues({});
|
||||
setIsDrawerVisible(false);
|
||||
setSearchPreviousAppointments("");
|
||||
|
||||
if (scheduledData) {
|
||||
const patient = patients.find(p => p.id === scheduledData.patient_id);
|
||||
const patient = patients.find((p) => p.id === scheduledData.patient_id);
|
||||
if (patient) {
|
||||
setSelectedPatient(patient);
|
||||
setCurrentStep(1); // Skip to appointment details step
|
||||
@ -80,33 +105,46 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
setSearchPatientString(e.target.value);
|
||||
};
|
||||
|
||||
const handleSetSearchPreviousAppointments = (e) => {
|
||||
setSearchPreviousAppointments(e.target.value);
|
||||
};
|
||||
|
||||
const getDateString = (date) => {
|
||||
return date ? dayjs(date).format('DD.MM.YYYY') : 'Не указано';
|
||||
return date ? dayjs(date).format("DD.MM.YYYY") : "Не указано";
|
||||
};
|
||||
|
||||
const getSelectedPatientBirthdayString = () => {
|
||||
return selectedPatient ? getDateString(selectedPatient.birthday) : 'Не выбран';
|
||||
return selectedPatient ? getDateString(selectedPatient.birthday) : "Не выбран";
|
||||
};
|
||||
|
||||
const resetPatient = () => {
|
||||
setSelectedPatient(null);
|
||||
form.setFieldsValue({patient_id: undefined});
|
||||
form.setFieldsValue({ patient_id: undefined });
|
||||
};
|
||||
|
||||
const handleSetAppointmentDate = (date) => setAppointmentDate(date);
|
||||
const modalWidth = useMemo(() => screenXS ? 700 : "90%", [screenXS]);
|
||||
const modalWidth = useMemo(() => (screenXS ? 700 : "90%"), [screenXS]);
|
||||
|
||||
const showDrawer = () => {
|
||||
setIsDrawerVisible(true);
|
||||
};
|
||||
|
||||
const closeDrawer = () => {
|
||||
setIsDrawerVisible(false);
|
||||
setSearchPreviousAppointments(""); // Reset search on close
|
||||
};
|
||||
|
||||
const handleClickNextButton = async () => {
|
||||
if (currentStep === 0) {
|
||||
if (!selectedPatient) {
|
||||
notification.error({
|
||||
message: 'Ошибка',
|
||||
description: 'Пожалуйста, выберите пациента.',
|
||||
placement: 'topRight',
|
||||
message: "Ошибка",
|
||||
description: "Пожалуйста, выберите пациента.",
|
||||
placement: "topRight",
|
||||
});
|
||||
return;
|
||||
}
|
||||
form.setFieldsValue({patient_id: selectedPatient.id});
|
||||
form.setFieldsValue({ patient_id: selectedPatient.id });
|
||||
setCurrentStep(1);
|
||||
} else if (currentStep === 1) {
|
||||
try {
|
||||
@ -115,9 +153,9 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
setCurrentStep(2);
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: 'Ошибка валидации',
|
||||
description: error.message || 'Пожалуйста, заполните все обязательные поля.',
|
||||
placement: 'topRight',
|
||||
message: "Ошибка валидации",
|
||||
description: error.message || "Пожалуйста, заполните все обязательные поля.",
|
||||
placement: "topRight",
|
||||
});
|
||||
}
|
||||
} else if (currentStep === 2) {
|
||||
@ -136,15 +174,15 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
const values = formValues;
|
||||
|
||||
const appointmentTime = values.appointment_datetime;
|
||||
const hasConflict = appointments.some(app =>
|
||||
dayjs(app.appointment_datetime).isSame(appointmentTime, 'minute')
|
||||
const hasConflict = appointments.some((app) =>
|
||||
dayjs(app.appointment_datetime).isSame(appointmentTime, "minute")
|
||||
);
|
||||
|
||||
if (hasConflict) {
|
||||
notification.error({
|
||||
message: 'Конфликт времени',
|
||||
description: 'Выбранное время уже занято другим приемом.',
|
||||
placement: 'topRight',
|
||||
message: "Конфликт времени",
|
||||
description: "Выбранное время уже занято другим приемом.",
|
||||
placement: "topRight",
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -165,9 +203,9 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
}
|
||||
|
||||
notification.success({
|
||||
message: 'Прием создан',
|
||||
description: 'Прием успешно создан.',
|
||||
placement: 'topRight',
|
||||
message: "Прием создан",
|
||||
description: "Прием успешно создан.",
|
||||
placement: "topRight",
|
||||
});
|
||||
|
||||
dispatch(closeModal());
|
||||
@ -177,9 +215,9 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
setFormValues({});
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: 'Ошибка',
|
||||
description: error.data?.message || 'Не удалось сохранить прием.',
|
||||
placement: 'topRight',
|
||||
message: "Ошибка",
|
||||
description: error.data?.message || "Не удалось сохранить прием.",
|
||||
placement: "topRight",
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -189,10 +227,10 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
await cancelAppointment(selectedScheduledAppointmentId);
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: 'Ошибка',
|
||||
description: error.data?.message || 'Не удалось отменить прием.',
|
||||
placement: 'topRight',
|
||||
})
|
||||
message: "Ошибка",
|
||||
description: error.data?.message || "Не удалось отменить прием.",
|
||||
placement: "topRight",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -202,12 +240,13 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
setCurrentStep(0);
|
||||
setSearchPatientString("");
|
||||
setFormValues({});
|
||||
setIsDrawerVisible(false);
|
||||
onCancel();
|
||||
};
|
||||
|
||||
const disableBackButton = currentStep === 0;
|
||||
const disableNextButton = currentStep === 0 && !selectedPatient;
|
||||
const nextButtonText = currentStep === 2 ? 'Создать' : 'Далее';
|
||||
const nextButtonText = currentStep === 2 ? "Создать" : "Далее";
|
||||
|
||||
return {
|
||||
form,
|
||||
@ -221,6 +260,8 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
setResults,
|
||||
handleSetSearchPatientString,
|
||||
filteredPatients,
|
||||
filteredPreviousAppointments,
|
||||
handleSetSearchPreviousAppointments,
|
||||
handleOk,
|
||||
handleCancel,
|
||||
resetPatient,
|
||||
@ -244,6 +285,11 @@ const useAppointmentFormModalUI = (onCancel, createAppointment, patients, cancel
|
||||
footerButtonStyle,
|
||||
screenXS,
|
||||
direction,
|
||||
isDrawerVisible,
|
||||
showDrawer,
|
||||
closeDrawer,
|
||||
isLoadingPreviousAppointments,
|
||||
isErrorPreviousAppointments,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Button, List, Modal, Typography } from "antd";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {Button, Card, List, Modal, Typography} from "antd";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
closeAppointmentsListModal,
|
||||
@ -9,7 +9,7 @@ import {
|
||||
|
||||
const AppointmentsListModal = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { appointmentsListModalVisible, selectedDateAppointments } = useSelector(
|
||||
const {appointmentsListModalVisible, selectedDateAppointments} = useSelector(
|
||||
(state) => state.appointmentsUI
|
||||
);
|
||||
|
||||
@ -39,10 +39,18 @@ const AppointmentsListModal = () => {
|
||||
dataSource={selectedDateAppointments}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
onClick={() => handleItemClick(item)}
|
||||
style={{ cursor: "pointer", padding: "8px 0" }}
|
||||
style={{cursor: "pointer", padding: "8px 0"}}
|
||||
>
|
||||
<div>
|
||||
<Card
|
||||
hoverable
|
||||
style={{width: "100%"}}
|
||||
size="small"
|
||||
actions={[
|
||||
<Button type={"link"} key={"view"} onClick={() => handleItemClick(item)}>
|
||||
Просмотр приема
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
<p>
|
||||
<b>Время:</b>{" "}
|
||||
{item.appointment_datetime
|
||||
@ -61,14 +69,14 @@ const AppointmentsListModal = () => {
|
||||
? `${item.patient.last_name} ${item.patient.first_name}`
|
||||
: "Не указан"}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<p>Нет приемов на эту дату</p>
|
||||
)}
|
||||
<Button onClick={handleCancel} style={{ marginTop: 16 }}>
|
||||
<Button onClick={handleCancel} style={{marginTop: 16}}>
|
||||
Закрыть
|
||||
</Button>
|
||||
</Modal>
|
||||
|
||||
@ -44,7 +44,7 @@ const useAppointmentsUI = (appointments, scheduledAppointments) => {
|
||||
padding: hovered ? "0 20px" : "0",
|
||||
overflow: "hidden",
|
||||
textAlign: "left",
|
||||
transition: "width 0.8s ease, padding 0.8s ease",
|
||||
transition: "width 0.3s ease, padding 0.3s ease",
|
||||
borderRadius: "4px 0 0 4px",
|
||||
};
|
||||
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import {Badge, Col, Tag, Tooltip} from "antd";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Badge, Col, Tag, Tooltip } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import PropTypes from "prop-types";
|
||||
import {AppointmentPropType} from "../../Types/appointmentPropType.js";
|
||||
import {ScheduledAppointmentPropType} from "../../Types/scheduledAppointmentPropType.js";
|
||||
import { AppointmentPropType } from "../../Types/appointmentPropType.js";
|
||||
import { ScheduledAppointmentPropType } from "../../Types/scheduledAppointmentPropType.js";
|
||||
|
||||
|
||||
const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemClick}) => {
|
||||
const CalendarCell = ({ allAppointments, onCellClick, onItemClick }) => {
|
||||
const containerRef = useRef(null);
|
||||
const [isCompressed, setIsCompressed] = useState(false);
|
||||
const COMPRESSION_THRESHOLD = 70;
|
||||
@ -28,56 +27,34 @@ const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemC
|
||||
ref={containerRef}
|
||||
onClick={isCompressed ? onCellClick : undefined}
|
||||
style={{
|
||||
height: '100%',
|
||||
cursor: isCompressed ? 'pointer' : 'default',
|
||||
position: 'relative',
|
||||
height: "100%",
|
||||
cursor: isCompressed ? "pointer" : "default",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{!isCompressed && (
|
||||
<ul style={{padding: 0, margin: 0}}>
|
||||
{appointments.map(app => (
|
||||
<Col
|
||||
key={app.id}
|
||||
style={{overflowX: 'hidden'}}
|
||||
>
|
||||
<ul style={{ padding: 0, margin: 0 }}>
|
||||
{allAppointments.map((app) => (
|
||||
<Col key={app.id} style={{ overflowX: "hidden" }}>
|
||||
<Tooltip
|
||||
title={`Прошедший прием: ${dayjs(app.appointment_datetime).format('HH:mm')}`}
|
||||
title={`${
|
||||
app.appointment_datetime ? "Прошедший прием" : "Запланированный прием"
|
||||
}: ${dayjs(app.appointment_datetime || app.scheduled_datetime).format("HH:mm")}`}
|
||||
>
|
||||
<Tag
|
||||
color="green"
|
||||
color={app.appointment_datetime ? "green" : "blue"}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onItemClick(app);
|
||||
}}
|
||||
style={{margin: '2px 2px 0 0', cursor: 'pointer', width: "95%", minHeight: 30}}
|
||||
style={{ margin: "2px 2px 0 0", cursor: "pointer", width: "95%", minHeight: 30 }}
|
||||
>
|
||||
<Badge
|
||||
status="success"
|
||||
text={dayjs(app.appointment_datetime).format('HH:mm') + ` ${app.patient.last_name} ${app.patient.first_name} `}
|
||||
/>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
))}
|
||||
{scheduledAppointments.map(app => (
|
||||
<Col
|
||||
key={app.id}
|
||||
style={{overflowX: 'hidden'}}
|
||||
>
|
||||
<Tooltip
|
||||
title={`Запланированный прием: ${dayjs(app.scheduled_datetime).format('HH:mm')}`}
|
||||
>
|
||||
<Tag
|
||||
color="blue"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onItemClick(app);
|
||||
}}
|
||||
style={{margin: '2px 2px 0 0', cursor: 'pointer', width: "95%", minHeight: 30}}
|
||||
>
|
||||
<Badge
|
||||
status="processing"
|
||||
text={dayjs(app.scheduled_datetime).format('HH:mm') + ` ${app.patient.last_name} ${app.patient.first_name}`}
|
||||
status={app.appointment_datetime ? "success" : "processing"}
|
||||
text={
|
||||
dayjs(app.appointment_datetime || app.scheduled_datetime).format("HH:mm") +
|
||||
` ${app.patient?.last_name || ""} ${app.patient?.first_name || ""}`
|
||||
}
|
||||
/>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
@ -86,15 +63,17 @@ const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemC
|
||||
</ul>
|
||||
)}
|
||||
{isCompressed && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: 2,
|
||||
right: 2,
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
color: '#1890ff'
|
||||
}}>
|
||||
{appointments.length + scheduledAppointments.length > 0 && `+${appointments.length + scheduledAppointments.length}`}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 2,
|
||||
right: 2,
|
||||
fontSize: 10,
|
||||
fontWeight: "bold",
|
||||
color: "#1890ff",
|
||||
}}
|
||||
>
|
||||
{allAppointments.length > 0 && `+${allAppointments.length}`}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -102,8 +81,9 @@ const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemC
|
||||
};
|
||||
|
||||
CalendarCell.propTypes = {
|
||||
appointments: PropTypes.arrayOf(AppointmentPropType).isRequired,
|
||||
scheduledAppointments: PropTypes.arrayOf(ScheduledAppointmentPropType).isRequired,
|
||||
allAppointments: PropTypes.arrayOf(
|
||||
PropTypes.oneOfType([AppointmentPropType, ScheduledAppointmentPropType])
|
||||
).isRequired,
|
||||
onCellClick: PropTypes.func.isRequired,
|
||||
onItemClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@ import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
const initialState = {
|
||||
modalVisible: false,
|
||||
collapsed: false,
|
||||
collapsed: true,
|
||||
siderWidth: 300,
|
||||
hovered: false,
|
||||
selectedAppointment: null,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user