feat: Исправление фильтрации и UI для выдачи линз
This commit is contained in:
parent
b8bc7023a0
commit
0a7fd16a29
@ -1,3 +1,4 @@
|
|||||||
|
from datetime import date
|
||||||
from typing import Optional, Sequence, Tuple, Literal
|
from typing import Optional, Sequence, Tuple, Literal
|
||||||
|
|
||||||
from sqlalchemy import select, desc, or_, func, asc
|
from sqlalchemy import select, desc, or_, func, asc
|
||||||
@ -17,8 +18,8 @@ class LensIssuesRepository:
|
|||||||
limit: int = 10,
|
limit: int = 10,
|
||||||
search: Optional[str] = None,
|
search: Optional[str] = None,
|
||||||
sort_order: Literal["asc", "desc"] = "desc",
|
sort_order: Literal["asc", "desc"] = "desc",
|
||||||
start_date: Optional[str] = None,
|
start_date: Optional[date] = None,
|
||||||
end_date: Optional[str] = None
|
end_date: Optional[date] = None
|
||||||
) -> Tuple[Sequence[LensIssue], int]:
|
) -> Tuple[Sequence[LensIssue], int]:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(LensIssue)
|
select(LensIssue)
|
||||||
@ -62,6 +63,7 @@ class LensIssuesRepository:
|
|||||||
)
|
)
|
||||||
if start_date:
|
if start_date:
|
||||||
count_stmt = count_stmt.filter(LensIssue.issue_date >= start_date)
|
count_stmt = count_stmt.filter(LensIssue.issue_date >= start_date)
|
||||||
|
|
||||||
if end_date:
|
if end_date:
|
||||||
count_stmt = count_stmt.filter(LensIssue.issue_date <= end_date)
|
count_stmt = count_stmt.filter(LensIssue.issue_date <= end_date)
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
from datetime import date
|
||||||
from typing import Optional, Literal
|
from typing import Optional, Literal
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Query
|
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),
|
page_size: int = Query(10, ge=1, le=100),
|
||||||
search: Optional[str] = Query(None),
|
search: Optional[str] = Query(None),
|
||||||
sort_order: Literal["asc", "desc"] = Query("desc"),
|
sort_order: Literal["asc", "desc"] = Query("desc"),
|
||||||
start_date: Optional[str] = Query(None),
|
start_date: Optional[date] = Query(None),
|
||||||
end_date: Optional[str] = Query(None),
|
end_date: Optional[date] = Query(None),
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
user=Depends(get_current_user),
|
user=Depends(get_current_user),
|
||||||
):
|
):
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
from datetime import date
|
||||||
from typing import Optional, Literal, Tuple
|
from typing import Optional, Literal, Tuple
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
@ -28,8 +29,8 @@ class LensIssuesService:
|
|||||||
limit: int = 10,
|
limit: int = 10,
|
||||||
search: Optional[str] = None,
|
search: Optional[str] = None,
|
||||||
sort_order: Literal["asc", "desc"] = "desc",
|
sort_order: Literal["asc", "desc"] = "desc",
|
||||||
start_date: Optional[str] = None,
|
start_date: Optional[date] = None,
|
||||||
end_date: Optional[str] = None
|
end_date: Optional[date] = None
|
||||||
) -> Tuple[list[LensIssueEntity], int]:
|
) -> Tuple[list[LensIssueEntity], int]:
|
||||||
lens_issues, total_count = await self.lens_issues_repository.get_all(
|
lens_issues, total_count = await self.lens_issues_repository.get_all(
|
||||||
skip=skip,
|
skip=skip,
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import timezone from "dayjs/plugin/timezone";
|
|||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
dayjs.tz.setDefault("Europe/Moscow");
|
|
||||||
|
|
||||||
const { useBreakpoint } = Grid;
|
const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
@ -35,13 +34,13 @@ const useAppointments = () => {
|
|||||||
const { collapsed, siderWidth, hovered, selectedAppointment } = useSelector(state => state.appointmentsUI);
|
const { collapsed, siderWidth, hovered, selectedAppointment } = useSelector(state => state.appointmentsUI);
|
||||||
const screens = useBreakpoint();
|
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 startDate = currentMonth.startOf('month').format('YYYY-MM-DD');
|
||||||
const endDate = currentMonth.endOf('month').tz("Europe/Moscow").format('YYYY-MM-DD');
|
const endDate = currentMonth.endOf('month').format('YYYY-MM-DD');
|
||||||
|
|
||||||
const handleMonthChange = (newMonth) => {
|
const handleMonthChange = (newMonth) => {
|
||||||
setCurrentMonth(dayjs(newMonth).tz("Europe/Moscow").startOf('month'));
|
setCurrentMonth(dayjs(newMonth).startOf('month'));
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -155,7 +154,7 @@ const useAppointments = () => {
|
|||||||
|
|
||||||
const upcomingEvents = useMemo(() =>
|
const upcomingEvents = useMemo(() =>
|
||||||
[...upcomingAppointments, ...upcomingScheduledAppointments]
|
[...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),
|
.slice(0, 5),
|
||||||
[upcomingAppointments, upcomingScheduledAppointments]
|
[upcomingAppointments, upcomingScheduledAppointments]
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,17 +13,17 @@ import {
|
|||||||
Pagination,
|
Pagination,
|
||||||
Result
|
Result
|
||||||
} from "antd";
|
} 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 LensIssueViewModal from "./Components/LensIssueViewModal/LensIssueViewModal.jsx";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import LensIssueFormModal from "./Components/LensIssueFormModal/LensIssueFormModal.jsx";
|
import LensIssueFormModal from "./Components/LensIssueFormModal/LensIssueFormModal.jsx";
|
||||||
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";
|
import {useMemo} from "react";
|
||||||
|
|
||||||
const { Title } = Typography;
|
const {Title} = Typography;
|
||||||
const { useBreakpoint } = Grid;
|
const {useBreakpoint} = Grid;
|
||||||
|
|
||||||
const IssuesPage = () => {
|
const IssuesPage = () => {
|
||||||
const issuesData = useIssues();
|
const issuesData = useIssues();
|
||||||
@ -33,12 +33,12 @@ const IssuesPage = () => {
|
|||||||
{
|
{
|
||||||
value: "table",
|
value: "table",
|
||||||
label: "Таблица",
|
label: "Таблица",
|
||||||
icon: <DatabaseOutlined style={issuesData.viewModIconStyle} />,
|
icon: <DatabaseOutlined style={issuesData.viewModIconStyle}/>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "timeline",
|
value: "timeline",
|
||||||
label: "Лента",
|
label: "Лента",
|
||||||
icon: <UnorderedListOutlined style={issuesData.viewModIconStyle} />,
|
icon: <UnorderedListOutlined style={issuesData.viewModIconStyle}/>,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -87,53 +87,43 @@ const IssuesPage = () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const TimeLineView = () => {
|
||||||
const timeLineItems = useMemo(() => 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">
|
||||||
<Col xs={24} sm={24} md={13}>
|
<Col xs={24} sm={24} md={13}>
|
||||||
<p style={{ textAlign: "right" }}>Пациент: {issue.patient.last_name} {issue.patient.first_name}</p>
|
<p style={{textAlign: "right"}}>Пациент: {issue.patient.last_name} {issue.patient.first_name}</p>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={24} md={5}>
|
<Col xs={24} sm={24} md={5}>
|
||||||
<p style={{ textAlign: "right" }}>Линза: {issue.lens.side} {issue.lens.diameter}</p>
|
<p style={{textAlign: "right"}}>Линза: {issue.lens.side} {issue.lens.diameter}</p>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={24} md={6}>
|
<Col xs={24} sm={24} md={6}>
|
||||||
<Button
|
<Button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
onClick={() => issuesData.handleSelectIssue(issue)}
|
onClick={() => issuesData.handleSelectIssue(issue)}
|
||||||
style={{ marginRight: 40 }}
|
style={{marginRight: 40}}
|
||||||
>
|
>
|
||||||
Подробнее
|
Подробнее
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
),
|
),
|
||||||
})), [issuesData]);
|
})), []);
|
||||||
|
|
||||||
const TimeLineView = () => {
|
|
||||||
const paginatedItems = timeLineItems.slice(
|
|
||||||
(issuesData.currentPage - 1) * issuesData.pageSize,
|
|
||||||
issuesData.currentPage * issuesData.pageSize
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Timeline
|
<Timeline
|
||||||
items={paginatedItems}
|
items={timeLineItems}
|
||||||
mode={screens.xs ? "left" : "right"}
|
mode={screens.xs ? "left" : "right"}
|
||||||
/>
|
/>
|
||||||
<Row
|
<Row
|
||||||
style={{ textAlign: "center", marginTop: 20 }}
|
style={{textAlign: "center", marginTop: 20}}
|
||||||
align="middle"
|
align="middle"
|
||||||
justify="end"
|
justify="end"
|
||||||
>
|
>
|
||||||
<Pagination
|
<Pagination
|
||||||
current={issuesData.currentPage}
|
{...issuesData.pagination}
|
||||||
pageSize={issuesData.pageSize}
|
|
||||||
total={issuesData.total_count}
|
|
||||||
onChange={issuesData.handlePaginationChange}
|
|
||||||
showSizeChanger={true}
|
|
||||||
pageSizeOptions={["5", "10", "20", "50"]}
|
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
</>
|
</>
|
||||||
@ -150,14 +140,13 @@ const IssuesPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={issuesData.containerStyle}>
|
<div style={issuesData.containerStyle}>
|
||||||
<Title level={1}><DatabaseOutlined /> Выдача линз</Title>
|
<Title level={1}><DatabaseOutlined/> Выдача линз</Title>
|
||||||
<Row gutter={[16, 16]} style={issuesData.filterBarStyle}>
|
<Row gutter={[16, 16]} style={issuesData.filterBarStyle}>
|
||||||
<Col xs={24} sm={24} md={12}>
|
<Col xs={24} sm={24} md={12}>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Поиск по пациенту или врачу"
|
placeholder="Поиск по пациенту или врачу"
|
||||||
value={issuesData.tempSearchText}
|
value={issuesData.tempSearchText}
|
||||||
onChange={(e) => issuesData.handleSetTempSearchText(e.target.value)}
|
onChange={(e) => issuesData.handleSetTempSearchText(e.target.value)}
|
||||||
onPressEnter={issuesData.handleSearch}
|
|
||||||
style={issuesData.formItemStyle}
|
style={issuesData.formItemStyle}
|
||||||
allowClear
|
allowClear
|
||||||
onClear={issuesData.handleClearSearch}
|
onClear={issuesData.handleClearSearch}
|
||||||
@ -199,15 +188,15 @@ const IssuesPage = () => {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{issuesData.isLoading ? (
|
{issuesData.isLoading ? (
|
||||||
<LoadingIndicator />
|
<LoadingIndicator/>
|
||||||
) : issuesData.viewMode === "table" ? (
|
) : issuesData.viewMode === "table" ? (
|
||||||
<TableView />
|
<TableView/>
|
||||||
) : (
|
) : (
|
||||||
<TimeLineView />
|
<TimeLineView/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FloatButton
|
<FloatButton
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined/>}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={issuesData.handleAddIssue}
|
onClick={issuesData.handleAddIssue}
|
||||||
tooltip="Добавить выдачу линзы"
|
tooltip="Добавить выдачу линзы"
|
||||||
|
|||||||
@ -105,7 +105,10 @@ const useIssues = () => {
|
|||||||
borderRadius: 8
|
borderRadius: 8
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetTempSearchText = (value) => setTempSearchText(value);
|
const handleSetTempSearchText = (value) => {
|
||||||
|
setTempSearchText(value);
|
||||||
|
handleSearch();
|
||||||
|
};
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
dispatch(setSearchText(tempSearchText));
|
dispatch(setSearchText(tempSearchText));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user