diff --git a/src/supply/supply.controller.ts b/src/supply/supply.controller.ts index 55133484..92600339 100644 --- a/src/supply/supply.controller.ts +++ b/src/supply/supply.controller.ts @@ -1,12 +1,15 @@ import { Body, Controller, + DefaultValuePipe, Get, HttpException, Logger, Param, + ParseIntPipe, Post, Put, + Query, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; @@ -31,6 +34,21 @@ export class SupplyController { } } + @Get('top') + async top( + @Query('perPage', new DefaultValuePipe(10), ParseIntPipe) perPage: number, + @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number, + @Query('shelterId') shelterId: string, + ) { + try { + const data = await this.supplyServices.top({ perPage, page, shelterId }); + return new ServerResponse(200, 'Successfully get top supplies', data); + } catch (err: any) { + this.logger.error(`Failed to get supplies: ${err}`); + throw new HttpException(err?.code ?? err?.name ?? `${err}`, 400); + } + } + @Post('') async store(@Body() body) { try { diff --git a/src/supply/supply.service.ts b/src/supply/supply.service.ts index a3caf57e..ee861079 100644 --- a/src/supply/supply.service.ts +++ b/src/supply/supply.service.ts @@ -1,8 +1,12 @@ import z from 'zod'; import { Injectable } from '@nestjs/common'; - +import { Prisma } from '@prisma/client'; import { PrismaService } from '../prisma/prisma.service'; -import { CreateSupplySchema, UpdateSupplySchema } from './types'; +import { + CreateSupplySchema, + SupplySearchSchema, + UpdateSupplySchema, +} from './types'; @Injectable() export class SupplyService { @@ -53,4 +57,24 @@ export class SupplyService { return data; } + + async top(body: z.infer) { + const payload = SupplySearchSchema.parse(body); + const take = payload.perPage; + const skip = payload.perPage * (payload.page - 1); + + const query = Prisma.sql`SELECT name, count(*)::int as amount + FROM shelter_supplies + LEFT JOIN supplies ON shelter_supplies.supply_id = supplies.id + ${ + payload.shelterId + ? Prisma.sql`WHERE shelter_id = ${payload.shelterId}` + : Prisma.empty + } + GROUP BY name + ORDER BY amount DESC + LIMIT ${take} OFFSET ${skip}`; + + return await this.prismaService.$queryRaw(query); + } } diff --git a/src/supply/types.ts b/src/supply/types.ts index faef2d11..f0086076 100644 --- a/src/supply/types.ts +++ b/src/supply/types.ts @@ -16,6 +16,15 @@ const SupplySchema = z.object({ updatedAt: z.string().nullable().optional(), }); +const SupplySearchSchema = z.object({ + shelterId: z.string().nullable().optional(), + page: z.preprocess((v) => +((v ?? '1') as string), z.number().min(1)), + perPage: z.preprocess( + (v) => +((v ?? '10') as string), + z.number().min(1).max(100), + ), +}); + const CreateSupplySchema = SupplySchema.omit({ id: true, createdAt: true, @@ -27,4 +36,10 @@ const UpdateSupplySchema = SupplySchema.pick({ supplyCategoryId: true, }).partial(); -export { SupplySchema, CreateSupplySchema, UpdateSupplySchema, SupplyPriority }; +export { + SupplySchema, + SupplySearchSchema, + CreateSupplySchema, + UpdateSupplySchema, + SupplyPriority, +};