refactor: Обновление UI компонентов и хуков
Перемещены и рефакторизованы виджеты и хуки для улучшения структуры. Обновлены стили и тексты в модальных окнах.
This commit is contained in:
parent
4648f638a3
commit
0c326d815a
@ -18,7 +18,7 @@ import {
|
|||||||
} from "antd";
|
} from "antd";
|
||||||
import useAppointmentFormModal from "./useAppointmentFormModal.js";
|
import useAppointmentFormModal from "./useAppointmentFormModal.js";
|
||||||
import useAppointmentFormModalUI from "./useAppointmentFormModalUI.js";
|
import useAppointmentFormModalUI from "./useAppointmentFormModalUI.js";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import {useMemo} from "react";
|
import {useMemo} from "react";
|
||||||
|
|
||||||
const AppointmentFormModal = () => {
|
const AppointmentFormModal = () => {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/Appoint
|
|||||||
import useAppointmentsUI from "./useAppointmentsUI.js";
|
import useAppointmentsUI from "./useAppointmentsUI.js";
|
||||||
import useAppointments from "./useAppointments.js";
|
import useAppointments from "./useAppointments.js";
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import AppointmentFormModal from "../../Dummies/AppointmentFormModal/AppointmentFormModal.jsx";
|
import AppointmentFormModal from "../../Dummies/AppointmentFormModal/AppointmentFormModal.jsx";
|
||||||
import {useDispatch} from "react-redux";
|
import {useDispatch} from "react-redux";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Calendar } from "antd";
|
import { Calendar } from "antd";
|
||||||
import "dayjs/locale/ru";
|
import "dayjs/locale/ru";
|
||||||
import CalendarCell from "../../../../Widgets/CalendarCell.jsx";
|
import CalendarCell from "../CalendarCell/CalendarCell.jsx";
|
||||||
import useAppointments from "../../useAppointments.js";
|
import useAppointments from "../../useAppointments.js";
|
||||||
import useAppointmentCalendarUI from "./useAppointmentCalendarUI.js";
|
import useAppointmentCalendarUI from "./useAppointmentCalendarUI.js";
|
||||||
import AppointmentsListModal from "../AppointmentsListModal/AppointmentsListModal.jsx";
|
import AppointmentsListModal from "../AppointmentsListModal/AppointmentsListModal.jsx";
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
import { Badge, Col, Tag, Tooltip } from "antd";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { AppointmentPropType } from "../../../../../Types/appointmentPropType.js";
|
||||||
|
import { ScheduledAppointmentPropType } from "../../../../../Types/scheduledAppointmentPropType.js";
|
||||||
|
import useCalendarCellUI from "./useCalendarCellUI.js";
|
||||||
|
|
||||||
|
const CalendarCell = ({ allAppointments, onCellClick, onItemClick }) => {
|
||||||
|
const {
|
||||||
|
containerRef,
|
||||||
|
isCompressed,
|
||||||
|
containerStyle,
|
||||||
|
listStyle,
|
||||||
|
columnStyle,
|
||||||
|
tagStyle,
|
||||||
|
badgeTextStyle,
|
||||||
|
compressedCountStyle,
|
||||||
|
getTooltipTitle,
|
||||||
|
getBadgeText,
|
||||||
|
getTagColor,
|
||||||
|
getBadgeStatus,
|
||||||
|
} = useCalendarCellUI();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
onClick={isCompressed ? onCellClick : undefined}
|
||||||
|
style={containerStyle}
|
||||||
|
>
|
||||||
|
{!isCompressed && (
|
||||||
|
<ul style={listStyle}>
|
||||||
|
{allAppointments.map((app) => (
|
||||||
|
<Col key={app.id} style={columnStyle}>
|
||||||
|
<Tooltip title={getTooltipTitle(app)}>
|
||||||
|
<Tag
|
||||||
|
color={getTagColor(!!app.appointment_datetime)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onItemClick(app);
|
||||||
|
}}
|
||||||
|
style={tagStyle}
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
status={getBadgeStatus(!!app.appointment_datetime)}
|
||||||
|
text={
|
||||||
|
<span style={badgeTextStyle}>
|
||||||
|
{getBadgeText(app)}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
{isCompressed && (
|
||||||
|
<div style={compressedCountStyle}>
|
||||||
|
{allAppointments.length > 0 && `+${allAppointments.length}`}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CalendarCell.propTypes = {
|
||||||
|
allAppointments: PropTypes.arrayOf(
|
||||||
|
PropTypes.oneOfType([AppointmentPropType, ScheduledAppointmentPropType])
|
||||||
|
).isRequired,
|
||||||
|
onCellClick: PropTypes.func.isRequired,
|
||||||
|
onItemClick: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CalendarCell;
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
const useCalendarCellUI = () => {
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
const [isCompressed, setIsCompressed] = useState(false);
|
||||||
|
const COMPRESSION_THRESHOLD = 70;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!containerRef.current) return;
|
||||||
|
|
||||||
|
const observer = new ResizeObserver((entries) => {
|
||||||
|
const width = entries[0].contentRect.width;
|
||||||
|
setIsCompressed(width < COMPRESSION_THRESHOLD);
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(containerRef.current);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Styles
|
||||||
|
const containerStyle = {
|
||||||
|
height: "100%",
|
||||||
|
cursor: isCompressed ? "pointer" : "default",
|
||||||
|
position: "relative",
|
||||||
|
};
|
||||||
|
|
||||||
|
const listStyle = {
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const columnStyle = {
|
||||||
|
overflowX: "hidden",
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagStyle = {
|
||||||
|
margin: "2px 2px 0 0",
|
||||||
|
cursor: "pointer",
|
||||||
|
width: "95%",
|
||||||
|
minHeight: 30,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
overflow: "hidden",
|
||||||
|
};
|
||||||
|
|
||||||
|
const badgeTextStyle = {
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
display: "inline-block",
|
||||||
|
width: "100%",
|
||||||
|
};
|
||||||
|
|
||||||
|
const compressedCountStyle = {
|
||||||
|
position: "absolute",
|
||||||
|
top: 2,
|
||||||
|
right: 2,
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: "bold",
|
||||||
|
color: "#1890ff",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Static configuration
|
||||||
|
const labels = {
|
||||||
|
pastAppointment: "Прошедший прием",
|
||||||
|
scheduledAppointment: "Запланированный прием",
|
||||||
|
notSpecified: "Не указан",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Formatting functions
|
||||||
|
const getAppointmentTime = (datetime) => {
|
||||||
|
return datetime ? dayjs(datetime).format("HH:mm") : labels.notSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTooltipTitle = (app) => {
|
||||||
|
const type = app.appointment_datetime ? labels.pastAppointment : labels.scheduledAppointment;
|
||||||
|
return `${type}: ${getAppointmentTime(app.appointment_datetime || app.scheduled_datetime)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBadgeText = (app) => {
|
||||||
|
const time = getAppointmentTime(app.appointment_datetime || app.scheduled_datetime);
|
||||||
|
const patientName = app.patient
|
||||||
|
? `${app.patient.last_name || ""} ${app.patient.first_name || ""}`.trim()
|
||||||
|
: labels.notSpecified;
|
||||||
|
return `${time} ${patientName}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTagColor = (isPast) => {
|
||||||
|
return isPast ? "green" : "blue";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBadgeStatus = (isPast) => {
|
||||||
|
return isPast ? "success" : "processing";
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
containerRef,
|
||||||
|
isCompressed,
|
||||||
|
containerStyle,
|
||||||
|
listStyle,
|
||||||
|
columnStyle,
|
||||||
|
tagStyle,
|
||||||
|
badgeTextStyle,
|
||||||
|
compressedCountStyle,
|
||||||
|
labels,
|
||||||
|
getTooltipTitle,
|
||||||
|
getBadgeText,
|
||||||
|
getTagColor,
|
||||||
|
getBadgeStatus,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCalendarCellUI;
|
||||||
@ -8,7 +8,7 @@ import {
|
|||||||
import useHomePage from "./useHomePage.js";
|
import useHomePage from "./useHomePage.js";
|
||||||
import useHomePageUI from "./useHomePageUI.js";
|
import useHomePageUI from "./useHomePageUI.js";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import {Bar} from "react-chartjs-2";
|
import {Bar} from "react-chartjs-2";
|
||||||
import {
|
import {
|
||||||
Chart as ChartJS,
|
Chart as ChartJS,
|
||||||
|
|||||||
@ -16,8 +16,8 @@ import {DatabaseOutlined, PlusOutlined, UnorderedListOutlined} from "@ant-design
|
|||||||
import LensIssueViewModal from "./Components/LensIssueViewModal/LensIssueViewModal.jsx";
|
import LensIssueViewModal from "./Components/LensIssueViewModal/LensIssueViewModal.jsx";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import LensIssueFormModal from "./Components/LensIssueFormModal/LensIssueFormModal.jsx";
|
import LensIssueFormModal from "./Components/LensIssueFormModal/LensIssueFormModal.jsx";
|
||||||
import SelectViewMode from "../../Widgets/SelectViewMode.jsx";
|
import SelectViewMode from "../../Widgets/SelectViewMode/SelectViewMode.jsx";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import useIssues from "./useIssues.js";
|
import useIssues from "./useIssues.js";
|
||||||
import useIssuesUI from "./useIssuesUI.js";
|
import useIssuesUI from "./useIssuesUI.js";
|
||||||
|
|
||||||
|
|||||||
@ -24,8 +24,8 @@ import {
|
|||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import LensCard from "../../../../Dummies/LensListCard.jsx";
|
import LensCard from "../../../../Dummies/LensListCard.jsx";
|
||||||
import LensFormModal from "./Components/LensFormModal/LensFormModal.jsx";
|
import LensFormModal from "./Components/LensFormModal/LensFormModal.jsx";
|
||||||
import SelectViewMode from "../../../../Widgets/SelectViewMode.jsx";
|
import SelectViewMode from "../../../../Widgets/SelectViewMode/SelectViewMode.jsx";
|
||||||
import LoadingIndicator from "../../../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import useLenses from "./useLenses.js";
|
import useLenses from "./useLenses.js";
|
||||||
import useLensesUI from "./useLensesUI.js";
|
import useLensesUI from "./useLensesUI.js";
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {FloatButton, Input, List, Result, Row, Typography} from "antd";
|
|||||||
import {PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
import {PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||||
import SetListCard from "../../../../Dummies/SetListCard.jsx";
|
import SetListCard from "../../../../Dummies/SetListCard.jsx";
|
||||||
import SetFormModal from "./Components/SetFormModal/SetFormModal.jsx";
|
import SetFormModal from "./Components/SetFormModal/SetFormModal.jsx";
|
||||||
import LoadingIndicator from "../../../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import useSets from "./useSets.js";
|
import useSets from "./useSets.js";
|
||||||
import useSetsUI from "./useSetsUI.js";
|
import useSetsUI from "./useSetsUI.js";
|
||||||
|
|
||||||
|
|||||||
@ -19,9 +19,9 @@ import {
|
|||||||
TeamOutlined
|
TeamOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import PatientListCard from "../../Dummies/PatientListCard.jsx";
|
import PatientListCard from "../../Dummies/PatientListCard.jsx";
|
||||||
import PatientFormModal from "./Components/PatientFormModal/PatientFormModal.jsx";
|
import PatientFormModal from "../../Dummies/PatientFormModal/PatientFormModal.jsx";
|
||||||
import SelectViewMode from "../../Widgets/SelectViewMode.jsx";
|
import SelectViewMode from "../../Widgets/SelectViewMode/SelectViewMode.jsx";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import usePatients from "./usePatients.js";
|
import usePatients from "./usePatients.js";
|
||||||
import usePatientsUI from "./usePatientsUI.js";
|
import usePatientsUI from "./usePatientsUI.js";
|
||||||
|
|
||||||
@ -186,11 +186,7 @@ const PatientsPage = () => {
|
|||||||
tooltip="Добавить пациента"
|
tooltip="Добавить пациента"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PatientFormModal
|
<PatientFormModal/>
|
||||||
visible={patientsUI.isModalVisible}
|
|
||||||
onCancel={patientsUI.handleCloseModal}
|
|
||||||
onSubmit={patientsData.handleModalSubmit}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,24 +1,14 @@
|
|||||||
import { useDispatch, useSelector } from "react-redux";
|
import {notification} from "antd";
|
||||||
import { notification } from "antd";
|
|
||||||
import {
|
import {
|
||||||
useAddPatientMutation,
|
|
||||||
useDeletePatientMutation,
|
useDeletePatientMutation,
|
||||||
useGetPatientsQuery,
|
useGetPatientsQuery,
|
||||||
useUpdatePatientMutation
|
|
||||||
} from "../../../Api/patientsApi.js";
|
} from "../../../Api/patientsApi.js";
|
||||||
import {closeModal} from "../../../Redux/Slices/patientsSlice.js";
|
|
||||||
|
|
||||||
const usePatients = () => {
|
const usePatients = () => {
|
||||||
const dispatch = useDispatch();
|
const {data: patients = [], isLoading, isError} = useGetPatientsQuery(undefined, {
|
||||||
const {
|
|
||||||
selectedPatient,
|
|
||||||
} = useSelector(state => state.patientsUI);
|
|
||||||
|
|
||||||
const { data: patients = [], isLoading, isError } = useGetPatientsQuery(undefined, {
|
|
||||||
pollingInterval: 20000,
|
pollingInterval: 20000,
|
||||||
});
|
});
|
||||||
const [addPatient] = useAddPatientMutation();
|
|
||||||
const [updatePatient] = useUpdatePatientMutation();
|
|
||||||
const [deletePatient] = useDeletePatientMutation();
|
const [deletePatient] = useDeletePatientMutation();
|
||||||
|
|
||||||
const handleDeletePatient = async (patientId) => {
|
const handleDeletePatient = async (patientId) => {
|
||||||
@ -38,40 +28,11 @@ const usePatients = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalSubmit = async (patientData) => {
|
|
||||||
dispatch(closeModal());
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (selectedPatient) {
|
|
||||||
await updatePatient({ id: selectedPatient.id, ...patientData }).unwrap();
|
|
||||||
notification.success({
|
|
||||||
message: "Пациент обновлён",
|
|
||||||
description: `Данные пациента ${patientData.first_name} ${patientData.last_name} успешно обновлены.`,
|
|
||||||
placement: "topRight",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await addPatient(patientData).unwrap();
|
|
||||||
notification.success({
|
|
||||||
message: "Пациент добавлен",
|
|
||||||
description: `Пациент ${patientData.first_name} ${patientData.last_name} успешно добавлен.`,
|
|
||||||
placement: "topRight",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
notification.error({
|
|
||||||
message: "Ошибка",
|
|
||||||
description: error.data?.message || "Произошла ошибка при сохранении",
|
|
||||||
placement: "topRight",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
patients,
|
patients,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
handleDeletePatient,
|
handleDeletePatient,
|
||||||
handleModalSubmit,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Button, Card, Col, Form, Input, Modal, Row, Space, Typography, Result } from "antd";
|
import { Button, Card, Col, Form, Input, Modal, Row, Space, Typography, Result } from "antd";
|
||||||
import { EditOutlined } from "@ant-design/icons";
|
import { EditOutlined } from "@ant-design/icons";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import useProfilePage from "./useProfilePage.js";
|
import useProfilePage from "./useProfilePage.js";
|
||||||
import useProfilePageUI from "./useProfilePageUI.js";
|
import useProfilePageUI from "./useProfilePageUI.js";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
|
|||||||
@ -1,68 +1,78 @@
|
|||||||
import {Button, Modal, Row, Typography} from "antd";
|
import {Button, Modal, Row, Typography} from "antd";
|
||||||
import useAppointmentViewUI from "./useAppointmentViewUI.js";
|
import useAppointmentViewUI from "./useAppointmentViewUI.js";
|
||||||
import dayjs from "dayjs";
|
|
||||||
|
|
||||||
|
|
||||||
const AppointmentViewModal = () => {
|
const AppointmentViewModal = () => {
|
||||||
const appointmentViewModalUI = useAppointmentViewUI();
|
const {
|
||||||
|
modalWidth,
|
||||||
|
blockStyle,
|
||||||
|
footerRowStyle,
|
||||||
|
footerButtonStyle,
|
||||||
|
labels,
|
||||||
|
selectedAppointment,
|
||||||
|
visible,
|
||||||
|
getDateString,
|
||||||
|
getAppointmentTime,
|
||||||
|
getPatientName,
|
||||||
|
getPatientField,
|
||||||
|
getResults,
|
||||||
|
onCancel,
|
||||||
|
} = useAppointmentViewUI();
|
||||||
|
|
||||||
if (!appointmentViewModalUI.selectedAppointment) {
|
if (!selectedAppointment) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Modal
|
<Modal
|
||||||
title="Просмотр приема"
|
title={labels.title}
|
||||||
open={appointmentViewModalUI.visible}
|
open={visible}
|
||||||
onCancel={appointmentViewModalUI.onCancel}
|
onCancel={onCancel}
|
||||||
footer={null}
|
footer={null}
|
||||||
width={appointmentViewModalUI.modalWidth}
|
width={modalWidth}
|
||||||
>
|
>
|
||||||
<div style={appointmentViewModalUI.blockStyle}>
|
<div style={blockStyle}>
|
||||||
<Typography.Title level={4}>Информация о приеме</Typography.Title>
|
<Typography.Title level={4}>Информация о приеме</Typography.Title>
|
||||||
<p>
|
<p>
|
||||||
<b>Пациент:</b>{" "}
|
<b>{labels.patient}</b> {getPatientName(selectedAppointment.patient)}
|
||||||
{appointmentViewModalUI.selectedAppointment.patient ? `${appointmentViewModalUI.selectedAppointment.patient.last_name} ${appointmentViewModalUI.selectedAppointment.patient.first_name}` : "Не указан"}
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Дата рождения:</b>{" "}
|
<b>{labels.birthday}</b>{" "}
|
||||||
{appointmentViewModalUI.selectedAppointment.patient ? appointmentViewModalUI.getDateString(appointmentViewModalUI.selectedAppointment.patient.birthday) : "Не указан"}
|
{getDateString(selectedAppointment.patient?.birthday)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Email:</b> {appointmentViewModalUI.selectedAppointment.patient?.email || "Не указан"}
|
<b>{labels.email}</b>{" "}
|
||||||
|
{getPatientField(selectedAppointment.patient?.email)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Телефон:</b> {appointmentViewModalUI.selectedAppointment.patient?.phone || "Не указан"}
|
<b>{labels.phone}</b>{" "}
|
||||||
|
{getPatientField(selectedAppointment.patient?.phone)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Тип приема:</b> {appointmentViewModalUI.selectedAppointment.type?.title || "Не указан"}
|
<b>{labels.type}</b>{" "}
|
||||||
|
{getPatientField(selectedAppointment.type?.title)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Время приема:</b>{" "}
|
<b>{labels.appointmentTime}</b>{" "}
|
||||||
{appointmentViewModalUI.selectedAppointment.appointment_datetime
|
{getAppointmentTime(selectedAppointment.appointment_datetime)}
|
||||||
? dayjs(appointmentViewModalUI.selectedAppointment.appointment_datetime).format("DD.MM.YYYY HH:mm")
|
|
||||||
: "Не указано"}
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Дней до следующего приема:</b>{" "}
|
<b>{labels.daysUntilNext}</b>{" "}
|
||||||
{appointmentViewModalUI.selectedAppointment.days_until_the_next_appointment || "Не указано"}
|
{getPatientField(selectedAppointment.days_until_the_next_appointment)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Результаты приема:</b>
|
<b>{labels.results}</b>
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{__html: appointmentViewModalUI.selectedAppointment.results || "Не указаны"}}/>
|
dangerouslySetInnerHTML={{__html: getResults(selectedAppointment.results)}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Row justify="end" style={appointmentViewModalUI.footerRowStyle}>
|
<Row justify="end" style={footerRowStyle}>
|
||||||
<Button style={appointmentViewModalUI.footerButtonStyle} onClick={appointmentViewModalUI.onCancel}>
|
<Button style={footerButtonStyle} onClick={onCancel}>
|
||||||
Закрыть
|
{labels.closeButton}
|
||||||
</Button>
|
</Button>
|
||||||
</Row>
|
</Row>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default AppointmentViewModal;
|
export default AppointmentViewModal;
|
||||||
@ -1,36 +1,74 @@
|
|||||||
import {useDispatch, useSelector} from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import { setSelectedAppointment } from "../../../Redux/Slices/appointmentsSlice.js";
|
||||||
setSelectedAppointment
|
import dayjs from "dayjs";
|
||||||
} from "../../../Redux/Slices/appointmentsSlice.js";
|
|
||||||
|
|
||||||
const useAppointmentViewUI = () => {
|
const useAppointmentViewUI = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const {
|
const { selectedAppointment } = useSelector((state) => state.appointmentsUI);
|
||||||
selectedAppointment,
|
|
||||||
} = useSelector(state => state.appointmentsUI);
|
|
||||||
|
|
||||||
const modalWidth = 700;
|
const modalWidth = 700;
|
||||||
const blockStyle = {marginBottom: 16};
|
const blockStyle = { marginBottom: 16 };
|
||||||
const footerRowStyle = {marginTop: 16};
|
const footerRowStyle = { marginTop: 16 };
|
||||||
const footerButtonStyle = {marginRight: 8};
|
const footerButtonStyle = { marginRight: 8 };
|
||||||
|
|
||||||
|
const labels = {
|
||||||
|
title: "Просмотр приема",
|
||||||
|
patient: "Пациент:",
|
||||||
|
birthday: "Дата рождения:",
|
||||||
|
email: "Email:",
|
||||||
|
phone: "Телефон:",
|
||||||
|
type: "Тип приема:",
|
||||||
|
appointmentTime: "Время приема:",
|
||||||
|
daysUntilNext: "Дней до следующего приема:",
|
||||||
|
results: "Результаты приема:",
|
||||||
|
closeButton: "Закрыть",
|
||||||
|
notSpecified: "Не указан",
|
||||||
|
resultsNotSpecified: "Не указаны",
|
||||||
|
};
|
||||||
|
|
||||||
const visible = !!selectedAppointment;
|
const visible = !!selectedAppointment;
|
||||||
|
|
||||||
|
const getDateString = (date) => {
|
||||||
|
return date ? new Date(date).toLocaleDateString("ru-RU") : labels.notSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAppointmentTime = (datetime) => {
|
||||||
|
return datetime
|
||||||
|
? dayjs(datetime).format("DD.MM.YYYY HH:mm")
|
||||||
|
: labels.notSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPatientName = (patient) => {
|
||||||
|
return patient
|
||||||
|
? `${patient.last_name} ${patient.first_name}`
|
||||||
|
: labels.notSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPatientField = (field) => {
|
||||||
|
return field || labels.notSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getResults = (results) => {
|
||||||
|
return results || labels.resultsNotSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
dispatch(setSelectedAppointment(null));
|
dispatch(setSelectedAppointment(null));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDateString = (date) => {
|
|
||||||
return new Date(date).toLocaleDateString('ru-RU');
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modalWidth,
|
modalWidth,
|
||||||
blockStyle,
|
blockStyle,
|
||||||
footerRowStyle,
|
footerRowStyle,
|
||||||
footerButtonStyle,
|
footerButtonStyle,
|
||||||
|
labels,
|
||||||
selectedAppointment,
|
selectedAppointment,
|
||||||
visible,
|
visible,
|
||||||
getDateString,
|
getDateString,
|
||||||
|
getAppointmentTime,
|
||||||
|
getPatientName,
|
||||||
|
getPatientField,
|
||||||
|
getResults,
|
||||||
onCancel,
|
onCancel,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,109 +0,0 @@
|
|||||||
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";
|
|
||||||
|
|
||||||
const CalendarCell = ({allAppointments, onCellClick, onItemClick}) => {
|
|
||||||
const containerRef = useRef(null);
|
|
||||||
const [isCompressed, setIsCompressed] = useState(false);
|
|
||||||
const COMPRESSION_THRESHOLD = 70;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!containerRef.current) return;
|
|
||||||
|
|
||||||
const observer = new ResizeObserver((entries) => {
|
|
||||||
const width = entries[0].contentRect.width;
|
|
||||||
setIsCompressed(width < COMPRESSION_THRESHOLD);
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(containerRef.current);
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={containerRef}
|
|
||||||
onClick={isCompressed ? onCellClick : undefined}
|
|
||||||
style={{
|
|
||||||
height: "100%",
|
|
||||||
cursor: isCompressed ? "pointer" : "default",
|
|
||||||
position: "relative",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!isCompressed && (
|
|
||||||
<ul style={{padding: 0, margin: 0}}>
|
|
||||||
{allAppointments.map((app) => (
|
|
||||||
<Col key={app.id} style={{overflowX: "hidden"}}>
|
|
||||||
<Tooltip
|
|
||||||
title={`${
|
|
||||||
app.appointment_datetime ? "Прошедший прием" : "Запланированный прием"
|
|
||||||
}: ${dayjs(app.appointment_datetime || app.scheduled_datetime).format("HH:mm")}`}
|
|
||||||
>
|
|
||||||
<Tag
|
|
||||||
color={app.appointment_datetime ? "green" : "blue"}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
onItemClick(app);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
margin: "2px 2px 0 0",
|
|
||||||
cursor: "pointer",
|
|
||||||
width: "95%",
|
|
||||||
minHeight: 30,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Badge
|
|
||||||
status={app.appointment_datetime ? "success" : "processing"}
|
|
||||||
text={
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
overflow: "hidden",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
display: "inline-block",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{dayjs(app.appointment_datetime || app.scheduled_datetime).format("HH:mm") +
|
|
||||||
` ${app.patient?.last_name || ""} ${app.patient?.first_name || ""}`}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tag>
|
|
||||||
</Tooltip>
|
|
||||||
</Col>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
{isCompressed && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 2,
|
|
||||||
right: 2,
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#1890ff",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{allAppointments.length > 0 && `+${allAppointments.length}`}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
CalendarCell.propTypes = {
|
|
||||||
allAppointments: PropTypes.arrayOf(
|
|
||||||
PropTypes.oneOfType([AppointmentPropType, ScheduledAppointmentPropType])
|
|
||||||
).isRequired,
|
|
||||||
onCellClick: PropTypes.func.isRequired,
|
|
||||||
onItemClick: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CalendarCell;
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
import {Spin} from "antd";
|
|
||||||
import {LoadingOutlined} from "@ant-design/icons";
|
|
||||||
|
|
||||||
|
|
||||||
const LoadingIndicator = () => {
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100vh",
|
|
||||||
}}>
|
|
||||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LoadingIndicator;
|
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import {Spin} from "antd";
|
||||||
|
import {LoadingOutlined} from "@ant-design/icons";
|
||||||
|
import useLoadingIndicatorUI from "./useLoadingIndicator.js";
|
||||||
|
|
||||||
|
const LoadingIndicator = () => {
|
||||||
|
const {containerStyle, iconStyle} = useLoadingIndicatorUI();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={containerStyle}>
|
||||||
|
<Spin indicator={<LoadingOutlined style={iconStyle} spin/>}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoadingIndicator;
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
const useLoadingIndicatorUI = () => {
|
||||||
|
const containerStyle = {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100vh",
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconStyle = {
|
||||||
|
fontSize: 64,
|
||||||
|
color: "#1890ff",
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
containerStyle,
|
||||||
|
iconStyle,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLoadingIndicatorUI;
|
||||||
@ -1,79 +1,79 @@
|
|||||||
import {Button, Modal, Popconfirm, Row, Typography} from "antd";
|
import {Button, Modal, Popconfirm, Row, Typography} from "antd";
|
||||||
import dayjs from "dayjs";
|
|
||||||
import useScheduledAppointmentsViewModal from "./useScheduledAppointmentsViewModal.js";
|
import useScheduledAppointmentsViewModal from "./useScheduledAppointmentsViewModal.js";
|
||||||
import useScheduledAppointmentsViewModalUI from "./useScheduledAppointmentsViewModalUI.js";
|
import useScheduledAppointmentsViewModalUI from "./useScheduledAppointmentsViewModalUI.js";
|
||||||
import { useDispatch } from "react-redux";
|
|
||||||
import {openModalWithScheduledData} from "../../../Redux/Slices/appointmentsSlice.js";
|
|
||||||
|
|
||||||
const ScheduledAppointmentsViewModal = () => {
|
const ScheduledAppointmentsViewModal = () => {
|
||||||
const dispatch = useDispatch();
|
|
||||||
const scheduledAppointmentsViewModalData = useScheduledAppointmentsViewModal();
|
const scheduledAppointmentsViewModalData = useScheduledAppointmentsViewModal();
|
||||||
const scheduledAppointmentsViewModalUI = useScheduledAppointmentsViewModalUI(scheduledAppointmentsViewModalData.cancelAppointment);
|
const {
|
||||||
|
selectedScheduledAppointment,
|
||||||
|
modalWidth,
|
||||||
|
blockStyle,
|
||||||
|
footerRowStyle,
|
||||||
|
footerButtonStyle,
|
||||||
|
labels,
|
||||||
|
visible,
|
||||||
|
getDateString,
|
||||||
|
getAppointmentTime,
|
||||||
|
getPatientName,
|
||||||
|
getPatientField,
|
||||||
|
onCancel,
|
||||||
|
cancelScheduledAppointment,
|
||||||
|
handleConvertToAppointment,
|
||||||
|
} = useScheduledAppointmentsViewModalUI(scheduledAppointmentsViewModalData.cancelAppointment);
|
||||||
|
|
||||||
if (!scheduledAppointmentsViewModalUI.selectedScheduledAppointment) {
|
if (!selectedScheduledAppointment) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleConvertToAppointment = () => {
|
|
||||||
dispatch(openModalWithScheduledData({
|
|
||||||
id: scheduledAppointmentsViewModalUI.selectedScheduledAppointment.id,
|
|
||||||
patient_id: scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient?.id,
|
|
||||||
type_id: scheduledAppointmentsViewModalUI.selectedScheduledAppointment.type?.id,
|
|
||||||
appointment_datetime: scheduledAppointmentsViewModalUI.selectedScheduledAppointment.scheduled_datetime,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="Просмотр запланированного приема"
|
title={labels.title}
|
||||||
open={true}
|
open={visible}
|
||||||
onCancel={scheduledAppointmentsViewModalUI.onCancel}
|
onCancel={onCancel}
|
||||||
footer={null}
|
footer={null}
|
||||||
width={scheduledAppointmentsViewModalUI.modalWidth}
|
width={modalWidth}
|
||||||
>
|
>
|
||||||
<div style={scheduledAppointmentsViewModalUI.blockStyle}>
|
<div style={blockStyle}>
|
||||||
<Typography.Title level={4}>Информация о приеме</Typography.Title>
|
<Typography.Title level={4}>Информация о приеме</Typography.Title>
|
||||||
<p>
|
<p>
|
||||||
<b>Пациент:</b>{" "}
|
<b>{labels.patient}</b> {getPatientName(selectedScheduledAppointment.patient)}
|
||||||
{scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient ? `${scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient.last_name} ${scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient.first_name}` : "Не указан"}
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Дата рождения:</b>{" "}
|
<b>{labels.birthday}</b>{" "}
|
||||||
{scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient ? scheduledAppointmentsViewModalUI.getDateString(scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient.birthday) : "Не указан"}
|
{getDateString(selectedScheduledAppointment.patient?.birthday)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Email:</b> {scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient?.email || "Не указан"}
|
<b>{labels.email}</b>{" "}
|
||||||
|
{getPatientField(selectedScheduledAppointment.patient?.email)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Телефон:</b> {scheduledAppointmentsViewModalUI.selectedScheduledAppointment.patient?.phone || "Не указан"}
|
<b>{labels.phone}</b>{" "}
|
||||||
|
{getPatientField(selectedScheduledAppointment.patient?.phone)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Тип
|
<b>{labels.type}</b>{" "}
|
||||||
приема:</b> {scheduledAppointmentsViewModalUI.selectedScheduledAppointment.type?.title || "Не указан"}
|
{getPatientField(selectedScheduledAppointment.type?.title)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Время приема:</b>{" "}
|
<b>{labels.appointmentTime}</b>{" "}
|
||||||
{scheduledAppointmentsViewModalUI.selectedScheduledAppointment.scheduled_datetime
|
{getAppointmentTime(selectedScheduledAppointment.scheduled_datetime)}
|
||||||
? dayjs(scheduledAppointmentsViewModalUI.selectedScheduledAppointment.scheduled_datetime).format("DD.MM.YYYY HH:mm")
|
|
||||||
: "Не указано"}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Row justify="end" style={{...scheduledAppointmentsViewModalUI.footerRowStyle, gap: 8}}>
|
<Row justify="end" style={footerRowStyle}>
|
||||||
<Button style={scheduledAppointmentsViewModalUI.footerButtonStyle}
|
<Button style={footerButtonStyle} onClick={onCancel}>
|
||||||
onClick={scheduledAppointmentsViewModalUI.onCancel}>
|
{labels.closeButton}
|
||||||
Закрыть
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" onClick={handleConvertToAppointment}>
|
<Button type="primary" onClick={handleConvertToAppointment}>
|
||||||
Конвертировать в прием
|
{labels.convertButton}
|
||||||
</Button>
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="Вы уверены, что хотите отменить прием?"
|
title={labels.popconfirmTitle}
|
||||||
onConfirm={scheduledAppointmentsViewModalUI.cancelScheduledAppointment}
|
onConfirm={cancelScheduledAppointment}
|
||||||
okText="Да, отменить"
|
okText={labels.popconfirmOk}
|
||||||
cancelText="Отмена"
|
cancelText={labels.popconfirmCancel}
|
||||||
>
|
>
|
||||||
<Button type={"primary"} danger>
|
<Button type="primary" danger>
|
||||||
Отмена приема
|
{labels.cancelButton}
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
@ -1,22 +1,55 @@
|
|||||||
import {useDispatch, useSelector} from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { setSelectedScheduledAppointment, openModalWithScheduledData } from "../../../Redux/Slices/appointmentsSlice.js";
|
||||||
|
import { notification } from "antd";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import {setSelectedScheduledAppointment} from "../../../Redux/Slices/appointmentsSlice.js";
|
|
||||||
import {notification} from "antd";
|
|
||||||
|
|
||||||
|
|
||||||
const useScheduledAppointmentsViewModalUI = (cancelAppointment) => {
|
const useScheduledAppointmentsViewModalUI = (cancelAppointment) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const { selectedScheduledAppointment } = useSelector((state) => state.appointmentsUI);
|
||||||
|
|
||||||
|
const modalWidth = 700;
|
||||||
|
const blockStyle = { marginBottom: 16 };
|
||||||
|
const footerRowStyle = { marginTop: 16, gap: 8 };
|
||||||
|
const footerButtonStyle = { marginRight: 8 };
|
||||||
|
|
||||||
const {
|
const labels = {
|
||||||
selectedScheduledAppointment,
|
title: "Просмотр запланированного приема",
|
||||||
} = useSelector(state => state.appointmentsUI);
|
patient: "Пациент:",
|
||||||
|
birthday: "Дата рождения:",
|
||||||
|
email: "Email:",
|
||||||
|
phone: "Телефон:",
|
||||||
|
type: "Тип приема:",
|
||||||
|
appointmentTime: "Время приема:",
|
||||||
|
closeButton: "Закрыть",
|
||||||
|
convertButton: "Конвертировать в прием",
|
||||||
|
cancelButton: "Отмена приема",
|
||||||
|
popconfirmTitle: "Вы уверены, что хотите отменить прием?",
|
||||||
|
popconfirmOk: "Да, отменить",
|
||||||
|
popconfirmCancel: "Отмена",
|
||||||
|
notSpecified: "Не указан",
|
||||||
|
};
|
||||||
|
|
||||||
const blockStyle = {marginBottom: 16};
|
const visible = !!selectedScheduledAppointment;
|
||||||
|
|
||||||
const getDateString = (date) => {
|
const getDateString = (date) => {
|
||||||
return date ? dayjs(date).format('DD.MM.YYYY') : 'Не указано';
|
return date ? dayjs(date).format("DD.MM.YYYY") : labels.notSpecified;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const getAppointmentTime = (datetime) => {
|
||||||
|
return datetime
|
||||||
|
? dayjs(datetime).format("DD.MM.YYYY HH:mm")
|
||||||
|
: labels.notSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPatientName = (patient) => {
|
||||||
|
return patient
|
||||||
|
? `${patient.last_name} ${patient.first_name}`
|
||||||
|
: labels.notSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPatientField = (field) => {
|
||||||
|
return field || labels.notSpecified;
|
||||||
|
};
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
dispatch(setSelectedScheduledAppointment(null));
|
dispatch(setSelectedScheduledAppointment(null));
|
||||||
@ -26,26 +59,48 @@ const useScheduledAppointmentsViewModalUI = (cancelAppointment) => {
|
|||||||
try {
|
try {
|
||||||
await cancelAppointment(selectedScheduledAppointment.id);
|
await cancelAppointment(selectedScheduledAppointment.id);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: 'Прием отменен',
|
message: "Прием отменен",
|
||||||
placement: 'topRight',
|
placement: "topRight",
|
||||||
description: 'Прием успешно отменен.',
|
description: "Прием успешно отменен.",
|
||||||
})
|
});
|
||||||
onCancel();
|
onCancel();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: 'Ошибка',
|
message: "Ошибка",
|
||||||
description: error.data?.message || 'Не удалось отменить прием.',
|
description: error.data?.message || "Не удалось отменить прием.",
|
||||||
placement: 'topRight',
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConvertToAppointment = () => {
|
||||||
|
if (selectedScheduledAppointment) {
|
||||||
|
dispatch(
|
||||||
|
openModalWithScheduledData({
|
||||||
|
id: selectedScheduledAppointment.id,
|
||||||
|
patient_id: selectedScheduledAppointment.patient?.id,
|
||||||
|
type_id: selectedScheduledAppointment.type?.id,
|
||||||
|
appointment_datetime: selectedScheduledAppointment.scheduled_datetime,
|
||||||
})
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedScheduledAppointment,
|
selectedScheduledAppointment,
|
||||||
|
modalWidth,
|
||||||
blockStyle,
|
blockStyle,
|
||||||
|
footerRowStyle,
|
||||||
|
footerButtonStyle,
|
||||||
|
labels,
|
||||||
|
visible,
|
||||||
getDateString,
|
getDateString,
|
||||||
|
getAppointmentTime,
|
||||||
|
getPatientName,
|
||||||
|
getPatientField,
|
||||||
onCancel,
|
onCancel,
|
||||||
cancelScheduledAppointment,
|
cancelScheduledAppointment,
|
||||||
|
handleConvertToAppointment,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
import {BuildOutlined, TableOutlined} from "@ant-design/icons";
|
|
||||||
import {Select, Tooltip} from "antd";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import {cacheInfo} from "../../Utils/cachedInfoUtils.js";
|
|
||||||
import {ViewModPropType} from "../../Types/viewModPropType.js";
|
|
||||||
|
|
||||||
const {Option} = Select;
|
|
||||||
|
|
||||||
const SelectViewMode = ({viewMode, setViewMode, localStorageKey, toolTipText, viewModes}) => {
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
title={toolTipText}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
value={viewMode}
|
|
||||||
onChange={value => {
|
|
||||||
setViewMode(value);
|
|
||||||
cacheInfo(localStorageKey, value);
|
|
||||||
}}
|
|
||||||
style={{width: "100%"}}
|
|
||||||
>
|
|
||||||
{viewModes.map(viewMode => (
|
|
||||||
<Option key={viewMode.value} value={viewMode.value}>
|
|
||||||
{viewMode.icon}
|
|
||||||
{viewMode.label}
|
|
||||||
</Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectViewMode.propTypes = {
|
|
||||||
viewMode: PropTypes.string.isRequired,
|
|
||||||
setViewMode: PropTypes.func.isRequired,
|
|
||||||
localStorageKey: PropTypes.string.isRequired,
|
|
||||||
toolTipText: PropTypes.string.isRequired,
|
|
||||||
viewModes: PropTypes.arrayOf(ViewModPropType).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SelectViewMode;
|
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { Select, Tooltip } from "antd";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { ViewModPropType } from "../../../Types/viewModPropType.js";
|
||||||
|
import useSelectViewModeUI from "./useSelectViewModeUI.js";
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
|
const SelectViewMode = ({ viewMode, setViewMode, localStorageKey, toolTipText, viewModes }) => {
|
||||||
|
const { selectStyle, handleChange } = useSelectViewModeUI({ setViewMode, localStorageKey });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={toolTipText}>
|
||||||
|
<Select value={viewMode} onChange={handleChange} style={selectStyle}>
|
||||||
|
{viewModes.map((viewMode) => (
|
||||||
|
<Option key={viewMode.value} value={viewMode.value}>
|
||||||
|
{viewMode.icon}
|
||||||
|
{viewMode.label}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SelectViewMode.propTypes = {
|
||||||
|
viewMode: PropTypes.string.isRequired,
|
||||||
|
setViewMode: PropTypes.func.isRequired,
|
||||||
|
localStorageKey: PropTypes.string.isRequired,
|
||||||
|
toolTipText: PropTypes.string.isRequired,
|
||||||
|
viewModes: PropTypes.arrayOf(ViewModPropType).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectViewMode;
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { cacheInfo } from "../../../Utils/cachedInfoUtils.js";
|
||||||
|
|
||||||
|
const useSelectViewModeUI = ({ setViewMode, localStorageKey }) => {
|
||||||
|
const selectStyle = {
|
||||||
|
width: "100%",
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (value) => {
|
||||||
|
setViewMode(value);
|
||||||
|
cacheInfo(localStorageKey, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectStyle,
|
||||||
|
handleChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSelectViewModeUI;
|
||||||
Loading…
x
Reference in New Issue
Block a user