сделал просмотр приемов и запланированных приемов по клику на ячейку календаря

This commit is contained in:
Андрей Дувакин 2025-06-01 20:48:57 +05:00
parent 55369ac1a4
commit b541bef7aa
5 changed files with 130 additions and 53 deletions

View File

@ -1,21 +1,19 @@
import {Button, FloatButton, Result, Tabs, Typography} from "antd"; import { Button, FloatButton, Result, Tabs, Typography } from "antd";
import {Splitter} from "antd"; import { Splitter } from "antd";
import {CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined, PlusOutlined} from "@ant-design/icons"; import { CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined, PlusOutlined } from "@ant-design/icons";
import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/AppointmentsCalendarTab.jsx"; import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/AppointmentsCalendarTab.jsx";
import AppointmentsTableTab from "./Components/AppointmentTableTab/AppointmentsTableTab.jsx"; import AppointmentsTableTab from "./Components/AppointmentTableTab/AppointmentsTableTab.jsx";
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.jsx";
import AppointmentFormModal import AppointmentFormModal from "./Components/AppointmentFormModal/AppointmentFormModal.jsx";
from "./Components/AppointmentFormModal/AppointmentFormModal.jsx"; import { useDispatch } from "react-redux";
import {useDispatch} from "react-redux"; import { closeModal, openModal } from "../../../Redux/Slices/appointmentsSlice.js";
import {closeModal, openModal} from "../../../Redux/Slices/appointmentsSlice.js"; import AppointmentViewModal from "./Components/AppointmentViewModal/AppointmentViewModal.jsx";
import AppointmentViewModal
from "./Components/AppointmentViewModal/AppointmentViewModal.jsx";
import ScheduledAppointmentFormModal from "./Components/ScheduledAppintmentFormModal/ScheduledAppointmentFormModal.jsx"; import ScheduledAppointmentFormModal from "./Components/ScheduledAppintmentFormModal/ScheduledAppointmentFormModal.jsx";
import ScheduledAppointmentsViewModal import ScheduledAppointmentsViewModal from "./Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx";
from "./Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx"; import AppointmentsListModal from "./Components/AppointmentsListModal/AppointmentsListModal.jsx";
const AppointmentsPage = () => { const AppointmentsPage = () => {
const appointmentsData = useAppointments(); const appointmentsData = useAppointments();
@ -30,14 +28,14 @@ const AppointmentsPage = () => {
{ {
key: "1", key: "1",
label: "Календарь приемов", label: "Календарь приемов",
children: <AppointmentsCalendarTab/>, children: <AppointmentsCalendarTab />,
icon: <CalendarOutlined/>, icon: <CalendarOutlined />,
}, },
{ {
key: "2", key: "2",
label: "Таблица приемов", label: "Таблица приемов",
children: <AppointmentsTableTab/>, children: <AppointmentsTableTab />,
icon: <TableOutlined/>, icon: <TableOutlined />,
}, },
]; ];
@ -52,7 +50,7 @@ const AppointmentsPage = () => {
return ( return (
<> <>
{appointmentsData.isLoading ? ( {appointmentsData.isLoading ? (
<LoadingIndicator/> <LoadingIndicator />
) : ( ) : (
<> <>
<Splitter <Splitter
@ -68,7 +66,7 @@ const AppointmentsPage = () => {
min="25%" min="25%"
max="90%" max="90%"
> >
<Tabs defaultActiveKey="1" items={items}/> <Tabs defaultActiveKey="1" items={items} />
</Splitter.Panel> </Splitter.Panel>
{appointmentsPageUI.showSplitterPanel && ( {appointmentsPageUI.showSplitterPanel && (
@ -83,7 +81,7 @@ const AppointmentsPage = () => {
</Typography.Title> </Typography.Title>
{appointmentsPageUI.upcomingEvents.length ? ( {appointmentsPageUI.upcomingEvents.length ? (
<ul> <ul>
{appointmentsPageUI.upcomingEvents.map(app => ( {appointmentsPageUI.upcomingEvents.map((app) => (
<li key={app.id}> <li key={app.id}>
{dayjs(app.appointment_datetime || app.scheduled_datetime) {dayjs(app.appointment_datetime || app.scheduled_datetime)
.format('DD.MM.YYYY HH:mm')} - .format('DD.MM.YYYY HH:mm')} -
@ -105,7 +103,7 @@ const AppointmentsPage = () => {
<Button <Button
type="primary" type="primary"
onClick={appointmentsPageUI.handleToggleSider} onClick={appointmentsPageUI.handleToggleSider}
icon={appointmentsPageUI.collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>} icon={appointmentsPageUI.collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
style={appointmentsPageUI.siderButtonStyle} style={appointmentsPageUI.siderButtonStyle}
> >
{appointmentsPageUI.siderButtonText} {appointmentsPageUI.siderButtonText}
@ -115,32 +113,29 @@ const AppointmentsPage = () => {
placement={"left"} placement={"left"}
trigger="hover" trigger="hover"
type="primary" type="primary"
icon={<PlusOutlined/>} icon={<PlusOutlined />}
tooltip="Создать" tooltip="Создать"
> >
<FloatButton <FloatButton
icon={<PlusOutlined/>} icon={<PlusOutlined />}
onClick={() => dispatch(openModal())} onClick={() => dispatch(openModal())}
tooltip="Прием" tooltip="Прием"
/> />
<FloatButton <FloatButton
icon={<CalendarOutlined/>} icon={<CalendarOutlined />}
onClick={appointmentsPageUI.openCreateScheduledAppointmentModal} onClick={appointmentsPageUI.openCreateScheduledAppointmentModal}
tooltip="Запланированный прием" tooltip="Запланированный прием"
/> />
</FloatButton.Group> </FloatButton.Group>
<AppointmentFormModal <AppointmentFormModal onCancel={handleCancelModal} />
onCancel={handleCancelModal}
/>
<AppointmentViewModal <AppointmentViewModal
visible={appointmentsPageUI.selectedAppointment !== null} visible={appointmentsPageUI.selectedAppointment !== null}
onCancel={appointmentsPageUI.handleCancelViewModal} onCancel={appointmentsPageUI.handleCancelViewModal}
/> />
<ScheduledAppointmentFormModal />
<ScheduledAppointmentFormModal/> <ScheduledAppointmentsViewModal />
<ScheduledAppointmentsViewModal/> <AppointmentsListModal />
</> </>
)} )}
</> </>

View File

@ -1,8 +1,9 @@
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 "../../../../Widgets/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";
const AppointmentsCalendarTab = () => { const AppointmentsCalendarTab = () => {
const appointmentsData = useAppointments(); const appointmentsData = useAppointments();
@ -40,6 +41,7 @@ const AppointmentsCalendarTab = () => {
onSelect={appointmentsCalendarUI.onSelect} onSelect={appointmentsCalendarUI.onSelect}
cellRender={dateCellRender} cellRender={dateCellRender}
/> />
<AppointmentsListModal />
</div> </div>
); );
}; };

View File

@ -1,11 +1,11 @@
import {useDispatch, useSelector} from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import dayjs from "dayjs"; import dayjs from "dayjs";
import utc from "dayjs/plugin/utc"; import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone"; import timezone from "dayjs/plugin/timezone";
import {Grid} from "antd"; import { Grid } from "antd";
import { import {
openModal, setSelectedAppointment, openAppointmentsListModal,
setSelectedAppointments, setSelectedAppointment,
setSelectedScheduledAppointment, setSelectedScheduledAppointment,
} from "../../../../../Redux/Slices/appointmentsSlice.js"; } from "../../../../../Redux/Slices/appointmentsSlice.js";
@ -13,31 +13,26 @@ dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
dayjs.tz.setDefault('Europe/Moscow'); dayjs.tz.setDefault('Europe/Moscow');
const {useBreakpoint} = Grid; const { useBreakpoint } = Grid;
const useAppointmentCalendarUI = (appointments, scheduledAppointments) => { const useAppointmentCalendarUI = (appointments, scheduledAppointments) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const selectedDate = dayjs.tz(useSelector(state => state.appointmentsUI.selectedDate), 'Europe/Moscow'); const selectedDate = dayjs.tz(useSelector((state) => state.appointmentsUI.selectedDate), 'Europe/Moscow');
const screens = useBreakpoint(); const screens = useBreakpoint();
const fullScreenCalendar = !screens.xs; const fullScreenCalendar = !screens.xs;
const calendarContainerStyle = {padding: 20}; const calendarContainerStyle = { padding: 20 };
const onSelect = (date) => { const onSelect = (date) => {
const selectedDateStr = date.format('YYYY-MM-DD'); const selectedDateStr = date.format('YYYY-MM-DD');
// dispatch(setSelectedDate(selectedDateStr)); const appointmentsForDate = appointments.filter((app) =>
const appointmentsForDate = appointments.filter(app =>
dayjs(app.appointment_datetime).format('YYYY-MM-DD') === selectedDateStr dayjs(app.appointment_datetime).format('YYYY-MM-DD') === selectedDateStr
); );
const scheduledForDate = scheduledAppointments.filter((app) =>
const scheduledForDate = scheduledAppointments.filter(app =>
dayjs(app.scheduled_datetime).format('YYYY-MM-DD') === selectedDateStr dayjs(app.scheduled_datetime).format('YYYY-MM-DD') === selectedDateStr
); );
dispatch(openAppointmentsListModal([...appointmentsForDate, ...scheduledForDate]));
dispatch(setSelectedAppointments([...appointmentsForDate, ...scheduledForDate]));
dispatch(openModal());
}; };
const onOpenAppointmentModal = (appointment) => { const onOpenAppointmentModal = (appointment) => {
@ -50,9 +45,8 @@ const useAppointmentCalendarUI = (appointments, scheduledAppointments) => {
const getAppointmentsByListAndDate = (list, value, isScheduled = false) => { const getAppointmentsByListAndDate = (list, value, isScheduled = false) => {
const date = value.format('YYYY-MM-DD'); const date = value.format('YYYY-MM-DD');
return list.filter(app => return list.filter((app) =>
dayjs(isScheduled ? app.scheduled_datetime : app.appointment_datetime) dayjs(isScheduled ? app.scheduled_datetime : app.appointment_datetime).format('YYYY-MM-DD') === date
.format('YYYY-MM-DD') === date
); );
}; };

View File

@ -0,0 +1,78 @@
import { Button, List, Modal, Typography } from "antd";
import { useDispatch, useSelector } from "react-redux";
import dayjs from "dayjs";
import {
closeAppointmentsListModal,
setSelectedAppointment,
setSelectedScheduledAppointment
} from "../../../../../Redux/Slices/appointmentsSlice.js";
const AppointmentsListModal = () => {
const dispatch = useDispatch();
const { appointmentsListModalVisible, selectedDateAppointments } = useSelector(
(state) => state.appointmentsUI
);
const handleCancel = () => {
dispatch(closeAppointmentsListModal());
};
const handleItemClick = (appointment) => {
if (appointment.appointment_datetime) {
dispatch(setSelectedAppointment(appointment));
} else {
dispatch(setSelectedScheduledAppointment(appointment));
}
};
return (
<Modal
title="Приемы на выбранную дату"
open={appointmentsListModalVisible}
onCancel={handleCancel}
footer={null}
width={600}
>
<Typography.Title level={4}>Список приемов</Typography.Title>
{selectedDateAppointments.length ? (
<List
dataSource={selectedDateAppointments}
renderItem={(item) => (
<List.Item
onClick={() => handleItemClick(item)}
style={{ cursor: "pointer", padding: "8px 0" }}
>
<div>
<p>
<b>Время:</b>{" "}
{item.appointment_datetime
? dayjs(item.appointment_datetime).format("DD.MM.YYYY HH:mm")
: item.scheduled_datetime
? dayjs(item.scheduled_datetime).format("DD.MM.YYYY HH:mm")
: "Не указано"}
</p>
<p>
<b>Тип:</b>{" "}
{item.appointment_datetime ? "Прием" : "Запланированный прием"}
</p>
<p>
<b>Пациент:</b>{" "}
{item.patient
? `${item.patient.last_name} ${item.patient.first_name}`
: "Не указан"}
</p>
</div>
</List.Item>
)}
/>
) : (
<p>Нет приемов на эту дату</p>
)}
<Button onClick={handleCancel} style={{ marginTop: 16 }}>
Закрыть
</Button>
</Modal>
);
};
export default AppointmentsListModal;

View File

@ -2,14 +2,15 @@ import { createSlice } from '@reduxjs/toolkit';
const initialState = { const initialState = {
modalVisible: false, modalVisible: false,
collapsed: true, collapsed: false,
siderWidth: 300, siderWidth: 300,
hovered: false, hovered: false,
selectedAppointment: null, selectedAppointment: null,
selectedScheduledAppointment: null, selectedScheduledAppointment: null,
scheduledModalVisible: false, scheduledModalVisible: false,
scheduledData: null, scheduledData: null,
selectedAppointments: [], // Новое поле для хранения массива выбранных приемов appointmentsListModalVisible: false,
selectedDateAppointments: [],
}; };
const appointmentsSlice = createSlice({ const appointmentsSlice = createSlice({
@ -47,8 +48,13 @@ const appointmentsSlice = createSlice({
state.modalVisible = true; state.modalVisible = true;
state.scheduledData = action.payload; state.scheduledData = action.payload;
}, },
setSelectedAppointments(state, action) { openAppointmentsListModal(state, action) {
state.selectedAppointments = action.payload; state.appointmentsListModalVisible = true;
state.selectedDateAppointments = action.payload;
},
closeAppointmentsListModal(state) {
state.appointmentsListModalVisible = false;
state.selectedDateAppointments = [];
}, },
}, },
}); });
@ -57,13 +63,15 @@ export const {
openModal, openModal,
closeModal, closeModal,
toggleSider, toggleSider,
setSiderWidth,
setHovered, setHovered,
setSelectedAppointment, setSelectedAppointment,
setSelectedScheduledAppointment, setSelectedScheduledAppointment,
openScheduledModal, openScheduledModal,
closeScheduledModal, closeScheduledModal,
openModalWithScheduledData, openModalWithScheduledData,
setSelectedAppointments, openAppointmentsListModal,
closeAppointmentsListModal,
} = appointmentsSlice.actions; } = appointmentsSlice.actions;
export default appointmentsSlice.reducer; export default appointmentsSlice.reducer;