From aed122b41d8df3a5ca465ad0bfa8f2e64be07efd Mon Sep 17 00:00:00 2001 From: tongo Date: Sat, 12 Aug 2023 16:31:11 +0300 Subject: [PATCH] add users pagination, sort and search --- .../src/common/dto/person-filter-decorator.ts | 12 +++++ apps/api/src/common/dto/person-query-dto.ts | 46 +++++++++++++++++++ apps/api/src/person/person.controller.ts | 15 ++++-- apps/api/src/person/person.service.ts | 37 ++++++++++++++- 4 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 apps/api/src/common/dto/person-filter-decorator.ts create mode 100644 apps/api/src/common/dto/person-query-dto.ts diff --git a/apps/api/src/common/dto/person-filter-decorator.ts b/apps/api/src/common/dto/person-filter-decorator.ts new file mode 100644 index 000000000..da567e8b7 --- /dev/null +++ b/apps/api/src/common/dto/person-filter-decorator.ts @@ -0,0 +1,12 @@ +import { ApiQuery } from '@nestjs/swagger' +import { applyDecorators } from '@nestjs/common' + +export function PersonQueryDecorator() { + return applyDecorators( + ApiQuery({ name: 'pageindex', required: false, type: Number }), + ApiQuery({ name: 'pagesize', required: false, type: Number }), + ApiQuery({ name: 'search', required: false, type: String }), + ApiQuery({ name: 'sortBy', required: false, type: String }), + ApiQuery({ name: 'sortOrder', required: false, type: String }), + ) +} diff --git a/apps/api/src/common/dto/person-query-dto.ts b/apps/api/src/common/dto/person-query-dto.ts new file mode 100644 index 000000000..b596fa303 --- /dev/null +++ b/apps/api/src/common/dto/person-query-dto.ts @@ -0,0 +1,46 @@ +import { Expose, Transform } from 'class-transformer' +import { IsOptional } from 'class-validator' + +export class PersonQueryDto { + @Expose() + @IsOptional() + @Transform(({ value }) => falsyToUndefined(value)) + search?: string + + @Expose() + @IsOptional() + @Transform(({ value }) => falsyToUndefined(value)) + sortBy?: string + + @Expose() + @IsOptional() + @Transform(({ value }) => falsyToUndefined(value)) + sortOrder?: string + + @Expose() + @IsOptional() + @Transform(({ value }) => toNumber(value)) + pageindex?: number + + @Expose() + @IsOptional() + @Transform(({ value }) => toNumber(value)) + pagesize?: number +} + +function toNumber(value: string): number | undefined { + if (!value || Number.isNaN(value)) { + return undefined + } + + const newValue: number = Number.parseInt(value) + return newValue +} + +function falsyToUndefined(value: unknown): unknown | undefined { + if (!value || value === 'undefined') { + return undefined + } + + return value +} diff --git a/apps/api/src/person/person.controller.ts b/apps/api/src/person/person.controller.ts index b562a5cad..9d5309290 100644 --- a/apps/api/src/person/person.controller.ts +++ b/apps/api/src/person/person.controller.ts @@ -1,10 +1,12 @@ -import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common' +import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common' import { RoleMatchingMode, Roles } from 'nest-keycloak-connect' import { RealmViewSupporters, ViewSupporters } from '@podkrepi-bg/podkrepi-types' import { PersonService } from './person.service' import { CreatePersonDto } from './dto/create-person.dto' import { UpdatePersonDto } from './dto/update-person.dto' import { ApiTags } from '@nestjs/swagger' +import { PersonQueryDecorator } from '../common/dto/person-filter-decorator' +import { PersonQueryDto } from '../common/dto/person-query-dto' @ApiTags('person') @Controller('person') @@ -25,8 +27,15 @@ export class PersonController { roles: [RealmViewSupporters.role, ViewSupporters.role], mode: RoleMatchingMode.ANY, }) - async findAll() { - return await this.personService.findAll() + @PersonQueryDecorator() + async findAll(@Query() query?: PersonQueryDto) { + return await this.personService.findAll( + query?.search, + query?.sortBy, + query?.sortOrder, + query?.pageindex, + query?.pagesize, + ) } @Get(':id') diff --git a/apps/api/src/person/person.service.ts b/apps/api/src/person/person.service.ts index 6810d244d..b1844fcc9 100644 --- a/apps/api/src/person/person.service.ts +++ b/apps/api/src/person/person.service.ts @@ -1,6 +1,7 @@ import { Injectable, Logger } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import client from '@sendgrid/client' +import { Prisma } from '@prisma/client' import { PrismaService } from '../prisma/prisma.service' import { CreatePersonDto } from './dto/create-person.dto' import { UpdatePersonDto } from './dto/update-person.dto' @@ -30,8 +31,40 @@ export class PersonService { return person } - async findAll() { - return await this.prisma.person.findMany() + async findAll( + search?: string, + sortBy?: string, + sortOrder?: string, + pageIndex?: number, + pageSize?: number, + ) { + const whereClause: Prisma.PersonWhereInput = { + ...(search && { + OR: [ + { firstName: { contains: search, mode: 'insensitive' } }, + { lastName: { contains: search, mode: 'insensitive' } }, + { email: { contains: search, mode: 'insensitive' } }, + { phone: { contains: search } }, + ], + }), + } + const data = await this.prisma.person.findMany({ + skip: pageIndex && pageSize ? pageIndex * pageSize : undefined, + take: pageSize ? pageSize : undefined, + where: whereClause, + orderBy: [sortBy ? { [sortBy]: sortOrder ? sortOrder : 'desc' } : { createdAt: 'desc' }], + }) + + const count = await this.prisma.person.count({ + where: whereClause, + }) + + const result = { + items: data, + total: count, + } + + return result } async findOne(id: string) {