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

This commit is contained in:
Андрей Дувакин 2025-06-07 14:54:05 +05:00
parent 67fa9db57a
commit a2635ed03e
7 changed files with 44 additions and 17 deletions

View File

@ -1,6 +1,6 @@
from typing import Sequence, Optional, Tuple from typing import Sequence, Optional, Tuple
from sqlalchemy import select, func from sqlalchemy import select, func, or_
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.domain.models import Patient from app.domain.models import Patient
@ -10,12 +10,37 @@ class PatientsRepository:
def __init__(self, db: AsyncSession): def __init__(self, db: AsyncSession):
self.db = db self.db = db
async def get_all(self, skip: int = 0, limit: int = 10) -> Tuple[Sequence[Patient], int]: async def get_all(self, skip: int = 0, limit: int = 10, search: str = None) -> Tuple[Sequence[Patient], int]:
stmt = select(Patient).offset(skip).limit(limit) stmt = select(Patient)
if search:
search = f"%{search}%"
stmt = stmt.filter(
or_(
Patient.last_name.ilike(search),
Patient.first_name.ilike(search),
Patient.patronymic.ilike(search),
Patient.email.ilike(search),
Patient.phone.ilike(search)
)
)
stmt = stmt.offset(skip).limit(limit)
result = await self.db.execute(stmt) result = await self.db.execute(stmt)
patients = result.scalars().all() patients = result.scalars().all()
count_stmt = select(func.count()).select_from(Patient) count_stmt = select(func.count()).select_from(Patient)
if search:
search = f"%{search}%"
count_stmt = count_stmt.filter(
or_(
Patient.last_name.ilike(search),
Patient.first_name.ilike(search),
Patient.patronymic.ilike(search),
Patient.email.ilike(search),
Patient.phone.ilike(search)
)
)
count_result = await self.db.execute(count_stmt) count_result = await self.db.execute(count_stmt)
total_count = count_result.scalar() total_count = count_result.scalar()

View File

@ -19,11 +19,12 @@ router = APIRouter()
async def get_all_patients( async def get_all_patients(
page: int = Query(1, ge=1, description="Page number"), page: int = Query(1, ge=1, description="Page number"),
page_size: int = Query(10, ge=1, le=100, description="Number of patients per page"), 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"),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
user=Depends(get_current_user), user=Depends(get_current_user),
): ):
patients_service = PatientsService(db) patients_service = PatientsService(db)
patients, total_count = await patients_service.get_all_patients(page, page_size) patients, total_count = await patients_service.get_all_patients(page, page_size, search)
return {"patients": patients, "total_count": total_count} return {"patients": patients, "total_count": total_count}

View File

@ -13,9 +13,10 @@ class PatientsService:
def __init__(self, db: AsyncSession): def __init__(self, db: AsyncSession):
self.patient_repository = PatientsRepository(db) self.patient_repository = PatientsRepository(db)
async def get_all_patients(self, page: int = 1, page_size: int = 10) -> Tuple[list[PatientEntity], int]: async def get_all_patients(self, page: int = 1, page_size: int = 10, search: str = None) -> Tuple[
list[PatientEntity], int]:
skip = (page - 1) * page_size skip = (page - 1) * page_size
patients, total_count = await self.patient_repository.get_all(skip=skip, limit=page_size) patients, total_count = await self.patient_repository.get_all(skip=skip, limit=page_size, search=search)
return ( return (
[self.model_to_entity(patient) for patient in patients], [self.model_to_entity(patient) for patient in patients],
total_count total_count

View File

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

View File

@ -37,7 +37,7 @@ const PatientsPage = () => {
title: "Фамилия", title: "Фамилия",
dataIndex: "last_name", dataIndex: "last_name",
key: "last_name", key: "last_name",
sorter: true, // Сортировка будет на сервере, если добавить sorter: true,
}, },
{ {
title: "Имя", title: "Имя",
@ -109,8 +109,6 @@ const PatientsPage = () => {
/> />
); );
console.log(patientsData.isLoading)
return ( return (
<div style={patientsUI.containerStyle}> <div style={patientsUI.containerStyle}>
<Title level={1}><TeamOutlined/> Пациенты</Title> <Title level={1}><TeamOutlined/> Пациенты</Title>

View File

@ -6,17 +6,15 @@ import {
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
const usePatients = () => { const usePatients = () => {
const { currentPage, pageSize } = useSelector(state => state.patientsUI); const { currentPage, pageSize, searchText } = useSelector(state => state.patientsUI);
const { data = { patients: [], total_count: 0 }, isLoading, isError } = useGetPatientsQuery( const { data = { patients: [], total_count: 0 }, isLoading, isError } = useGetPatientsQuery(
{ page: currentPage, pageSize }, { page: currentPage, pageSize, search: searchText },
{ {
pollingInterval: 20000, pollingInterval: 20000,
} }
); );
console.log(data);
const [deletePatient] = useDeletePatientMutation(); const [deletePatient] = useDeletePatientMutation();
const handleDeletePatient = async (patientId) => { const handleDeletePatient = async (patientId) => {

View File

@ -1,5 +1,6 @@
import { useEffect, useMemo } from "react"; import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { debounce } from "lodash";
import { import {
closeModal, closeModal,
openModal, openModal,
@ -34,7 +35,10 @@ const usePatientsUI = (patients, totalCount) => {
const formItemStyle = { width: "100%" }; const formItemStyle = { width: "100%" };
const viewModIconStyle = { marginRight: 8 }; const viewModIconStyle = { marginRight: 8 };
const handleSetSearchText = (value) => dispatch(setSearchText(value)); const handleSetSearchText = debounce((value) => {
dispatch(setSearchText(value));
dispatch(setCurrentPage(1)); // Сбрасываем на первую страницу при новом поиске
}, 300);
const handleSetSortOrder = (value) => dispatch(setSortOrder(value)); const handleSetSortOrder = (value) => dispatch(setSortOrder(value));
const handleSetCurrentPage = (page) => dispatch(setCurrentPage(page)); const handleSetCurrentPage = (page) => dispatch(setCurrentPage(page));
const handleSetPageSize = (size) => dispatch(setPageSize(size)); const handleSetPageSize = (size) => dispatch(setPageSize(size));