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,7 +1,6 @@
import { createApi } from "@reduxjs/toolkit/query/react"; import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQueryWithAuth } from "./baseQuery.js"; import { baseQueryWithAuth } from "./baseQuery.js";
export const lensIssuesApi = createApi({ export const lensIssuesApi = createApi({
reducerPath: 'lensIssuesApi', reducerPath: 'lensIssuesApi',
baseQuery: baseQueryWithAuth, baseQuery: baseQueryWithAuth,
@ -18,24 +17,34 @@ export const lensIssuesApi = createApi({
start_date: startDate || undefined, start_date: startDate || undefined,
end_date: endDate || undefined, end_date: endDate || undefined,
}, },
providesTags: ['LensIssues'],
}), }),
providesTags: ['LensIssue'], providesTags: ['LensIssues'],
transformResponse: (response) => { transformResponse: (response) => {
if (!response || !Array.isArray(response.issues)) { if (!response) {
console.warn('Unexpected lens issues API response:', response); console.warn('Empty lens issues API response:', response);
return { issues: [], total_count: 0 }; return { issues: [], total_count: 0 };
} }
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; 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({ addLensIssues: builder.mutation({
query: (lensIssues) => ({ query: (lensIssues) => ({
url: `/lens_issues/`, url: '/lens_issues/',
method: 'POST', 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 HomePage = () => {
const homePageData = useHomePage(); 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) { if (homePageData.isError) {
return ( return (
@ -94,7 +100,7 @@ const HomePage = () => {
<Statistic <Statistic
title="Приемы за месяц" title="Приемы за месяц"
value={ value={
homePageData.appointments.filter((a) => dayjs(a.appointment_datetime).isSame(dayjs(), "month")).length homePageData.appointments.length
} }
/> />
</Card> </Card>
@ -107,7 +113,7 @@ const HomePage = () => {
</Row> </Row>
<Card <Card
title={`События на сегодня (${dayjs().format("DD.MM.YYYY")})`} title={`События на сегодня (${dayjs().tz("Europe/Moscow").format("DD.MM.YYYY")})`}
style={homePageUI.sectionStyle} style={homePageUI.sectionStyle}
> >
<List <List
@ -119,7 +125,7 @@ const HomePage = () => {
> >
<Space> <Space>
<Typography.Text strong> <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>
<Typography.Text> <Typography.Text>
{item.patient ? `${item.patient.last_name} ${item.patient.first_name}` : "Без пациента"} -{" "} {item.patient ? `${item.patient.last_name} ${item.patient.first_name}` : "Без пациента"} -{" "}

View File

@ -1,14 +1,17 @@
import {Grid} from "antd"; import {Grid} from "antd";
import {useEffect, useMemo} from "react"; import {useEffect, useMemo} from "react";
import dayjs from "dayjs"; import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
import isBetween from "dayjs/plugin/isBetween"; import isBetween from "dayjs/plugin/isBetween";
import {useSelector} from "react-redux"; // Import isBetween plugin
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(isBetween); 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 screens = useBreakpoint();
const containerStyle = {padding: screens.xs ? 16 : 24}; const containerStyle = {padding: screens.xs ? 16 : 24};
@ -23,10 +26,10 @@ const useHomePageUI = (appointments, scheduledAppointments, patients) => {
}, []); }, []);
const todayEvents = useMemo(() => { const todayEvents = useMemo(() => {
return [...appointments, ...scheduledAppointments].filter((event) => return [...upcomingAppointments, ...upcomingScheduledAppointments].filter((event) =>
dayjs(event.appointment_datetime || event.scheduled_datetime).isSame(dayjs(), "day") dayjs(event.appointment_datetime || event.scheduled_datetime).isSame(dayjs(), "day")
); );
}, [appointments, scheduledAppointments]); }, [upcomingAppointments, upcomingScheduledAppointments]);
const upcomingBirthdays = useMemo(() => { const upcomingBirthdays = useMemo(() => {
return patients.filter((p) => return patients.filter((p) =>
@ -38,7 +41,12 @@ const useHomePageUI = (appointments, scheduledAppointments, patients) => {
const data = Array(7).fill(0); const data = Array(7).fill(0);
appointments appointments
.filter((app) => .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) => { .forEach((app) => {
const dayIndex = dayjs(app.appointment_datetime).day(); const dayIndex = dayjs(app.appointment_datetime).day();
@ -51,7 +59,12 @@ const useHomePageUI = (appointments, scheduledAppointments, patients) => {
const data = Array(7).fill(0); const data = Array(7).fill(0);
scheduledAppointments scheduledAppointments
.filter((app) => .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) => { .forEach((app) => {
const dayIndex = dayjs(app.scheduled_datetime).day(); const dayIndex = dayjs(app.scheduled_datetime).day();
@ -68,7 +81,7 @@ const useHomePageUI = (appointments, scheduledAppointments, patients) => {
x: {title: {display: true, text: "День недели"}}, x: {title: {display: true, text: "День недели"}},
}, },
plugins: { plugins: {
legend: { display: false }, legend: {display: true, position: "top"},
title: {display: true, text: "Приемы за неделю"}, title: {display: true, text: "Приемы за неделю"},
}, },
}; };

View File

@ -20,6 +20,7 @@ import LensIssueFormModal from "./Components/LensIssueFormModal/LensIssueFormMod
import SelectViewMode from "../../Widgets/SelectViewMode/SelectViewMode.jsx"; import SelectViewMode from "../../Widgets/SelectViewMode/SelectViewMode.jsx";
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
import useIssues from "./useIssues.js"; import useIssues from "./useIssues.js";
import { useMemo } from "react";
const { Title } = Typography; const { Title } = Typography;
const { useBreakpoint } = Grid; 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"), label: dayjs(issue.issue_date).format("DD.MM.YYYY"),
children: ( children: (
<Row gutter={[16, 16]} align="middle"> <Row gutter={[16, 16]} align="middle">
@ -107,7 +108,7 @@ const IssuesPage = () => {
</Col> </Col>
</Row> </Row>
), ),
})); })), [issuesData]);
const TimeLineView = () => { const TimeLineView = () => {
const paginatedItems = timeLineItems.slice( const paginatedItems = timeLineItems.slice(
@ -129,7 +130,7 @@ const IssuesPage = () => {
<Pagination <Pagination
current={issuesData.currentPage} current={issuesData.currentPage}
pageSize={issuesData.pageSize} pageSize={issuesData.pageSize}
total={timeLineItems.length} total={issuesData.total_count}
onChange={issuesData.handlePaginationChange} onChange={issuesData.handlePaginationChange}
showSizeChanger={true} showSizeChanger={true}
pageSizeOptions={["5", "10", "20", "50"]} pageSizeOptions={["5", "10", "20", "50"]}

View File

@ -40,17 +40,19 @@ const useIssues = () => {
data: issuesData = {issues: [], total_count: 0}, data: issuesData = {issues: [], total_count: 0},
isLoading: isIssuesLoading, isLoading: isIssuesLoading,
isError: isIssuesError, isError: isIssuesError,
error: issuesError error: issuesError,
refetch
} = useGetLensIssuesQuery({ } = useGetLensIssuesQuery({
page: currentPage, page: currentPage,
pageSize, pageSize,
search: searchText || undefined, search: searchText || undefined,
sortOrder: 'desc', sortOrder: 'desc',
startDate: startFilterDate || undefined, startDate: startFilterDate ? dayjs(startFilterDate).format('YYYY-MM-DD') : undefined,
endDate: endFilterDate || undefined, endDate: endFilterDate ? dayjs(endFilterDate).format('YYYY-MM-DD') : undefined,
}, { }, {
pollingInterval: 20000, pollingInterval: 20000,
}); });
const {data: patients = [], isLoading: isPatientsLoading, isError: isPatientsError} = useGetAllPatientsQuery(); const {data: patients = [], isLoading: isPatientsLoading, isError: isPatientsError} = useGetAllPatientsQuery();
const {data: lenses = [], isLoading: isLensesLoading, isError: isLensesError} = useGetNotIssuedLensesQuery(); const {data: lenses = [], isLoading: isLensesLoading, isError: isLensesError} = useGetNotIssuedLensesQuery();
const [addIssue, {isLoading: isAdding}] = useAddLensIssuesMutation(); const [addIssue, {isLoading: isAdding}] = useAddLensIssuesMutation();
@ -108,19 +110,23 @@ const useIssues = () => {
const handleSearch = () => { const handleSearch = () => {
dispatch(setSearchText(tempSearchText)); dispatch(setSearchText(tempSearchText));
dispatch(setCurrentPage(1)); dispatch(setCurrentPage(1));
refetch();
}; };
const handleClearSearch = () => { const handleClearSearch = () => {
setTempSearchText(''); setTempSearchText('');
dispatch(setSearchText('')); dispatch(setSearchText(''));
dispatch(setCurrentPage(1)); dispatch(setCurrentPage(1));
refetch();
}; };
const handleSetViewMode = (mode) => dispatch(setViewMode(mode)); const handleSetViewMode = (mode) => dispatch(setViewMode(mode));
const handleSetCurrentPage = (page) => dispatch(setCurrentPage(page)); const handleSetCurrentPage = (page) => {
dispatch(setCurrentPage(page));
};
const handleSetPageSize = (size) => { const handleSetPageSize = (size) => {
dispatch(setPageSize(size)); dispatch(setPageSize(size));
dispatch(setCurrentPage(1));
}; };
const handleCloseModal = () => dispatch(closeModal()); const handleCloseModal = () => dispatch(closeModal());
const handleSelectIssue = (issue) => dispatch(selectIssue(issue)); const handleSelectIssue = (issue) => dispatch(selectIssue(issue));
@ -134,6 +140,7 @@ const useIssues = () => {
const handlePaginationChange = (page, pageSize) => { const handlePaginationChange = (page, pageSize) => {
handleSetCurrentPage(page); handleSetCurrentPage(page);
handleSetPageSize(pageSize); handleSetPageSize(pageSize);
refetch();
}; };
const handleFilterDateChange = (dates) => { const handleFilterDateChange = (dates) => {
@ -142,6 +149,7 @@ const useIssues = () => {
dispatch(setStartFilterDate(start.toISOString())); dispatch(setStartFilterDate(start.toISOString()));
dispatch(setEndFilterDate(end.toISOString())); dispatch(setEndFilterDate(end.toISOString()));
dispatch(setCurrentPage(1)); dispatch(setCurrentPage(1));
refetch();
} }
}; };
@ -149,6 +157,7 @@ const useIssues = () => {
dispatch(setStartFilterDate(null)); dispatch(setStartFilterDate(null));
dispatch(setEndFilterDate(null)); dispatch(setEndFilterDate(null));
dispatch(setCurrentPage(1)); dispatch(setCurrentPage(1));
refetch();
}; };
const pagination = { const pagination = {
@ -158,18 +167,22 @@ const useIssues = () => {
showSizeChanger: true, showSizeChanger: true,
pageSizeOptions: ["5", "10", "20", "50"], pageSizeOptions: ["5", "10", "20", "50"],
onChange: handlePaginationChange, onChange: handlePaginationChange,
onShowSizeChange: handlePaginationChange,
}; };
const handleSubmitFormModal = async (issueDate, patientId, lensId) => { const handleSubmitFormModal = async (issueDate, patientId, lensId) => {
dispatch(closeModal()); dispatch(closeModal());
try { 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({ notification.success({
message: "Линза выдана", message: "Линза выдана",
description: "Линза успешно выдана пациенту.", description: "Линза успешно выдана пациенту.",
placement: "topRight", placement: "topRight",
}); });
refetch();
} catch (error) { } catch (error) {
console.error('Add lens issue error:', error);
notification.error({ notification.error({
message: "Ошибка выдачи линзы", message: "Ошибка выдачи линзы",
description: error?.data?.detail || "Не удалось выдать линзу пациенту.", description: error?.data?.detail || "Не удалось выдать линзу пациенту.",
@ -180,6 +193,7 @@ const useIssues = () => {
return { return {
issues: issuesData.issues, issues: issuesData.issues,
total_count: issuesData.total_count,
patients, patients,
lenses, lenses,
isLoading, isLoading,