refactor: AppointmentsPage: Refactoring UI logic
Перенос UI-логики из AppointmentsPage в useAppointments. Удален useAppointmentsUI.js.
This commit is contained in:
parent
7a2733cda6
commit
118ae84930
@ -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,
|
||||
});
|
||||
|
||||
|
||||
@ -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 (
|
||||
<Result
|
||||
status="error"
|
||||
title="Ошибка"
|
||||
@ -49,62 +71,62 @@ const AppointmentsPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography.Title level={1}><CalendarOutlined/> Приемы</Typography.Title>
|
||||
{appointmentsData.isLoading ? (
|
||||
<LoadingIndicator/>
|
||||
<Typography.Title level={1}><CalendarOutlined /> Приемы</Typography.Title>
|
||||
{isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : (
|
||||
<>
|
||||
<Row justify="end" style={{marginBottom: 10, marginRight: "2.4rem"}}>
|
||||
<Row justify="end" style={{ marginBottom: 10, marginRight: "2.4rem" }}>
|
||||
<Space direction={"vertical"}>
|
||||
<Tag color={"blue"} style={{width: "100%"}}>
|
||||
<Tag color={"blue"} style={{ width: "100%" }}>
|
||||
<Badge status={"processing"}
|
||||
text={
|
||||
<span style={appointmentsPageUI.badgeTextStyle}>
|
||||
Запланированный прием
|
||||
</span>
|
||||
<span style={badgeTextStyle}>
|
||||
Запланированный прием
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</Tag>
|
||||
<Tag color={"green"} style={{width: "100%"}}>
|
||||
<Tag color={"green"} style={{ width: "100%" }}>
|
||||
<Badge status={"success"}
|
||||
text={
|
||||
<span style={appointmentsPageUI.badgeTextStyle}>
|
||||
Прошедший прием
|
||||
</span>
|
||||
<span style={badgeTextStyle}>
|
||||
Прошедший прием
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</Tag>
|
||||
</Space>
|
||||
</Row>
|
||||
<Splitter
|
||||
style={appointmentsPageUI.splitterStyle}
|
||||
style={splitterStyle}
|
||||
min={200}
|
||||
max={400}
|
||||
initial={appointmentsPageUI.siderWidth}
|
||||
onChange={appointmentsPageUI.setSiderWidth}
|
||||
initial={siderWidth}
|
||||
onChange={handleSetSiderWidth}
|
||||
>
|
||||
<Splitter.Panel
|
||||
style={appointmentsPageUI.splitterContentPanelStyle}
|
||||
style={splitterContentPanelStyle}
|
||||
defaultSize="80%"
|
||||
min="25%"
|
||||
max="90%"
|
||||
>
|
||||
<AppointmentsCalendarTab/>
|
||||
<AppointmentsCalendarTab />
|
||||
</Splitter.Panel>
|
||||
|
||||
{appointmentsPageUI.showSplitterPanel && (
|
||||
{showSplitterPanel && (
|
||||
<Splitter.Panel
|
||||
style={appointmentsPageUI.splitterSiderPanelStyle}
|
||||
style={splitterSiderPanelStyle}
|
||||
defaultSize="20%"
|
||||
min="20%"
|
||||
max="75%"
|
||||
>
|
||||
<Typography.Title level={3} style={appointmentsPageUI.siderTitleStyle}>
|
||||
<Typography.Title level={3} style={siderTitleStyle}>
|
||||
Предстоящие события
|
||||
</Typography.Title>
|
||||
{appointmentsPageUI.upcomingEvents.length ? (
|
||||
{upcomingEvents.length ? (
|
||||
<List
|
||||
dataSource={appointmentsPageUI.upcomingEvents.sort((a, b) =>
|
||||
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 = () => {
|
||||
<Space direction="vertical" size={2}>
|
||||
<Space>
|
||||
{item.appointment_datetime ? (
|
||||
<ClockCircleOutlined style={{color: "#52c41a"}}/>
|
||||
<ClockCircleOutlined style={{ color: "#52c41a" }} />
|
||||
) : (
|
||||
<CalendarOutlined style={{color: "#1890ff"}}/>
|
||||
<CalendarOutlined style={{ color: "#1890ff" }} />
|
||||
)}
|
||||
<Typography.Text strong>
|
||||
{dayjs(item.appointment_datetime || item.scheduled_datetime).format('DD.MM.YYYY HH:mm')}
|
||||
@ -155,43 +177,43 @@ const AppointmentsPage = () => {
|
||||
)}
|
||||
</Splitter>
|
||||
<div
|
||||
style={appointmentsPageUI.siderButtonContainerStyle}
|
||||
onMouseEnter={appointmentsPageUI.handleHoverSider}
|
||||
onMouseLeave={appointmentsPageUI.handleLeaveSider}
|
||||
style={siderButtonContainerStyle}
|
||||
onMouseEnter={handleHoverSider}
|
||||
onMouseLeave={handleLeaveSider}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={appointmentsPageUI.handleToggleSider}
|
||||
icon={appointmentsPageUI.collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
|
||||
style={appointmentsPageUI.siderButtonStyle}
|
||||
onClick={handleToggleSider}
|
||||
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
style={siderButtonStyle}
|
||||
>
|
||||
{appointmentsPageUI.siderButtonText}
|
||||
{siderButtonText}
|
||||
</Button>
|
||||
</div>
|
||||
<FloatButton.Group
|
||||
placement={"left"}
|
||||
trigger="hover"
|
||||
type="primary"
|
||||
icon={<PlusOutlined/>}
|
||||
icon={<PlusOutlined />}
|
||||
tooltip="Создать"
|
||||
>
|
||||
<FloatButton
|
||||
icon={<PlusOutlined/>}
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => dispatch(openModal())}
|
||||
tooltip="Прием"
|
||||
/>
|
||||
<FloatButton
|
||||
icon={<CalendarOutlined/>}
|
||||
onClick={appointmentsPageUI.openCreateScheduledAppointmentModal}
|
||||
icon={<CalendarOutlined />}
|
||||
onClick={openCreateScheduledAppointmentModal}
|
||||
tooltip="Запланированный прием"
|
||||
/>
|
||||
</FloatButton.Group>
|
||||
|
||||
<AppointmentFormModal/>
|
||||
<AppointmentViewModal/>
|
||||
<ScheduledAppointmentFormModal/>
|
||||
<ScheduledAppointmentsViewModal/>
|
||||
<AppointmentsListModal/>
|
||||
<AppointmentFormModal />
|
||||
<AppointmentViewModal />
|
||||
<ScheduledAppointmentFormModal />
|
||||
<ScheduledAppointmentsViewModal />
|
||||
<AppointmentsListModal />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
Loading…
x
Reference in New Issue
Block a user