начал делать главную страницу
This commit is contained in:
parent
2ad1711d15
commit
6ef548514f
30
web-app/package-lock.json
generated
30
web-app/package-lock.json
generated
@ -16,10 +16,12 @@
|
|||||||
"antd-dayjs-webpack-plugin": "^1.0.6",
|
"antd-dayjs-webpack-plugin": "^1.0.6",
|
||||||
"antd-mask-input": "^2.0.7",
|
"antd-mask-input": "^2.0.7",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
|
"chart.js": "^4.4.9",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"jodit-react": "^5.2.19",
|
"jodit-react": "^5.2.19",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.1.1",
|
"react-router-dom": "^7.1.1",
|
||||||
@ -801,6 +803,12 @@
|
|||||||
"url": "https://github.com/sponsors/nzakas"
|
"url": "https://github.com/sponsors/nzakas"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@rc-component/async-validator": {
|
"node_modules/@rc-component/async-validator": {
|
||||||
"version": "5.0.4",
|
"version": "5.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz",
|
||||||
@ -2019,6 +2027,18 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.4.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz",
|
||||||
|
"integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/classnames": {
|
"node_modules/classnames": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||||
@ -4739,6 +4759,16 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-chartjs-2": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": "^4.1.1",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "18.3.1",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
|
|||||||
@ -18,10 +18,12 @@
|
|||||||
"antd-dayjs-webpack-plugin": "^1.0.6",
|
"antd-dayjs-webpack-plugin": "^1.0.6",
|
||||||
"antd-mask-input": "^2.0.7",
|
"antd-mask-input": "^2.0.7",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
|
"chart.js": "^4.4.9",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"jodit-react": "^5.2.19",
|
"jodit-react": "^5.2.19",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.1.1",
|
"react-router-dom": "^7.1.1",
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import PrivateRoute from "./PrivateRoute.jsx";
|
|||||||
import LoginPage from "../Components/Pages/LoginPage/LoginPage.jsx";
|
import LoginPage from "../Components/Pages/LoginPage/LoginPage.jsx";
|
||||||
import MainLayout from "../Components/Layouts/MainLayout.jsx";
|
import MainLayout from "../Components/Layouts/MainLayout.jsx";
|
||||||
import PatientsPage from "../Components/Pages/PatientsPage/PatientsPage.jsx";
|
import PatientsPage from "../Components/Pages/PatientsPage/PatientsPage.jsx";
|
||||||
import HomePage from "../Components/Pages/HomePage.jsx";
|
import HomePage from "../Components/Pages/HomePage/HomePage.jsx";
|
||||||
import LensesSetsPage from "../Components/Pages/LensesSetsPage/LensesSetsPage.jsx";
|
import LensesSetsPage from "../Components/Pages/LensesSetsPage/LensesSetsPage.jsx";
|
||||||
import IssuesPage from "../Components/Pages/IssuesPage/IssuesPage.jsx";
|
import IssuesPage from "../Components/Pages/IssuesPage/IssuesPage.jsx";
|
||||||
import AppointmentsPage from "../Components/Pages/AppointmentsPage/AppointmentsPage.jsx";
|
import AppointmentsPage from "../Components/Pages/AppointmentsPage/AppointmentsPage.jsx";
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
MenuFoldOutlined,
|
MenuFoldOutlined,
|
||||||
MenuUnfoldOutlined,
|
MenuUnfoldOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
ClockCircleOutlined
|
ClockCircleOutlined, DatabaseOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/AppointmentsCalendarTab.jsx";
|
import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/AppointmentsCalendarTab.jsx";
|
||||||
import useAppointmentsUI from "./useAppointmentsUI.js";
|
import useAppointmentsUI from "./useAppointmentsUI.js";
|
||||||
@ -53,6 +53,7 @@ const AppointmentsPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Typography.Title level={1}><CalendarOutlined/> Приемы</Typography.Title>
|
||||||
{appointmentsData.isLoading ? (
|
{appointmentsData.isLoading ? (
|
||||||
<LoadingIndicator/>
|
<LoadingIndicator/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const HomePage = () => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default HomePage;
|
|
||||||
214
web-app/src/Components/Pages/HomePage/HomePage.jsx
Normal file
214
web-app/src/Components/Pages/HomePage/HomePage.jsx
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
import { Button, Card, Col, List, Row, Space, Statistic, Typography, Alert, Result } from "antd";
|
||||||
|
import {
|
||||||
|
HomeOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
CalendarOutlined,
|
||||||
|
UserAddOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import useHomePage from "./useHomePage.js";
|
||||||
|
import useHomePageUI from "./useHomePageUI.js";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||||
|
import { Bar } from "react-chartjs-2";
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
BarElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
} from "chart.js";
|
||||||
|
import AppointmentFormModal from "../AppointmentsPage/Components/AppointmentFormModal/AppointmentFormModal.jsx";
|
||||||
|
import ScheduledAppointmentFormModal from "../AppointmentsPage/Components/ScheduledAppintmentFormModal/ScheduledAppointmentFormModal.jsx";
|
||||||
|
import AppointmentViewModal from "../AppointmentsPage/Components/AppointmentViewModal/AppointmentViewModal.jsx";
|
||||||
|
import ScheduledAppointmentsViewModal from "../AppointmentsPage/Components/ScheduledAppointmentsViewModal/ScheduledAppointmentsViewModal.jsx";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
|
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
const {
|
||||||
|
patients,
|
||||||
|
appointments,
|
||||||
|
scheduledAppointments,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
handleEventClick,
|
||||||
|
handleCreateAppointment,
|
||||||
|
handleCreateScheduledAppointment,
|
||||||
|
handleCreatePatient,
|
||||||
|
handleCancelModal,
|
||||||
|
} = useHomePage();
|
||||||
|
const { containerStyle, sectionStyle, cardStyle, listItemStyle, buttonStyle, chartContainerStyle, isMobile, todayEvents, upcomingBirthdays, appointmentsByDay } = useHomePageUI(appointments, scheduledAppointments, patients);
|
||||||
|
const selectedAppointment = useSelector((state) => state.appointmentsUI.selectedAppointment);
|
||||||
|
// const selectedScheduledAppointment = useSelector((state) => state.appointmentsUI.selectedScheduledAppointment);
|
||||||
|
|
||||||
|
const chartData = {
|
||||||
|
labels: ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"],
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Приемы",
|
||||||
|
data: appointmentsByDay,
|
||||||
|
backgroundColor: "#1890ff",
|
||||||
|
borderColor: "#096dd9",
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const chartOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: { beginAtZero: true, title: { display: true, text: "Количество приемов" } },
|
||||||
|
x: { title: { display: true, text: "День недели" } },
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
title: { display: true, text: "Приемы за неделю" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Result
|
||||||
|
status="error"
|
||||||
|
title="Ошибка"
|
||||||
|
subTitle="Произошла ошибка при загрузке данных"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={containerStyle}>
|
||||||
|
{isLoading ? (
|
||||||
|
<LoadingIndicator />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Typography.Title level={1}>
|
||||||
|
<HomeOutlined /> Главная страница
|
||||||
|
</Typography.Title>
|
||||||
|
|
||||||
|
{/* Быстрые действия */}
|
||||||
|
<div style={sectionStyle}>
|
||||||
|
<Space direction={isMobile ? "vertical" : "horizontal"} size="middle" style={{ width: "100%" }}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={handleCreateAppointment}
|
||||||
|
style={buttonStyle}
|
||||||
|
>
|
||||||
|
Новый прием
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<CalendarOutlined />}
|
||||||
|
onClick={handleCreateScheduledAppointment}
|
||||||
|
style={buttonStyle}
|
||||||
|
>
|
||||||
|
Запланировать
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<UserAddOutlined />}
|
||||||
|
onClick={handleCreatePatient}
|
||||||
|
style={buttonStyle}
|
||||||
|
>
|
||||||
|
Добавить пациента
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Статистика */}
|
||||||
|
<Row gutter={[16, 16]} style={sectionStyle}>
|
||||||
|
<Col span={isMobile ? 24 : 8}>
|
||||||
|
<Card style={cardStyle}>
|
||||||
|
<Statistic title="Пациенты" value={patients.length} />
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col span={isMobile ? 24 : 8}>
|
||||||
|
<Card style={cardStyle}>
|
||||||
|
<Statistic
|
||||||
|
title="Приемы за месяц"
|
||||||
|
value={
|
||||||
|
appointments.filter((a) => dayjs(a.appointment_datetime).isSame(dayjs(), "month")).length
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col span={isMobile ? 24 : 8}>
|
||||||
|
<Card style={cardStyle}>
|
||||||
|
<Statistic title="Запланировано" value={scheduledAppointments.length} />
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
{/* События на сегодня */}
|
||||||
|
<Card
|
||||||
|
title={`События на сегодня (${dayjs().format("DD.MM.YYYY")})`}
|
||||||
|
style={sectionStyle}
|
||||||
|
>
|
||||||
|
<List
|
||||||
|
dataSource={todayEvents}
|
||||||
|
renderItem={(item) => (
|
||||||
|
<List.Item
|
||||||
|
onClick={() => handleEventClick(item)}
|
||||||
|
style={listItemStyle}
|
||||||
|
>
|
||||||
|
<Space>
|
||||||
|
<Typography.Text strong>
|
||||||
|
{dayjs(item.appointment_datetime || item.scheduled_datetime).format("HH:mm")}
|
||||||
|
</Typography.Text>
|
||||||
|
<Typography.Text>
|
||||||
|
{item.patient ? `${item.patient.last_name} ${item.patient.first_name}` : "Без пациента"} -{" "}
|
||||||
|
{item.type?.title || "Не указан"}
|
||||||
|
</Typography.Text>
|
||||||
|
</Space>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{todayEvents.length === 0 && (
|
||||||
|
<Typography.Text type="secondary">Нет событий на сегодня</Typography.Text>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Уведомления */}
|
||||||
|
<Card title="Уведомления" style={sectionStyle}>
|
||||||
|
<Space direction="vertical" style={{ width: "100%" }}>
|
||||||
|
{upcomingBirthdays.map((p) => (
|
||||||
|
<Alert
|
||||||
|
key={p.id}
|
||||||
|
message={`День рождения: ${p.last_name} ${p.first_name} - ${dayjs(p.birthday).format("DD.MM")}`}
|
||||||
|
type="info"
|
||||||
|
showIcon
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{upcomingBirthdays.length === 0 && (
|
||||||
|
<Typography.Text type="secondary">Нет уведомлений</Typography.Text>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* График */}
|
||||||
|
<Card title="Статистика приемов" style={sectionStyle}>
|
||||||
|
<div style={{ ...chartContainerStyle, height: 300 }}>
|
||||||
|
<Bar data={chartData} options={chartOptions} />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Модальные окна */}
|
||||||
|
<AppointmentFormModal onCancel={handleCancelModal} />
|
||||||
|
<ScheduledAppointmentFormModal />
|
||||||
|
<AppointmentViewModal
|
||||||
|
visible={!!selectedAppointment}
|
||||||
|
onCancel={handleCancelModal}
|
||||||
|
/>
|
||||||
|
<ScheduledAppointmentsViewModal />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HomePage;
|
||||||
137
web-app/src/Components/Pages/HomePage/useHomePage.js
Normal file
137
web-app/src/Components/Pages/HomePage/useHomePage.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { useGetAppointmentsQuery } from "../../../Api/appointmentsApi.js";
|
||||||
|
import { useGetScheduledAppointmentsQuery } from "../../../Api/scheduledAppointmentsApi.js";
|
||||||
|
import { useGetPatientsQuery } from "../../../Api/patientsApi.js";
|
||||||
|
import { notification } from "antd";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import {
|
||||||
|
setSelectedAppointment,
|
||||||
|
setSelectedScheduledAppointment,
|
||||||
|
openModal,
|
||||||
|
openScheduledModal,
|
||||||
|
closeModal,
|
||||||
|
} from "../../../Redux/Slices/appointmentsSlice.js";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import isBetween from "dayjs/plugin/isBetween";
|
||||||
|
import {useGetAppointmentTypesQuery} from "../../../Api/appointmentTypesApi.js"; // Import isBetween plugin
|
||||||
|
|
||||||
|
dayjs.extend(isBetween); // Extend dayjs with isBetween
|
||||||
|
|
||||||
|
const useHomePage = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: appointments = [],
|
||||||
|
isLoading: isLoadingAppointments,
|
||||||
|
isError: isErrorAppointments,
|
||||||
|
} = useGetAppointmentsQuery(undefined, {
|
||||||
|
pollingInterval: 20000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: scheduledAppointments = [],
|
||||||
|
isLoading: isLoadingScheduledAppointments,
|
||||||
|
isError: isErrorScheduledAppointments,
|
||||||
|
} = useGetScheduledAppointmentsQuery(undefined, {
|
||||||
|
pollingInterval: 20000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: patients = [],
|
||||||
|
isLoading: isLoadingPatients,
|
||||||
|
isError: isErrorPatients,
|
||||||
|
} = useGetPatientsQuery(undefined, {
|
||||||
|
pollingInterval: 20000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: appointmentTypes = [],
|
||||||
|
isLoading: isLoadingAppointmentTypes,
|
||||||
|
isError: isErrorAppointmentTypes,
|
||||||
|
} = useGetAppointmentTypesQuery(undefined, {
|
||||||
|
pollingInterval: 20000,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isErrorAppointments) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка",
|
||||||
|
description: "Ошибка загрузки приемов.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isErrorScheduledAppointments) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка",
|
||||||
|
description: "Ошибка загрузки запланированных приемов.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isErrorPatients) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка",
|
||||||
|
description: "Ошибка загрузки пациентов.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isErrorAppointmentTypes) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка",
|
||||||
|
description: "Ошибка загрузки типов приемов.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [isErrorAppointments, isErrorScheduledAppointments, isErrorPatients, isErrorAppointmentTypes]);
|
||||||
|
|
||||||
|
const handleEventClick = (event) => {
|
||||||
|
if (event.appointment_datetime) {
|
||||||
|
dispatch(setSelectedAppointment(event));
|
||||||
|
} else {
|
||||||
|
dispatch(setSelectedScheduledAppointment(event));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateAppointment = () => {
|
||||||
|
dispatch(openModal());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateScheduledAppointment = () => {
|
||||||
|
dispatch(openScheduledModal());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreatePatient = () => {
|
||||||
|
notification.info({
|
||||||
|
message: "В разработке",
|
||||||
|
description: "Функция добавления пациента будет доступна в будущем.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancelModal = () => {
|
||||||
|
dispatch(closeModal());
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
patients,
|
||||||
|
appointments,
|
||||||
|
scheduledAppointments,
|
||||||
|
appointmentTypes,
|
||||||
|
isLoading:
|
||||||
|
isLoadingAppointments ||
|
||||||
|
isLoadingScheduledAppointments ||
|
||||||
|
isLoadingPatients ||
|
||||||
|
isLoadingAppointmentTypes,
|
||||||
|
isError:
|
||||||
|
isErrorAppointments ||
|
||||||
|
isErrorScheduledAppointments ||
|
||||||
|
isErrorPatients ||
|
||||||
|
isErrorAppointmentTypes,
|
||||||
|
handleEventClick,
|
||||||
|
handleCreateAppointment,
|
||||||
|
handleCreateScheduledAppointment,
|
||||||
|
handleCreatePatient,
|
||||||
|
handleCancelModal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useHomePage;
|
||||||
59
web-app/src/Components/Pages/HomePage/useHomePageUI.js
Normal file
59
web-app/src/Components/Pages/HomePage/useHomePageUI.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Grid } from "antd";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import isBetween from "dayjs/plugin/isBetween"; // Import isBetween plugin
|
||||||
|
|
||||||
|
dayjs.extend(isBetween); // Extend dayjs with isBetween
|
||||||
|
|
||||||
|
const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
|
const useHomePageUI = (appointments, scheduledAppointments, patients) => {
|
||||||
|
const screens = useBreakpoint();
|
||||||
|
|
||||||
|
const containerStyle = { padding: screens.xs ? 16 : 24 };
|
||||||
|
const sectionStyle = { marginBottom: 24 };
|
||||||
|
const cardStyle = { height: "100%" };
|
||||||
|
const listItemStyle = { cursor: "pointer", padding: "12px", borderRadius: 4 };
|
||||||
|
const buttonStyle = { width: screens.xs ? "100%" : "auto" };
|
||||||
|
const chartContainerStyle = { padding: 16, background: "#fff", borderRadius: 4 };
|
||||||
|
|
||||||
|
const todayEvents = useMemo(() => {
|
||||||
|
return [...appointments, ...scheduledAppointments].filter((event) =>
|
||||||
|
dayjs(event.appointment_datetime || event.scheduled_datetime).isSame(dayjs(), "day")
|
||||||
|
);
|
||||||
|
}, [appointments, scheduledAppointments]);
|
||||||
|
|
||||||
|
const upcomingBirthdays = useMemo(() => {
|
||||||
|
return patients.filter((p) =>
|
||||||
|
dayjs(p.birthday).isBetween(dayjs(), dayjs().add(7, "day"), "day", "[]")
|
||||||
|
);
|
||||||
|
}, [patients]);
|
||||||
|
|
||||||
|
const appointmentsByDay = useMemo(() => {
|
||||||
|
const data = Array(7).fill(0);
|
||||||
|
appointments
|
||||||
|
.filter((app) =>
|
||||||
|
dayjs(app.appointment_datetime).isBetween(dayjs().startOf("week"), dayjs().endOf("week"), "day", "[]")
|
||||||
|
)
|
||||||
|
.forEach((app) => {
|
||||||
|
const dayIndex = dayjs(app.appointment_datetime).day();
|
||||||
|
data[dayIndex === 0 ? 6 : dayIndex - 1]++;
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}, [appointments]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
containerStyle,
|
||||||
|
sectionStyle,
|
||||||
|
cardStyle,
|
||||||
|
listItemStyle,
|
||||||
|
buttonStyle,
|
||||||
|
chartContainerStyle,
|
||||||
|
isMobile: screens.xs,
|
||||||
|
todayEvents,
|
||||||
|
upcomingBirthdays,
|
||||||
|
appointmentsByDay,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useHomePageUI;
|
||||||
Loading…
x
Reference in New Issue
Block a user