feat: Добавлена сортировка пациентов по фамилии

This commit is contained in:
Андрей Дувакин 2025-06-07 15:30:27 +05:00
parent 1d8888da87
commit 342e021211
7 changed files with 63 additions and 58 deletions

View File

@ -1,4 +1,4 @@
from typing import Sequence, Optional, Tuple
from typing import Sequence, Optional, Tuple, Literal
from sqlalchemy import select, func, or_
from sqlalchemy.ext.asyncio import AsyncSession
@ -10,7 +10,8 @@ class PatientsRepository:
def __init__(self, db: AsyncSession):
self.db = db
async def get_all(self, skip: int = 0, limit: int = 10, search: str = None) -> Tuple[Sequence[Patient], int]:
async def get_all(self, skip: int = 0, limit: int = 10, search: str = None,
sort_order: Literal["asc", "desc"] = "asc") -> Tuple[Sequence[Patient], int]:
stmt = select(Patient)
if search:
@ -25,6 +26,11 @@ class PatientsRepository:
)
)
if sort_order == "desc":
stmt = stmt.order_by(Patient.last_name.desc())
else:
stmt = stmt.order_by(Patient.last_name.asc())
stmt = stmt.offset(skip).limit(limit)
result = await self.db.execute(stmt)
patients = result.scalars().all()

View File

@ -1,3 +1,5 @@
from typing import Literal
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
@ -20,11 +22,12 @@ async def get_all_patients(
page: int = Query(1, ge=1, description="Page number"),
page_size: int = Query(10, ge=1, le=100, description="Number of patients per page"),
search: str = Query(None, description="Search term for filtering patients"),
sort_order: Literal["asc", "desc"] = Query("asc", description="Sort order by first_name (asc or desc)"),
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
):
patients_service = PatientsService(db)
patients, total_count = await patients_service.get_all_patients(page, page_size, search)
patients, total_count = await patients_service.get_all_patients(page, page_size, search, sort_order)
return {"patients": patients, "total_count": total_count}

View File

