refactor: appointments UI

Перемещены и переименованы компоненты Appointments.
This commit is contained in:
Андрей Дувакин 2025-06-29 09:49:26 +05:00
parent c3d77738a7
commit 04242d63f1
7 changed files with 126 additions and 76 deletions

View File

@ -33,10 +33,8 @@ const MainLayout = () => {
} }
menuItems.push( menuItems.push(
mainLayoutData.getItem("Мой профиль", "profile", <UserOutlined/>, [ mainLayoutData.getItem("Перейти в профиль", "/profile", <UserOutlined/>),
mainLayoutData.getItem("Перейти в профиль", "/profile", <UserOutlined/>), mainLayoutData.getItem("Выйти", "logout", <LogoutOutlined/>)
mainLayoutData.getItem("Выйти", "logout", <LogoutOutlined/>)
])
); );
if (mainLayoutData.isUserError) { if (mainLayoutData.isUserError) {
@ -45,48 +43,101 @@ const MainLayout = () => {
return ( return (
<Layout style={{minHeight: "100vh"}}> <Layout style={{minHeight: "100vh"}}>
<Sider {mainLayoutData.screens.xs ? (
collapsible={!mainLayoutData.screens.xs} <>
collapsed={mainLayoutData.collapsed} <Content style={{
onCollapse={mainLayoutData.setCollapsed} margin: "0 8px",
style={{height: "100vh", position: "fixed", left: 0, overflow: "auto"}} padding: 24,
> flex: 1,
<div style={{display: "flex", justifyContent: "center", padding: 16}}> overflow: "auto",
<img background: "#fff",
src="/logo_rounded.png" borderRadius: 8,
alt="Логотип" marginTop: "8px"
style={{width: mainLayoutData.collapsed ? 40 : 80, transition: "width 0.2s"}} }}>
/> {mainLayoutData.isUserLoading ? (
</div> <LoadingIndicator/>
<Menu ) : (
theme="dark" <Outlet/>
selectedKeys={[location.pathname]} )}
mode="inline" </Content>
items={menuItems} <Layout.Header style={{
onClick={mainLayoutData.handleMenuClick} position: "fixed",
/> bottom: 0,
</Sider> left: 0,
right: 0,
display: "flex",
alignItems: "center",
padding: "0 16px",
background: "#001529",
zIndex: 1000
}}>
<div style={{display: "flex", alignItems: "center", paddingRight: 16}}>
<img
src="/logo_rounded.png"
alt="Логотип"
style={{width: 40}}
/>
</div>
<Menu
theme="dark"
mode="horizontal"
selectedKeys={[mainLayoutData.location.pathname]}
items={menuItems}
onClick={mainLayoutData.handleMenuClick}
style={{flex: 1, minWidth: 0}}
/>
</Layout.Header>
<Footer style={{textAlign: "center", padding: "12px 0", marginBottom: 48}}>
Visus+ © {new Date().getFullYear()}
</Footer>
</>
) : (
<>
<Sider
collapsible={!mainLayoutData.screens.xs}
collapsed={mainLayoutData.collapsed}
onCollapse={mainLayoutData.setCollapsed}
style={{height: "100vh", position: "fixed", left: 0, overflow: "auto"}}
>
<div style={{display: "flex", justifyContent: "center", padding: 16}}>
<img
src="/logo_rounded.png"
alt="Логотип"
style={{width: mainLayoutData.collapsed ? 40 : 80, transition: "width 0.2s"}}
/>
</div>
<Menu
theme="dark"
selectedKeys={[location.pathname]}
mode="inline"
items={menuItems}
onClick={mainLayoutData.handleMenuClick}
/>
</Sider>
<Layout
style={{marginLeft: mainLayoutData.collapsed ? 80 : 200, transition: "margin-left 0.2s"}}
>
<Content style={{
margin: "0 16px",
padding: 24,
minHeight: "100vh",
overflow: "auto",
background: "#fff",
borderRadius: 8,
marginTop: "15px"
}}>
{mainLayoutData.isUserLoading ? (
<LoadingIndicator/>
) : (
<Outlet/>
)}
</Content>
<Footer style={{textAlign: "center"}}>Visus+ © {new Date().getFullYear()}</Footer>
</Layout>
</>
)}
<Layout
style={{marginLeft: mainLayoutData.collapsed ? 80 : 200, transition: "margin-left 0.2s"}}
>
<Content style={{
margin: "0 16px",
padding: 24,
minHeight: "100vh",
overflow: "auto",
background: "#fff",
borderRadius: 8,
marginTop: "15px"
}}>
{mainLayoutData.isUserLoading ? (
<LoadingIndicator/>
) : (
<Outlet/>
)}
</Content>
<Footer style={{textAlign: "center"}}>Visus+ © {new Date().getFullYear()}</Footer>
</Layout>
</Layout> </Layout>
); );
}; };

View File

@ -7,7 +7,7 @@ import {
PlusOutlined, PlusOutlined,
ClockCircleOutlined, ClockCircleOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/AppointmentsCalendarTab.jsx"; import AppointmentsCalendar from "./Components/AppointmentCalendar/AppointmentsCalendar.jsx";
import useAppointments from "./useAppointments.js"; import useAppointments from "./useAppointments.js";
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
@ -87,7 +87,7 @@ const AppointmentsPage = () => {
min="25%" min="25%"
max="90%" max="90%"
> >
<AppointmentsCalendarTab <AppointmentsCalendar
currentMonth={currentMonth} currentMonth={currentMonth}
onMonthChange={handleMonthChange} onMonthChange={handleMonthChange}
appointments={appointments} // Добавляем appointments={appointments} // Добавляем

View File

@ -1,13 +1,13 @@
import {Calendar} from "antd"; import {Calendar} from "antd";
import "dayjs/locale/ru"; import "dayjs/locale/ru";
import CalendarCell from "../CalendarCell/CalendarCell.jsx"; import CalendarCell from "../CalendarCell/CalendarCell.jsx";
import useAppointmentCalendarUI from "./useAppointmentCalendarUI.js"; import useAppointmentCalendar from "./useAppointmentCalendar.js";
import AppointmentsListModal from "../AppointmentsListModal/AppointmentsListModal.jsx"; import AppointmentsListModal from "../AppointmentsListModal/AppointmentsListModal.jsx";
import dayjs from "dayjs"; import dayjs from "dayjs";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
const AppointmentsCalendarTab = ({currentMonth, onMonthChange, appointments, scheduledAppointments}) => { const AppointmentsCalendar = ({currentMonth, onMonthChange, appointments, scheduledAppointments}) => {
const appointmentsCalendarUI = useAppointmentCalendarUI(appointments, scheduledAppointments); const appointmentsCalendarUI = useAppointmentCalendar(appointments, scheduledAppointments);
const dateCellRender = (value) => { const dateCellRender = (value) => {
const appointmentsForDate = appointmentsCalendarUI.getAppointmentsByListAndDate( const appointmentsForDate = appointmentsCalendarUI.getAppointmentsByListAndDate(
@ -39,12 +39,11 @@ const AppointmentsCalendarTab = ({currentMonth, onMonthChange, appointments, sch
<div style={appointmentsCalendarUI.calendarContainerStyle}> <div style={appointmentsCalendarUI.calendarContainerStyle}>
<Calendar <Calendar
fullscreen={appointmentsCalendarUI.fullScreenCalendar} fullscreen={appointmentsCalendarUI.fullScreenCalendar}
value={currentMonth} // Используем currentMonth вместо selectedDate value={currentMonth}
onSelect={appointmentsCalendarUI.onSelect}
onPanelChange={(value, mode) => { onPanelChange={(value, mode) => {
appointmentsCalendarUI.onPanelChange(value, mode); appointmentsCalendarUI.onPanelChange(value, mode);
if (mode === "month") { if (mode === "month") {
onMonthChange(value); // Вызываем onMonthChange при смене месяца onMonthChange(value);
} }
}} }}
cellRender={dateCellRender} cellRender={dateCellRender}
@ -54,11 +53,11 @@ const AppointmentsCalendarTab = ({currentMonth, onMonthChange, appointments, sch
); );
}; };
AppointmentsCalendarTab.propTypes = { AppointmentsCalendar.propTypes = {
currentMonth: PropTypes.object.isRequired, currentMonth: PropTypes.object.isRequired,
onMonthChange: PropTypes.func.isRequired, onMonthChange: PropTypes.func.isRequired,
appointments: PropTypes.array.isRequired, appointments: PropTypes.array.isRequired,
scheduledAppointments: PropTypes.array.isRequired, scheduledAppointments: PropTypes.array.isRequired,
}; };
export default AppointmentsCalendarTab; export default AppointmentsCalendar;

View File

@ -17,7 +17,7 @@ dayjs.tz.setDefault("Europe/Moscow");
const { useBreakpoint } = Grid; const { useBreakpoint } = Grid;
const useAppointmentCalendarUI = (appointments, scheduledAppointments) => { const useAppointmentCalendar = (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");
@ -75,4 +75,4 @@ const useAppointmentCalendarUI = (appointments, scheduledAppointments) => {
}; };
}; };
export default useAppointmentCalendarUI; export default useAppointmentCalendar;

View File

@ -2,7 +2,7 @@ import {Badge, Col, Tag, Tooltip} from "antd";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import {AppointmentPropType} from "../../../../../Types/appointmentPropType.js"; import {AppointmentPropType} from "../../../../../Types/appointmentPropType.js";
import {ScheduledAppointmentPropType} from "../../../../../Types/scheduledAppointmentPropType.js"; import {ScheduledAppointmentPropType} from "../../../../../Types/scheduledAppointmentPropType.js";
import useCalendarCellUI from "./useCalendarCellUI.js"; import useCalendarCell from "./useCalendarCell.js";
const CalendarCell = ({allAppointments, onCellClick, onItemClick}) => { const CalendarCell = ({allAppointments, onCellClick, onItemClick}) => {
const { const {
@ -18,7 +18,7 @@ const CalendarCell = ({allAppointments, onCellClick, onItemClick}) => {
getBadgeText, getBadgeText,
getTagColor, getTagColor,
getBadgeStatus, getBadgeStatus,
} = useCalendarCellUI(); } = useCalendarCell();
return ( return (
<div <div

View File

@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import dayjs from "dayjs"; import dayjs from "dayjs";
const useCalendarCellUI = () => { const useCalendarCell = () => {
const containerRef = useRef(null); const containerRef = useRef(null);
const [isCompressed, setIsCompressed] = useState(false); const [isCompressed, setIsCompressed] = useState(false);
const COMPRESSION_THRESHOLD = 70; const COMPRESSION_THRESHOLD = 70;
@ -53,7 +53,7 @@ const useCalendarCellUI = () => {
const compressedCountStyle = { const compressedCountStyle = {
position: "absolute", position: "absolute",
top: 2, bottom: 12,
right: 2, right: 2,
fontSize: 10, fontSize: 10,
fontWeight: "bold", fontWeight: "bold",
@ -108,4 +108,4 @@ const useCalendarCellUI = () => {
}; };
}; };
export default useCalendarCellUI; export default useCalendarCell;

View File

@ -1,12 +1,12 @@
import { useEffect, useMemo, useState } from "react"; import {useEffect, useMemo, useState} from "react";
import { useDispatch, useSelector } from "react-redux"; import {useDispatch, useSelector} from "react-redux";
import { notification } from "antd"; import {notification} from "antd";
import { Grid } from "antd"; import {Grid} from "antd";
import { import {
useGetAppointmentsQuery, useGetAppointmentsQuery,
useGetUpcomingAppointmentsQuery, useGetUpcomingAppointmentsQuery,
} from "../../../Api/appointmentsApi.js"; } from "../../../Api/appointmentsApi.js";
import { useGetAllPatientsQuery } from "../../../Api/patientsApi.js"; import {useGetAllPatientsQuery} from "../../../Api/patientsApi.js";
import { import {
openModal, openModal,
openScheduledModal, openScheduledModal,
@ -26,12 +26,12 @@ import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
const { useBreakpoint } = Grid; const {useBreakpoint} = Grid;
const useAppointments = () => { const useAppointments = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { userData } = useSelector(state => state.auth); const {userData} = useSelector(state => state.auth);
const { collapsed, siderWidth, hovered, selectedAppointment } = useSelector(state => state.appointmentsUI); const {collapsed, siderWidth, hovered, selectedAppointment} = useSelector(state => state.appointmentsUI);
const screens = useBreakpoint(); const screens = useBreakpoint();
const [currentMonth, setCurrentMonth] = useState(dayjs().startOf('month')); const [currentMonth, setCurrentMonth] = useState(dayjs().startOf('month'));
@ -47,7 +47,7 @@ const useAppointments = () => {
data: appointments = [], data: appointments = [],
isLoading: isLoadingAppointments, isLoading: isLoadingAppointments,
isError: isErrorAppointments, isError: isErrorAppointments,
} = useGetAppointmentsQuery({ doctor_id: userData.id, start_date: startDate, end_date: endDate }, { } = useGetAppointmentsQuery({doctor_id: userData.id, start_date: startDate, end_date: endDate}, {
pollingInterval: 60000, pollingInterval: 60000,
skip: !userData.id, skip: !userData.id,
}); });
@ -56,7 +56,7 @@ const useAppointments = () => {
data: scheduledAppointments = [], data: scheduledAppointments = [],
isLoading: isLoadingScheduledAppointments, isLoading: isLoadingScheduledAppointments,
isError: isErrorScheduledAppointments, isError: isErrorScheduledAppointments,
} = useGetScheduledAppointmentsQuery({ doctor_id: userData.id, start_date: startDate, end_date: endDate }, { } = useGetScheduledAppointmentsQuery({doctor_id: userData.id, start_date: startDate, end_date: endDate}, {
pollingInterval: 60000, pollingInterval: 60000,
skip: !userData.id, skip: !userData.id,
}); });
@ -90,10 +90,10 @@ const useAppointments = () => {
const [localSiderWidth, setLocalSiderWidth] = useState(siderWidth); const [localSiderWidth, setLocalSiderWidth] = useState(siderWidth);
const splitterStyle = { flex: 1 }; const splitterStyle = {flex: 1};
const splitterContentPanelStyle = { padding: 16 }; const splitterContentPanelStyle = {padding: 16};
const splitterSiderPanelStyle = { padding: "16px", borderLeft: "1px solid #ddd", overflowY: "auto" }; const splitterSiderPanelStyle = {padding: "16px", borderLeft: "1px solid #ddd", overflowY: "auto"};
const siderTitleStyle = { marginBottom: 36 }; const siderTitleStyle = {marginBottom: 36};
const siderButtonContainerStyle = { const siderButtonContainerStyle = {
position: "fixed", position: "fixed",
right: 0, right: 0,