diff --git a/web-app/src/Api/lensIssuesApi.js b/web-app/src/Api/lensIssuesApi.js index 921e3c3..0180363 100644 --- a/web-app/src/Api/lensIssuesApi.js +++ b/web-app/src/Api/lensIssuesApi.js @@ -1,6 +1,5 @@ -import {createApi} from "@reduxjs/toolkit/query/react"; -import {baseQueryWithAuth} from "./baseQuery.js"; - +import { createApi } from "@reduxjs/toolkit/query/react"; +import { baseQueryWithAuth } from "./baseQuery.js"; export const lensIssuesApi = createApi({ reducerPath: 'lensIssuesApi', @@ -18,24 +17,34 @@ export const lensIssuesApi = createApi({ start_date: startDate || undefined, end_date: endDate || undefined, }, - providesTags: ['LensIssues'], }), - providesTags: ['LensIssue'], + providesTags: ['LensIssues'], transformResponse: (response) => { - if (!response || !Array.isArray(response.issues)) { - console.warn('Unexpected lens issues API response:', response); + if (!response) { + console.warn('Empty lens issues API response:', response); return { issues: [], total_count: 0 }; } - return response; + if (Array.isArray(response.results) && typeof response.count === 'number') { + return { issues: response.results, total_count: response.count }; + } + if (Array.isArray(response.issues) && typeof response.total_count === 'number') { + return response; + } + console.warn('Unexpected lens issues API response:', response); + return { issues: [], total_count: 0 }; + }, + transformErrorResponse: (response) => { + console.error('Lens issues API error:', response); + return response.data?.detail || 'Unknown error'; }, }), addLensIssues: builder.mutation({ query: (lensIssues) => ({ - url: `/lens_issues/`, + url: '/lens_issues/', method: 'POST', - body: lensIssues + body: lensIssues, }), - invalidatesTags: ['LensIssues'] + invalidatesTags: ['LensIssues'], }), }), }); diff --git a/web-app/src/Components/Pages/HomePage/HomePage.jsx b/web-app/src/Components/Pages/HomePage/HomePage.jsx index 9a81e9c..ea212aa 100644 --- a/web-app/src/Components/Pages/HomePage/HomePage.jsx +++ b/web-app/src/Components/Pages/HomePage/HomePage.jsx @@ -31,7 +31,13 @@ ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend) const HomePage = () => { const homePageData = useHomePage(); - const homePageUI = useHomePageUI(homePageData.appointments, homePageData.scheduledAppointments, homePageData.patients); + const homePageUI = useHomePageUI( + homePageData.appointments, + homePageData.scheduledAppointments, + homePageData.patients, + homePageData.upcomingAppointments, + homePageData.upcomingScheduledAppointments + ); if (homePageData.isError) { return ( @@ -94,7 +100,7 @@ const HomePage = () => { dayjs(a.appointment_datetime).isSame(dayjs(), "month")).length + homePageData.appointments.length } /> @@ -107,7 +113,7 @@ const HomePage = () => { { > - {dayjs(item.appointment_datetime || item.scheduled_datetime).format("HH:mm")} + {dayjs(item.appointment_datetime || item.scheduled_datetime).tz("Europe/Moscow").format("HH:mm")} {item.patient ? `${item.patient.last_name} ${item.patient.first_name}` : "Без пациента"} -{" "} diff --git a/web-app/src/Components/Pages/HomePage/useHomePageUI.js b/web-app/src/Components/Pages/HomePage/useHomePageUI.js index 1369638..9f36d92 100644 --- a/web-app/src/Components/Pages/HomePage/useHomePageUI.js +++ b/web-app/src/Components/Pages/HomePage/useHomePageUI.js @@ -1,32 +1,35 @@ -import { Grid } from "antd"; +import {Grid} from "antd"; import {useEffect, useMemo} from "react"; import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import timezone from "dayjs/plugin/timezone"; import isBetween from "dayjs/plugin/isBetween"; -import {useSelector} from "react-redux"; // Import isBetween plugin +dayjs.extend(utc); +dayjs.extend(timezone); dayjs.extend(isBetween); -const { useBreakpoint } = Grid; +const {useBreakpoint} = Grid; -const useHomePageUI = (appointments, scheduledAppointments, patients) => { +const useHomePageUI = (appointments, scheduledAppointments, patients, upcomingAppointments, upcomingScheduledAppointments) => { 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 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}; useEffect(() => { document.title = "Главная страница"; }, []); const todayEvents = useMemo(() => { - return [...appointments, ...scheduledAppointments].filter((event) => + return [...upcomingAppointments, ...upcomingScheduledAppointments].filter((event) => dayjs(event.appointment_datetime || event.scheduled_datetime).isSame(dayjs(), "day") ); - }, [appointments, scheduledAppointments]); + }, [upcomingAppointments, upcomingScheduledAppointments]); const upcomingBirthdays = useMemo(() => { return patients.filter((p) => @@ -38,7 +41,12 @@ const useHomePageUI = (appointments, scheduledAppointments, patients) => { const data = Array(7).fill(0); appointments .filter((app) => - dayjs(app.appointment_datetime).isBetween(dayjs().startOf("week"), dayjs().endOf("week"), "day", "[]") + dayjs(app.appointment_datetime).isBetween( + dayjs().startOf("week"), + dayjs().endOf("week"), + "day", + "[]" + ) ) .forEach((app) => { const dayIndex = dayjs(app.appointment_datetime).day(); @@ -51,7 +59,12 @@ const useHomePageUI = (appointments, scheduledAppointments, patients) => { const data = Array(7).fill(0); scheduledAppointments .filter((app) => - dayjs(app.scheduled_datetime).isBetween(dayjs().startOf("week"), dayjs().endOf("week"), "day", "[]") + dayjs(app.scheduled_datetime).isBetween( + dayjs().startOf("week"), + dayjs().endOf("week"), + "day", + "[]" + ) ) .forEach((app) => { const dayIndex = dayjs(app.scheduled_datetime).day(); @@ -64,12 +77,12 @@ const useHomePageUI = (appointments, scheduledAppointments, patients) => { responsive: true, maintainAspectRatio: false, scales: { - y: { beginAtZero: true, title: { display: true, text: "Количество приемов" } }, - x: { title: { display: true, text: "День недели" } }, + y: {beginAtZero: true, title: {display: true, text: "Количество приемов"}}, + x: {title: {display: true, text: "День недели"}}, }, plugins: { - legend: { display: false }, - title: { display: true, text: "Приемы за неделю" }, + legend: {display: true, position: "top"}, + title: {display: true, text: "Приемы за неделю"}, }, }; diff --git a/web-app/src/Components/Pages/IssuesPage/IssuesPage.jsx b/web-app/src/Components/Pages/IssuesPage/IssuesPage.jsx index 00e8b7a..55cd079 100644 --- a/web-app/src/Components/Pages/IssuesPage/IssuesPage.jsx +++ b/web-app/src/Components/Pages/IssuesPage/IssuesPage.jsx @@ -20,6 +20,7 @@ import LensIssueFormModal from "./Components/LensIssueFormModal/LensIssueFormMod import SelectViewMode from "../../Widgets/SelectViewMode/SelectViewMode.jsx"; import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; import useIssues from "./useIssues.js"; +import { useMemo } from "react"; const { Title } = Typography; const { useBreakpoint } = Grid; @@ -86,7 +87,7 @@ const IssuesPage = () => { /> ); - const timeLineItems = issuesData.issues.map(issue => ({ + const timeLineItems = useMemo(() => issuesData.issues.map(issue => ({ label: dayjs(issue.issue_date).format("DD.MM.YYYY"), children: ( @@ -107,7 +108,7 @@ const IssuesPage = () => { ), - })); + })), [issuesData]); const TimeLineView = () => { const paginatedItems = timeLineItems.slice( @@ -129,7 +130,7 @@ const IssuesPage = () => { { const [tempSearchText, setTempSearchText] = useState(searchText); const { - data: issuesData = { issues: [], total_count: 0 }, + data: issuesData = {issues: [], total_count: 0}, isLoading: isIssuesLoading, isError: isIssuesError, - error: issuesError + error: issuesError, + refetch } = useGetLensIssuesQuery({ page: currentPage, pageSize, search: searchText || undefined, sortOrder: 'desc', - startDate: startFilterDate || undefined, - endDate: endFilterDate || undefined, + startDate: startFilterDate ? dayjs(startFilterDate).format('YYYY-MM-DD') : undefined, + endDate: endFilterDate ? dayjs(endFilterDate).format('YYYY-MM-DD') : undefined, }, { pollingInterval: 20000, }); - const { data: patients = [], isLoading: isPatientsLoading, isError: isPatientsError } = useGetAllPatientsQuery(); - const { data: lenses = [], isLoading: isLensesLoading, isError: isLensesError } = useGetNotIssuedLensesQuery(); - const [addIssue, { isLoading: isAdding }] = useAddLensIssuesMutation(); + + const {data: patients = [], isLoading: isPatientsLoading, isError: isPatientsError} = useGetAllPatientsQuery(); + const {data: lenses = [], isLoading: isLensesLoading, isError: isLensesError} = useGetNotIssuedLensesQuery(); + const [addIssue, {isLoading: isAdding}] = useAddLensIssuesMutation(); const isLoading = isIssuesLoading || isPatientsLoading || isLensesLoading; const isError = isIssuesError || isPatientsError || isLensesError; @@ -93,10 +95,10 @@ const useIssues = () => { const filterDates = [startFilterDateConverted, endFilterDateConverted]; const isFilterDates = startFilterDate && endFilterDate; - const containerStyle = { padding: 20 }; - const filterBarStyle = { marginBottom: 20 }; - const formItemStyle = { width: "100%" }; - const viewModIconStyle = { marginRight: 8 }; + const containerStyle = {padding: 20}; + const filterBarStyle = {marginBottom: 20}; + const formItemStyle = {width: "100%"}; + const viewModIconStyle = {marginRight: 8}; const advancedSearchCardStyle = { marginBottom: 20, boxShadow: "0 1px 6px rgba(0, 0, 0, 0.15)", @@ -108,19 +110,23 @@ const useIssues = () => { const handleSearch = () => { dispatch(setSearchText(tempSearchText)); dispatch(setCurrentPage(1)); + refetch(); }; const handleClearSearch = () => { setTempSearchText(''); dispatch(setSearchText('')); dispatch(setCurrentPage(1)); + refetch(); }; const handleSetViewMode = (mode) => dispatch(setViewMode(mode)); - const handleSetCurrentPage = (page) => dispatch(setCurrentPage(page)); + const handleSetCurrentPage = (page) => { + dispatch(setCurrentPage(page)); + }; + const handleSetPageSize = (size) => { dispatch(setPageSize(size)); - dispatch(setCurrentPage(1)); }; const handleCloseModal = () => dispatch(closeModal()); const handleSelectIssue = (issue) => dispatch(selectIssue(issue)); @@ -134,6 +140,7 @@ const useIssues = () => { const handlePaginationChange = (page, pageSize) => { handleSetCurrentPage(page); handleSetPageSize(pageSize); + refetch(); }; const handleFilterDateChange = (dates) => { @@ -142,6 +149,7 @@ const useIssues = () => { dispatch(setStartFilterDate(start.toISOString())); dispatch(setEndFilterDate(end.toISOString())); dispatch(setCurrentPage(1)); + refetch(); } }; @@ -149,6 +157,7 @@ const useIssues = () => { dispatch(setStartFilterDate(null)); dispatch(setEndFilterDate(null)); dispatch(setCurrentPage(1)); + refetch(); }; const pagination = { @@ -158,18 +167,22 @@ const useIssues = () => { showSizeChanger: true, pageSizeOptions: ["5", "10", "20", "50"], onChange: handlePaginationChange, + onShowSizeChange: handlePaginationChange, }; const handleSubmitFormModal = async (issueDate, patientId, lensId) => { dispatch(closeModal()); try { - await addIssue({ issue_date: issueDate, patient_id: patientId, lens_id: lensId }).unwrap(); + const formattedIssueDate = dayjs(issueDate).format('YYYY-MM-DD'); + await addIssue({issue_date: formattedIssueDate, patient_id: patientId, lens_id: lensId}).unwrap(); notification.success({ message: "Линза выдана", description: "Линза успешно выдана пациенту.", placement: "topRight", }); + refetch(); } catch (error) { + console.error('Add lens issue error:', error); notification.error({ message: "Ошибка выдачи линзы", description: error?.data?.detail || "Не удалось выдать линзу пациенту.", @@ -180,6 +193,7 @@ const useIssues = () => { return { issues: issuesData.issues, + total_count: issuesData.total_count, patients, lenses, isLoading,