@ -1,4 +1,4 @@
from typing import Optional, Tuple
from typing import Optional, Tuple, Literal
from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
@ -13,10 +13,10 @@ class PatientsService:
def __init__(self, db: AsyncSession):
self.patient_repository = PatientsRepository(db)
async def get_all_patients(self, page: int = 1, page_size: int = 10, search: str = None) -> Tuple[
async def get_all_patients(self, page: int = 1, page_size: int = 10, search: str = None, sort_order: Literal["asc", "desc"] = "asc") -> Tuple[
list[PatientEntity], int]:
skip = (page - 1) * page_size
patients, total_count = await self.patient_repository.get_all(skip=skip, limit=page_size, search=search)
patients, total_count = await self.patient_repository.get_all(skip=skip, limit=page_size, search=search, sort_order=sort_order)
return (
[self.model_to_entity(patient) for patient in patients],
total_count

View File

@ -7,9 +7,14 @@ export const patientsApi = createApi({
tagTypes: ['Patient'],
endpoints: (builder) => ({
getPatients: builder.query({
query: ({ page, pageSize, search }) => ({
query: ({ page, pageSize, search, sortOrder }) => ({
url: '/patients/',
params: { page, page_size: pageSize, search: search || undefined },
params: {
page,
page_size: pageSize,
search: search || undefined,
sort_order: sortOrder || undefined,
},
}),
providesTags: ['Patients'],
}),

View File

@ -18,7 +18,7 @@ import {
SortAscendingOutlined,
SortDescendingOutlined,
TableOutlined,
TeamOutlined,
TeamOutlined
} from "@ant-design/icons";
import PatientListCard from "../../Dummies/PatientListCard.jsx";
import PatientFormModal from "../../Dummies/PatientFormModal/PatientFormModal.jsx";
@ -37,24 +37,20 @@ const PatientsPage = () => {
title: "Фамилия",
dataIndex: "last_name",
key: "last_name",
sorter: true,
},
{
title: "Имя",
dataIndex: "first_name",
key: "first_name",
sorter: true,
},
{
title: "Отчество",
dataIndex: "patronymic",
key: "patronymic",
sorter: true,
},
{
title: "Дата рождения",
dataIndex: "birthday",
sorter: true,
render: patientsData.formatDate,
},
{
@ -118,13 +114,11 @@ const PatientsPage = () => {
placeholder="Поиск пациента"
value={patientsData.searchText}
onChange={(e) => patientsData.handleSetSearchText(e.target.value)}
onPressEnter={patientsData.handleSearch}
style={patientsData.formItemStyle}
allowClear
onClear={patientsData.handleClearSearch}
/>
</Col>
{patientsData.viewMode === "tile" && (
<Col xs={24} md={5} sm={6} xl={3} xxl={2}>
<Tooltip title={"Сортировка пациентов"}>
<Select
@ -137,7 +131,6 @@ const PatientsPage = () => {
</Select>
</Tooltip>
</Col>
)}
<Col xs={24} md={patientsData.viewMode === "tile" ? 5 : 10}
sm={patientsData.viewMode === "tile" ? 8 : 14}
xl={patientsData.viewMode === "tile" ? 3 : 5}

View File

@ -29,7 +29,7 @@ const usePatients = () => {
const [tempSearchText, setTempSearchText] = useState("");
const { data = { patients: [], total_count: 0 }, isLoading, isError } = useGetPatientsQuery(
{ page: currentPage, pageSize, search: tempSearchText || undefined },
{ page: currentPage, pageSize, search: tempSearchText || undefined, sortOrder },
{
pollingInterval: 20000, // Автообновление каждые 20 секунд
refetchOnMountOrArgChange: true, // Обновление при изменении параметров
@ -76,7 +76,11 @@ const usePatients = () => {
}
};
const handleSetSortOrder = (value) => dispatch(setSortOrder(value));
const handleSetSortOrder = (value) => {
dispatch(setSortOrder(value));
dispatch(setCurrentPage(1)); // Сбрасываем на первую страницу при изменении сортировки
};
const handleSetCurrentPage = (page) => dispatch(setCurrentPage(page));
const handleSetPageSize = (size) => dispatch(setPageSize(size));
const handleSetViewMode = (mode) => dispatch(setViewMode(mode));
@ -131,6 +135,8 @@ const usePatients = () => {
handleClearSearch,
handleDeletePatient,
handleSetSortOrder,
handleSetCurrentPage,
handleSetPageSize,
handleSetViewMode,
handleCloseModal,
handleAddPatient,

View File

@ -1,58 +1,50 @@
import {createSlice} from '@reduxjs/toolkit'
import {cacheInfo} from "../../Utils/cachedInfoUtils.js";
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
searchText: '',
sortOrder: 'asc',
viewMode: 'tile',
isModalVisible: false,
selectedPatient: null,
currentPage: 1,
pageSize: 10,
selectedPatient: null,
isModalVisible: false
};
const patientsSlice = createSlice({
name: 'patientsUI',
initialState,
reducers: {
setSearchText: (state, action) => {
state.searchText = action.payload;
},
setSortOrder: (state, action) => {
setSortOrder(state, action) {
state.sortOrder = action.payload;
},
setViewMode: (state, action) => {
setViewMode(state, action) {
state.viewMode = action.payload;
cacheInfo("viewModePatients", action.payload);
},
setCurrentPage: (state, action) => {
state.currentPage = action.payload;
},
setPageSize: (state, action) => {
state.pageSize = action.payload;
},
openModal: (state) => {
openModal(state) {
state.isModalVisible = true;
},
closeModal: (state) => {
closeModal(state) {
state.isModalVisible = false;
state.selectedPatient = null;
},
selectPatient: (state, action) => {
selectPatient(state, action) {
state.selectedPatient = action.payload;
}
}
})
},
setCurrentPage(state, action) {
state.currentPage = action.payload;
},
setPageSize(state, action) {
state.pageSize = action.payload;
},
},
});
export const {
setSearchText,
setSortOrder,
setViewMode,
setCurrentPage,
setPageSize,
openModal,
closeModal,
selectPatient
selectPatient,
setCurrentPage,
setPageSize,
} = patientsSlice.actions;
export default patientsSlice.reducer;