diff --git a/web-app/src/Components/Dummies/ScheduledAppintmentFormModal/useScheduledAppointmentFormModal.js b/web-app/src/Components/Dummies/ScheduledAppintmentFormModal/useScheduledAppointmentFormModal.js index cec2d02..36423e6 100644 --- a/web-app/src/Components/Dummies/ScheduledAppintmentFormModal/useScheduledAppointmentFormModal.js +++ b/web-app/src/Components/Dummies/ScheduledAppintmentFormModal/useScheduledAppointmentFormModal.js @@ -1,4 +1,4 @@ -import {useGetPatientsQuery} from "../../../Api/patientsApi.js"; +import {useGetAllPatientsQuery} from "../../../Api/patientsApi.js"; import {useGetAppointmentTypesQuery} from "../../../Api/appointmentTypesApi.js"; import {useCreateScheduledAppointmentMutation} from "../../../Api/scheduledAppointmentsApi.js"; @@ -8,7 +8,7 @@ const useScheduledAppointmentFormModal = () => { data: patients = [], isLoading: isLoadingPatients, isError: isErrorPatients, - } = useGetPatientsQuery(undefined, { + } = useGetAllPatientsQuery(undefined, { pollingInterval: 20000, }); diff --git a/web-app/src/Components/Pages/AppointmentsPage/AppointmentsPage.jsx b/web-app/src/Components/Pages/AppointmentsPage/AppointmentsPage.jsx index e28d667..eddf9ad 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/AppointmentsPage.jsx +++ b/web-app/src/Components/Pages/AppointmentsPage/AppointmentsPage.jsx @@ -1,5 +1,5 @@ -import {Badge, Button, Col, FloatButton, List, Result, Row, Space, Tag, Typography} from "antd"; -import {Splitter} from "antd"; +import { Badge, Button, FloatButton, List, Result, Row, Space, Tag, Typography } from "antd"; +import { Splitter } from "antd"; import { CalendarOutlined, MenuFoldOutlined, @@ -8,12 +8,11 @@ import { ClockCircleOutlined, } from "@ant-design/icons"; import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/AppointmentsCalendarTab.jsx"; -import useAppointmentsUI from "./useAppointmentsUI.js"; import useAppointments from "./useAppointments.js"; import dayjs from 'dayjs'; import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; import AppointmentFormModal from "../../Dummies/AppointmentFormModal/AppointmentFormModal.jsx"; -import {useDispatch} from "react-redux"; +import { useDispatch } from "react-redux"; import { openModal, setSelectedAppointment, @@ -27,8 +26,31 @@ import ScheduledAppointmentsViewModal import AppointmentsListModal from "./Components/AppointmentsListModal/AppointmentsListModal.jsx"; const AppointmentsPage = () => { - const appointmentsData = useAppointments(); - const appointmentsPageUI = useAppointmentsUI(appointmentsData.appointments, appointmentsData.scheduledAppointments); + const { + patients, + appointments, + scheduledAppointments, + isLoading, + isError, + collapsed, + siderWidth, + hovered, + showSplitterPanel, + siderButtonText, + splitterStyle, + splitterContentPanelStyle, + splitterSiderPanelStyle, + siderTitleStyle, + siderButtonContainerStyle, + siderButtonStyle, + badgeTextStyle, + upcomingEvents, + handleToggleSider, + handleHoverSider, + handleLeaveSider, + handleSetSiderWidth, + openCreateScheduledAppointmentModal, + } = useAppointments(); const dispatch = useDispatch(); const handleEventClick = (event) => { @@ -39,7 +61,7 @@ const AppointmentsPage = () => { } }; - if (appointmentsData.isError) return ( + if (isError) return ( { return ( <> - Приемы - {appointmentsData.isLoading ? ( - + Приемы + {isLoading ? ( + ) : ( <> - + - + - Запланированный прием - + + Запланированный прием + } /> - + - Прошедший прием - + + Прошедший прием + } /> - + - {appointmentsPageUI.showSplitterPanel && ( + {showSplitterPanel && ( - + Предстоящие события - {appointmentsPageUI.upcomingEvents.length ? ( + {upcomingEvents.length ? ( + dataSource={upcomingEvents.sort((a, b) => dayjs(a.appointment_datetime || a.scheduled_datetime).diff( dayjs(b.appointment_datetime || b.scheduled_datetime) ) @@ -126,9 +148,9 @@ const AppointmentsPage = () => { {item.appointment_datetime ? ( - + ) : ( - + )} {dayjs(item.appointment_datetime || item.scheduled_datetime).format('DD.MM.YYYY HH:mm')} @@ -155,43 +177,43 @@ const AppointmentsPage = () => { )}
} + icon={} tooltip="Создать" > } + icon={} onClick={() => dispatch(openModal())} tooltip="Прием" /> } - onClick={appointmentsPageUI.openCreateScheduledAppointmentModal} + icon={} + onClick={openCreateScheduledAppointmentModal} tooltip="Запланированный прием" /> - - - - - + + + + + )} diff --git a/web-app/src/Components/Pages/AppointmentsPage/useAppointments.js b/web-app/src/Components/Pages/AppointmentsPage/useAppointments.js index 87bb150..4e05b60 100644 --- a/web-app/src/Components/Pages/AppointmentsPage/useAppointments.js +++ b/web-app/src/Components/Pages/AppointmentsPage/useAppointments.js @@ -1,20 +1,43 @@ -import {useGetAppointmentsQuery} from "../../../Api/appointmentsApi.js"; +import { useEffect, useMemo, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { notification } from "antd"; +import { Grid } from "antd"; +import { + useGetAppointmentsQuery, +} from "../../../Api/appointmentsApi.js"; +import { useGetAllPatientsQuery } from "../../../Api/patientsApi.js"; +import { + openModal, + openScheduledModal, + setHovered, + setSelectedAppointment, + setSelectedScheduledAppointment, + toggleSider +} from "../../../Redux/Slices/appointmentsSlice.js"; +import dayjs from "dayjs"; import {useGetScheduledAppointmentsQuery} from "../../../Api/scheduledAppointmentsApi.js"; -import {useGetAllPatientsQuery} from "../../../Api/patientsApi.js"; -import {notification} from "antd"; -import {useEffect} from "react"; -import {useSelector} from "react-redux"; + +const { useBreakpoint } = Grid; const useAppointments = () => { + const dispatch = useDispatch(); const { userData } = useSelector(state => state.auth); + const { + collapsed, + siderWidth, + hovered, + selectedAppointment, + } = useSelector(state => state.appointmentsUI); + const screens = useBreakpoint(); + // Data fetching const { data: appointments = [], isLoading: isLoadingAppointments, isError: isErrorAppointments, - } = useGetAppointmentsQuery((userData.id), { + } = useGetAppointmentsQuery(userData.id, { pollingInterval: 20000, }); @@ -22,7 +45,7 @@ const useAppointments = () => { data: scheduledAppointments = [], isLoading: isLoadingScheduledAppointments, isError: isErrorScheduledAppointments, - } = useGetScheduledAppointmentsQuery((userData.id), { + } = useGetScheduledAppointmentsQuery(userData.id, { pollingInterval: 20000, }); @@ -34,6 +57,76 @@ const useAppointments = () => { pollingInterval: 20000, }); + // UI state and styles + const [localSiderWidth, setLocalSiderWidth] = useState(siderWidth); + + const splitterStyle = { flex: 1 }; + const splitterContentPanelStyle = { padding: 16 }; + const splitterSiderPanelStyle = { padding: "16px", borderLeft: "1px solid #ddd", overflowY: "auto" }; + const siderTitleStyle = { marginBottom: 36 }; + const siderButtonContainerStyle = { + position: "fixed", + right: 0, + top: "50%", + transform: "translateY(-50%)", + transition: "right 0.3s ease", + zIndex: 1000, + display: screens.xs ? "none" : "block", + }; + const siderButtonStyle = { + width: hovered ? 250 : 50, + padding: hovered ? "0 20px" : "0", + overflow: "hidden", + textAlign: "left", + transition: "width 0.3s ease, padding 0.3s ease", + borderRadius: "4px 0 0 4px", + }; + const badgeTextStyle = { + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + display: "inline-block", + width: "100%", + }; + + // Handlers + const handleToggleSider = () => dispatch(toggleSider()); + const handleHoverSider = () => dispatch(setHovered(true)); + const handleLeaveSider = () => dispatch(setHovered(false)); + const handleSetSiderWidth = (width) => setLocalSiderWidth(width); + + const handleCancelViewModal = () => { + if (selectedAppointment) { + dispatch(setSelectedAppointment(null)); + } else { + dispatch(setSelectedScheduledAppointment(null)); + } + }; + + const openCreateScheduledAppointmentModal = () => { + dispatch(openScheduledModal()); + }; + + // Computed properties + const siderButtonText = useMemo(() => + hovered ? (collapsed ? "Показать предстоящие события" : "Скрыть предстоящие события") : "", + [collapsed, hovered] + ); + const showSplitterPanel = useMemo(() => !collapsed && !screens.xs, [collapsed, screens]); + + const upcomingEvents = useMemo(() => + [...appointments, ...scheduledAppointments] + .filter(app => dayjs(app.appointment_datetime || app.scheduled_datetime).isAfter(dayjs())) + .sort((a, b) => dayjs(a.appointment_datetime || a.scheduled_datetime) - dayjs(b.appointment_datetime || b.scheduled_datetime)) + .slice(0, 5), + [appointments, scheduledAppointments] + ); + + // Effects + useEffect(() => { + document.title = "Приемы"; + }, []); + useEffect(() => { if (isErrorAppointments) { notification.error({ @@ -64,6 +157,26 @@ const useAppointments = () => { scheduledAppointments, isLoading: isLoadingAppointments || isLoadingScheduledAppointments || isLoadingPatients, isError: isErrorAppointments || isErrorScheduledAppointments || isErrorPatients, + collapsed, + siderWidth: localSiderWidth, + hovered, + showSplitterPanel, + siderButtonText, + splitterStyle, + splitterContentPanelStyle, + splitterSiderPanelStyle, + siderTitleStyle, + siderButtonContainerStyle, + siderButtonStyle, + badgeTextStyle, + upcomingEvents, + selectedAppointment, + handleCancelViewModal, + handleToggleSider, + handleHoverSider, + handleLeaveSider, + handleSetSiderWidth, + openCreateScheduledAppointmentModal, }; }; diff --git a/web-app/src/Components/Pages/AppointmentsPage/useAppointmentsUI.js b/web-app/src/Components/Pages/AppointmentsPage/useAppointmentsUI.js deleted file mode 100644 index 1d53c45..0000000 --- a/web-app/src/Components/Pages/AppointmentsPage/useAppointmentsUI.js +++ /dev/null @@ -1,97 +0,0 @@ -import { useDispatch, useSelector } from "react-redux"; -import { Grid } from "antd"; -import { - setHovered, - setSelectedAppointment, - setSelectedScheduledAppointment, - toggleSider, - openScheduledModal, -} from "../../../Redux/Slices/appointmentsSlice.js"; -import { useEffect, useMemo } from "react"; -import dayjs from "dayjs"; - -const { useBreakpoint } = Grid; - -const useAppointmentsUI = (appointments, scheduledAppointments) => { - const dispatch = useDispatch(); - const { - collapsed, - siderWidth, - hovered, - selectedAppointment, - } = useSelector(state => state.appointmentsUI); - const screens = useBreakpoint(); - - useEffect(() => { - document.title = "Приемы"; - }, []); - - const splitterStyle = { flex: 1 }; - const splitterContentPanelStyle = { padding: 16 }; - const splitterSiderPanelStyle = { padding: "16px", borderLeft: "1px solid #ddd", overflowY: "auto" }; - const siderTitleStyle = { marginBottom: 36 }; - const siderButtonContainerStyle = { - position: "fixed", - right: 0, - top: "50%", - transform: "translateY(-50%)", - transition: "right 0.3s ease", - zIndex: 1000, - display: screens.xs ? "none" : "block", - }; - const siderButtonStyle = { - width: hovered ? 250 : 50, - padding: hovered ? "0 20px" : "0", - overflow: "hidden", - textAlign: "left", - transition: "width 0.3s ease, padding 0.3s ease", - borderRadius: "4px 0 0 4px", - }; - - const handleToggleSider = () => dispatch(toggleSider()); - const handleHoverSider = () => dispatch(setHovered(true)); - const handleLeaveSider = () => dispatch(setHovered(false)); - - const handleCancelViewModal = () => { - if (selectedAppointment) { - dispatch(setSelectedAppointment(null)); - } else { - dispatch(setSelectedScheduledAppointment(null)); - } - }; - - const openCreateScheduledAppointmentModal = () => { - dispatch(openScheduledModal()); - }; - - const siderButtonText = useMemo(() => hovered ? (collapsed ? "Показать предстоящие события" : "Скрыть предстоящие события") : "", [collapsed, hovered]); - const showSplitterPanel = useMemo(() => !collapsed && !screens.xs, [collapsed, screens]); - - const upcomingEvents = [...appointments, ...scheduledAppointments] - .filter(app => dayjs(app.appointment_datetime || app.scheduled_datetime).isAfter(dayjs())) - .sort((a, b) => dayjs(a.appointment_datetime || a.scheduled_datetime) - dayjs(b.appointment_datetime || b.scheduled_datetime)) - .slice(0, 5); - - return { - collapsed, - siderWidth, - hovered, - showSplitterPanel, - siderButtonText, - splitterStyle, - splitterContentPanelStyle, - splitterSiderPanelStyle, - siderTitleStyle, - siderButtonContainerStyle, - siderButtonStyle, - upcomingEvents, - selectedAppointment, - handleCancelViewModal, - handleToggleSider, - handleHoverSider, - handleLeaveSider, - openCreateScheduledAppointmentModal, - }; -}; - -export default useAppointmentsUI;