diff --git a/src/application/activity-class/queries/list-activity-class/list-activity-class-query-validator.ts b/src/application/activity-class/queries/list-activity-class/list-activity-class-query-validator.ts new file mode 100644 index 0000000..52f7158 --- /dev/null +++ b/src/application/activity-class/queries/list-activity-class/list-activity-class-query-validator.ts @@ -0,0 +1,18 @@ +import { ValidationException } from '@application/common/exceptions'; +import { ZodError, z } from 'zod'; + +import { ListActivitClassQuery } from './list-activity-class-query'; + +export async function validate(query: ListActivitClassQuery) { + try { + const schema: z.ZodType = z.object({ + language: z.string(), + size: z.number().optional(), + page: z.number().optional() + }); + + await schema.parseAsync(query); + } catch (error) { + throw new ValidationException(error as ZodError); + } +} diff --git a/src/application/activity-class/queries/list-activity-class/list-activity-class-query.ts b/src/application/activity-class/queries/list-activity-class/list-activity-class-query.ts index ece553e..ee068e0 100644 --- a/src/application/activity-class/queries/list-activity-class/list-activity-class-query.ts +++ b/src/application/activity-class/queries/list-activity-class/list-activity-class-query.ts @@ -1,13 +1,25 @@ import { ActivityClassDTO } from "@domain/dtos/activity-class"; + import { map } from "./list-activity-class-query-mapper"; +import { validate } from "./list-activity-class-query-validator"; export type ListActivitClassQuery = Readonly<{ language: string; + size?: number; + page?: number }>; export function makeListActivityClassQuery({ activityClassRepository, db }: Pick) { - return async function listActivityClassQuery({ language }: ListActivitClassQuery) { - const activityClasses = await activityClassRepository.list({ language }); + return async function listActivityClassQuery(query: ListActivitClassQuery) { + await validate(query); + + const { language, size, page } = query; + + const { data: activityClasses, total } = await activityClassRepository.list({ language, size, page }); + + const per_page = size; + const current_page = page; + const last_page = total && per_page ? Math.ceil(total / per_page) : 1; const enrichedActivityClasses = await Promise.all(activityClasses.map(async (activityClass) => { const classInfo = await db.renamedclass.findUnique({ @@ -47,6 +59,12 @@ export function makeListActivityClassQuery({ activityClassRepository, db }: Pick return activityClassDTO; })); - return enrichedActivityClasses.map((activityClass) => map(activityClass)); + return { + data: enrichedActivityClasses.map((activityClass) => map(activityClass)), + total, + per_page, + current_page, + last_page, + } }; } diff --git a/src/application/common/interfaces/activity-class-repository.ts b/src/application/common/interfaces/activity-class-repository.ts index c13a733..2cc40d7 100644 --- a/src/application/common/interfaces/activity-class-repository.ts +++ b/src/application/common/interfaces/activity-class-repository.ts @@ -1,5 +1,9 @@ import { ActivityClassEntity } from "@domain/entities/activity-class"; export interface ActivityClassesRepository { - list(params: { language: string }): Promise>; + list(params: { + language: string; + size?: number; + page?: number + }): Promise<{ data: Array, total: number } | { data: [], total: null }>; } diff --git a/src/infrastructure/repositories/activity-classes-repository.ts b/src/infrastructure/repositories/activity-classes-repository.ts index 8b4ec33..7096f9f 100644 --- a/src/infrastructure/repositories/activity-classes-repository.ts +++ b/src/infrastructure/repositories/activity-classes-repository.ts @@ -2,17 +2,30 @@ import { ActivityClassesRepository } from "@application/common/interfaces"; export function makeActivityClassesRepository({ db }: Dependencies): ActivityClassesRepository { return { - async list() { + async list(params: { language: string; size?: number; page?: number }) { + const { size = 10, page = 1 } = params; + const activityClasses = await db.activity_class.findMany({ where: { status: 1 }, orderBy: { order: 'asc' }, + skip: (page - 1) * size, + take: size, + }); + + const total = await db.activity_class.count({ + where: { + status: 1, + }, }); - return activityClasses.map(activityClass => ({ - ...activityClass, - id: Number(activityClass.id), - class_id: Number(activityClass.class_id), - })); + return { + data: activityClasses.map(activityClass => ({ + ...activityClass, + id: Number(activityClass.id), + class_id: Number(activityClass.class_id), + })), + total + } }, }; } diff --git a/src/web/routes/activity-class.ts b/src/web/routes/activity-class.ts index c8e3bcb..6255281 100644 --- a/src/web/routes/activity-class.ts +++ b/src/web/routes/activity-class.ts @@ -15,8 +15,10 @@ export default async function activityClassRoutes(fastify: FastifyRouteInstance) type: 'object', properties: { language: { type: 'string', description: 'Language code (e.g., vi, en, etc.)' }, + size: { type: 'number', description: 'Size number of records (e.g., 10.)' }, + page: { type: 'number', description: 'Page number of database (e.g., 1.)' } }, - required: ['language'], + required: ['language', 'size', 'page'], }, response: { 200: { @@ -51,6 +53,10 @@ export default async function activityClassRoutes(fastify: FastifyRouteInstance) }, }, }, + total: { type: 'integer', }, + per_page: { type: 'integer', }, + current_page: { type: 'integer', }, + last_page: { type: 'integer', }, message: { type: 'string', example: 'Activity class fetched successfully' }, }, }, @@ -63,22 +69,35 @@ export default async function activityClassRoutes(fastify: FastifyRouteInstance) res ) { try { - const activtyClassList = await activityClass.queries.listActivityClasses({ language: req.body.language }); - const response = ResponseBase.formatBaseResponse( + const { data, total, per_page, current_page, last_page } = await activityClass.queries.listActivityClasses({ + language: req.body.language, + size: req.body.size, + page: req.body.page, + }); + + const response = ResponseBase.formatPaginationResponse( 200, - activtyClassList, - 'Activity class fetched successfully', + data, + total ?? 0, + per_page ?? 0, + current_page ?? 0, + last_page ?? 0, + 'Activity classes fetched successfully', ); res.status(200).send(response); } catch (error) { fastify.log.error(error); - const errorResponse = ResponseBase.formatBaseResponse( + const errorResponse = ResponseBase.formatPaginationResponse( 400, null, - 'Failed to fetch activity class', + 0, + 0, + 0, + 0, + 'Failed to fetch activty classes', ); res.status(400).send(errorResponse);