diff --git a/src/modules/package/package.controller.ts b/src/modules/package/package.controller.ts index 1571593..4047da1 100644 --- a/src/modules/package/package.controller.ts +++ b/src/modules/package/package.controller.ts @@ -34,6 +34,7 @@ import { packageDetailParamsSchema, packageDetailRequestSchema, packageParamsSchema, + packageQuerySchema, packageRequestSchema, } from "@/modules/package/package.schemas"; import type { @@ -41,7 +42,7 @@ import type { PackageDetailResponse, PackageResponse, } from "@/modules/package/package.types"; -import { wrap } from "@mikro-orm/core"; +import { wrap, type OrderDefinition } from "@mikro-orm/core"; import { Router, type Request, type Response } from "express"; import slugify from "slugify"; import { ulid } from "ulid"; @@ -143,7 +144,7 @@ export class PackageController extends Controller { } async list(req: Request, res: Response) { - const parseQueryResult = paginationQuerySchema.safeParse(req.query); + const parseQueryResult = packageQuerySchema.safeParse(req.query); if (!parseQueryResult.success) { return this.handleZodError(parseQueryResult.error, res, "query"); } @@ -151,16 +152,45 @@ export class PackageController extends Controller { const count = await orm.em.count(Package); - const packages = await orm.em.find( - Package, - {}, - { - limit: query.per_page, - offset: (query.page - 1) * query.per_page, - orderBy: { createdAt: "DESC" }, - populate: ["*"], - }, - ); + const orderBy: OrderDefinition = {}; + switch (query.sort_by) { + case "newest": + orderBy.createdAt = "DESC"; + break; + case "oldest": + orderBy.createdAt = "ASC"; + break; + } + + let packageQueryBuilder = orm.em + .createQueryBuilder(Package, "_package") + .select(["*"], true) + .distinctOn(["class"]) + .limit(query.per_page) + .offset((query.page - 1) * query.per_page) + .leftJoinAndSelect("_package.partner", "_partner"); + + if ("class" in query && query.class) { + packageQueryBuilder = packageQueryBuilder.where({ class: query.class }); + } + if ("by_ideal" in query && query.by_ideal) { + packageQueryBuilder = packageQueryBuilder.where({ class: "by_ideal" }); + } + + switch (query.sort_by) { + case "newest": + packageQueryBuilder = packageQueryBuilder.orderBy({ + "_package.created_at": "DESC", + }); + break; + case "oldest": + packageQueryBuilder = packageQueryBuilder.orderBy({ + "_package.created_at": "ASC", + }); + break; + } + + const packages = await packageQueryBuilder.getResultList(); return res.status(200).json({ data: packages.map(this.mapper.mapEntityToResponse.bind(this.mapper)), @@ -376,10 +406,9 @@ export class PackageController extends Controller { } let outboundFlightSchedule: FlightSchedule | null = null; - for (const [ - index, - outboundFlightId, - ] of body.outbound_flight_ids.entries()) { + for (const [index, outboundFlightId] of body.outbound_flight_ids + .toReversed() + .entries()) { const outboundFlight = await orm.em.findOne( FlightClass, { id: outboundFlightId }, @@ -954,10 +983,9 @@ export class PackageController extends Controller { orm.em.remove(outboundFlight); } let outboundFlightSchedule: FlightSchedule | null = null; - for (const [ - index, - outboundFlightId, - ] of body.outbound_flight_ids.entries()) { + for (const [index, outboundFlightId] of body.outbound_flight_ids + .toReversed() + .entries()) { const outboundFlight = await orm.em.findOne( FlightClass, { id: outboundFlightId }, diff --git a/src/modules/package/package.schemas.ts b/src/modules/package/package.schemas.ts index a7792e9..f995f1b 100644 --- a/src/modules/package/package.schemas.ts +++ b/src/modules/package/package.schemas.ts @@ -1,4 +1,4 @@ -import { dateSchema } from "@/common/schemas"; +import { dateSchema, paginationQuerySchema } from "@/common/schemas"; import { PackageClass } from "@/database/enums/package-class.enum"; import { PackageType } from "@/database/enums/package-type.enum"; import z from "zod"; @@ -205,6 +205,29 @@ export const packageDetailRequestSchema = z.object({ ), }); +export const packageQuerySchema = z.intersection( + paginationQuerySchema, + z.intersection( + z.union([ + z.object({ + class: z + .enum(PackageClass, "Must be either 'silver', 'gold', or 'platinum'.") + .optional(), + }), + z.object({ + by_ideal: z + .stringbool("Must be string boolean 'true' or 'false'.") + .optional(), + }), + ]), + z.object({ + sort_by: z + .enum(["newest", "oldest"], "Must be either 'newest' or 'oldest'.") + .default("newest"), + }), + ), +); + export const packageParamsSchema = z.object({ id: z .ulid("Must be ulid string.") diff --git a/src/modules/package/package.types.ts b/src/modules/package/package.types.ts index a844170..31e09b5 100644 --- a/src/modules/package/package.types.ts +++ b/src/modules/package/package.types.ts @@ -6,6 +6,7 @@ import type { packageDetailParamsSchema, packageDetailRequestSchema, packageParamsSchema, + packageQuerySchema, packageRequestSchema, } from "@/modules/package/package.schemas"; import type { PartnerResponse } from "@/modules/partner/partner.types"; @@ -16,6 +17,8 @@ export type PackageRequest = z.infer; export type PackageDetailRequest = z.infer; +export type PackageQuery = z.infer; + export type PackageParams = z.infer; export type PackageDetailParams = z.infer;