feat: Исправление фильтрации и UI для выдачи линз

This commit is contained in:
Андрей Дувакин 2025-06-08 14:35:03 +05:00
parent b8bc7023a0
commit 0a7fd16a29
6 changed files with 56 additions and 61 deletions

View File

@ -1,3 +1,4 @@
from datetime import date
from typing import Optional, Sequence, Tuple, Literal
from sqlalchemy import select, desc, or_, func, asc
@ -17,8 +18,8 @@ class LensIssuesRepository:
limit: int = 10,
search: Optional[str] = None,
sort_order: Literal["asc", "desc"] = "desc",
start_date: Optional[str] = None,
end_date: Optional[str] = None
start_date: Optional[date] = None,
end_date: Optional[date] = None
) -> Tuple[Sequence[LensIssue], int]:
stmt = (
select(LensIssue)
@ -62,6 +63,7 @@ class LensIssuesRepository:
)
if start_date:
count_stmt = count_stmt.filter(LensIssue.issue_date >= start_date)
if end_date:
count_stmt = count_stmt.filter(LensIssue.issue_date <= end_date)

View File

@ -1,3 +1,4 @@
from datetime import date
from typing import Optional, Literal
from fastapi import APIRouter, Depends, Query
@ -23,8 +24,8 @@ async def get_all_lens_issues(
page_size: int = Query(10, ge=1, le=100),
search: Optional[str] = Query(None),
sort_order: Literal["asc", "desc"] = Query("desc"),
start_date: Optional[str] = Query(None),
end_date: Optional[str] = Query(None),
start_date: Optional[date] = Query(None),
end_date: Optional[date] = Query(None),
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
):

View File

