refactor: appointments UI
Перемещены и переименованы компоненты Appointments.
This commit is contained in:
parent
c3d77738a7
commit
04242d63f1
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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} // Добавляем
|
||||||
|
|||||||
@ -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;
|
||||||
@ -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;
|
||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
@ -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,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user