feat: lensIssuesApi

fix(IssuesPage): Исправлены фильтры и пагинация.
This commit is contained in:
Андрей Дувакин 2025-06-08 14:18:05 +05:00
parent f0a712eb9d
commit b8bc7023a0
5 changed files with 98 additions and 55 deletions

View File

@ -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'],
}),
}),
});

View File

@ -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 = () => {
<Statistic
title="Приемы за месяц"
value={
homePageData.appointments.filter((a) => dayjs(a.appointment_datetime).isSame(dayjs(), "month")).length
homePageData.appointments.length
}
/>
</Card>
@ -107,7 +113,7 @@ const HomePage = () => {
</Row>
<Card
title={`События на сегодня (${dayjs().format("DD.MM.YYYY")})`}
title={`События на сегодня (${dayjs().tz("Europe/Moscow").format("DD.MM.YYYY")})`}
style={homePageUI.sectionStyle}
>
<List
@ -119,7 +125,7 @@ const HomePage = () => {
>
<Space>
<Typography.Text strong>
{dayjs(item.appointment_datetime || item.scheduled_datetime).format("HH:mm")}
{dayjs(item.appointment_datetime || item.scheduled_datetime).tz("Europe/Moscow").format("HH:mm")}
</Typography.Text>
<Typography.Text>
{item.patient ? `${item.patient.last_name} ${item.patient.first_name}` : "Без пациента"} -{" "}

View File

@ -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: "Приемы за неделю"},
},
};

View File

@ -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: (
<Row gutter={[16, 16]} align="middle">
@ -107,7 +108,7 @@ const IssuesPage = () => {
</Col>
</Row>
),
}));
})), [issuesData]);
const TimeLineView = () => {
const paginatedItems = timeLineItems.slice(
@ -129,7 +130,7 @@ const IssuesPage = () => {
<Pagination
current={issuesData.currentPage}
pageSize={issuesData.pageSize}
total={timeLineItems.length}
total={issuesData.total_count}
onChange={issuesData.handlePaginationChange}
showSizeChanger={true}
pageSizeOptions={["5", "10", "20", "50"]}

View File

@ -1,11 +1,11 @@
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { notification } from "antd";
import {useEffect, useState} from "react";
import {useDispatch, useSelector} from "react-redux";
import {notification} from "antd";
import {
useAddLensIssuesMutation,
useGetLensIssuesQuery,
} from "../../../Api/lensIssuesApi.js";
import { useGetAllPatientsQuery } from "../../../Api/patientsApi.js";
import {useGetAllPatientsQuery} from "../../../Api/patientsApi.js";
import {
closeModal,
openModal,
@ -17,7 +17,7 @@ import {
setStartFilterDate,
setViewMode
} from "../../../Redux/Slices/lensIssuesSlice.js";
import { getCachedInfo } from "../../../Utils/cachedInfoUtils.js";
import {getCachedInfo} from "../../../Utils/cachedInfoUtils.js";
import dayjs from "dayjs";
import {useGetNotIssuedLensesQuery} from "../../../Api/lensesApi.js";
@ -37,23 +37,25 @@ const useIssues = () => {
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,