@ -1,3 +1,4 @@
from datetime import date
from typing import Optional, Literal, Tuple
from fastapi import HTTPException
@ -28,8 +29,8 @@ class LensIssuesService:
limit: int = 10,
search: Optional[str] = None,
sort_order: Literal["asc", "desc"] = "desc",
start_date: Optional[str] = None,
end_date: Optional[str] = None
start_date: Optional[date] = None,
end_date: Optional[date] = None
) -> Tuple[list[LensIssueEntity], int]:
lens_issues, total_count = await self.lens_issues_repository.get_all(
skip=skip,

View File

@ -25,7 +25,6 @@ import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.tz.setDefault("Europe/Moscow");
const { useBreakpoint } = Grid;
@ -35,13 +34,13 @@ const useAppointments = () => {
const { collapsed, siderWidth, hovered, selectedAppointment } = useSelector(state => state.appointmentsUI);
const screens = useBreakpoint();
const [currentMonth, setCurrentMonth] = useState(dayjs().tz("Europe/Moscow").startOf('month'));
const [currentMonth, setCurrentMonth] = useState(dayjs().startOf('month'));
const startDate = currentMonth.startOf('month').tz("Europe/Moscow").format('YYYY-MM-DD');
const endDate = currentMonth.endOf('month').tz("Europe/Moscow").format('YYYY-MM-DD');
const startDate = currentMonth.startOf('month').format('YYYY-MM-DD');
const endDate = currentMonth.endOf('month').format('YYYY-MM-DD');
const handleMonthChange = (newMonth) => {
setCurrentMonth(dayjs(newMonth).tz("Europe/Moscow").startOf('month'));
setCurrentMonth(dayjs(newMonth).startOf('month'));
};
const {
@ -155,7 +154,7 @@ const useAppointments = () => {
const upcomingEvents = useMemo(() =>
[...upcomingAppointments, ...upcomingScheduledAppointments]
.sort((a, b) => dayjs(a.appointment_datetime || a.scheduled_datetime).tz("Europe/Moscow") - dayjs(b.appointment_datetime || b.scheduled_datetime).tz("Europe/Moscow"))
.sort((a, b) => dayjs(a.appointment_datetime || a.scheduled_datetime) - dayjs(b.appointment_datetime || b.scheduled_datetime))
.slice(0, 5),
[upcomingAppointments, upcomingScheduledAppointments]
);

View File

@ -13,17 +13,17 @@ import {
Pagination,
Result
} from "antd";
import { DatabaseOutlined, PlusOutlined, UnorderedListOutlined } from "@ant-design/icons";
import {DatabaseOutlined, PlusOutlined, UnorderedListOutlined} from "@ant-design/icons";
import LensIssueViewModal from "./Components/LensIssueViewModal/LensIssueViewModal.jsx";
import dayjs from "dayjs";
import LensIssueFormModal from "./Components/LensIssueFormModal/LensIssueFormModal.jsx";
import SelectViewMode from "../../Widgets/SelectViewMode/SelectViewMode.jsx";
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
import useIssues from "./useIssues.js";
import { useMemo } from "react";
import {useMemo} from "react";
const { Title } = Typography;
const { useBreakpoint } = Grid;
const {Title} = Typography;
const {useBreakpoint} = Grid;
const IssuesPage = () => {
const issuesData = useIssues();
@ -33,12 +33,12 @@ const IssuesPage = () => {
{
value: "table",
label: "Таблица",
icon: <DatabaseOutlined style={issuesData.viewModIconStyle} />,
icon: <DatabaseOutlined style={issuesData.viewModIconStyle}/>,
},
{
value: "timeline",
label: "Лента",
icon: <UnorderedListOutlined style={issuesData.viewModIconStyle} />,
icon: <UnorderedListOutlined style={issuesData.viewModIconStyle}/>,
},
];
@ -87,59 +87,49 @@ const IssuesPage = () => {
/>
);
const timeLineItems = useMemo(() => issuesData.issues.map(issue => ({
label: dayjs(issue.issue_date).format("DD.MM.YYYY"),
children: (
<Row gutter={[16, 16]} align="middle">
<Col xs={24} sm={24} md={13}>
<p style={{ textAlign: "right" }}>Пациент: {issue.patient.last_name} {issue.patient.first_name}</p>
</Col>
<Col xs={24} sm={24} md={5}>
<p style={{ textAlign: "right" }}>Линза: {issue.lens.side} {issue.lens.diameter}</p>
</Col>
<Col xs={24} sm={24} md={6}>
<Button
type="dashed"
onClick={() => issuesData.handleSelectIssue(issue)}
style={{ marginRight: 40 }}
>
Подробнее
</Button>
</Col>
</Row>
),
})), [issuesData]);
const TimeLineView = () => {
const paginatedItems = timeLineItems.slice(
(issuesData.currentPage - 1) * issuesData.pageSize,
issuesData.currentPage * issuesData.pageSize
);
const timeLineItems = useMemo(() => issuesData.issues.map(issue => ({
label: dayjs(issue.issue_date).format("DD.MM.YYYY"),
children: (
<Row gutter={[16, 16]} align="middle">
<Col xs={24} sm={24} md={13}>
<p style={{textAlign: "right"}}>Пациент: {issue.patient.last_name} {issue.patient.first_name}</p>
</Col>
<Col xs={24} sm={24} md={5}>
<p style={{textAlign: "right"}}>Линза: {issue.lens.side} {issue.lens.diameter}</p>
</Col>
<Col xs={24} sm={24} md={6}>
<Button
type="dashed"
onClick={() => issuesData.handleSelectIssue(issue)}
style={{marginRight: 40}}
>
Подробнее
</Button>
</Col>
</Row>
),
})), []);
return (
<>
<Timeline
items={paginatedItems}
items={timeLineItems}
mode={screens.xs ? "left" : "right"}
/>
<Row
style={{ textAlign: "center", marginTop: 20 }}
style={{textAlign: "center", marginTop: 20}}
align="middle"
justify="end"
>
<Pagination
current={issuesData.currentPage}
pageSize={issuesData.pageSize}
total={issuesData.total_count}
onChange={issuesData.handlePaginationChange}
showSizeChanger={true}
pageSizeOptions={["5", "10", "20", "50"]}
{...issuesData.pagination}
/>
</Row>
</>
);
};
if (issuesData.isError) return (
<Result
status="error"
@ -150,14 +140,13 @@ const IssuesPage = () => {
return (
<div style={issuesData.containerStyle}>
<Title level={1}><DatabaseOutlined /> Выдача линз</Title>
<Title level={1}><DatabaseOutlined/> Выдача линз</Title>
<Row gutter={[16, 16]} style={issuesData.filterBarStyle}>
<Col xs={24} sm={24} md={12}>
<Input
placeholder="Поиск по пациенту или врачу"
value={issuesData.tempSearchText}
onChange={(e) => issuesData.handleSetTempSearchText(e.target.value)}
onPressEnter={issuesData.handleSearch}
style={issuesData.formItemStyle}
allowClear
onClear={issuesData.handleClearSearch}
@ -199,15 +188,15 @@ const IssuesPage = () => {
</Col>
</Row>
{issuesData.isLoading ? (
<LoadingIndicator />
<LoadingIndicator/>
) : issuesData.viewMode === "table" ? (
<TableView />
<TableView/>
) : (
<TimeLineView />
<TimeLineView/>
)}
<FloatButton
icon={<PlusOutlined />}
icon={<PlusOutlined/>}
type="primary"
onClick={issuesData.handleAddIssue}
tooltip="Добавить выдачу линзы"

View File

@ -105,7 +105,10 @@ const useIssues = () => {
borderRadius: 8
};
const handleSetTempSearchText = (value) => setTempSearchText(value);
const handleSetTempSearchText = (value) => {
setTempSearchText(value);
handleSearch();
};
const handleSearch = () => {
dispatch(setSearchText(tempSearchText));