From 786dbe1bb7cfdec51759d5c55caac4218821e85d Mon Sep 17 00:00:00 2001 From: ItsMalma Date: Mon, 1 Dec 2025 12:54:45 +0700 Subject: [PATCH] add partner and slug into package --- bun.lock | 1 + src/database/entities/package.entity.ts | 11 ++++++ src/modules/city/city.controller.ts | 6 +++- src/modules/package/package.controller.ts | 42 ++++++++++++++++++++++- src/modules/package/package.mapper.ts | 4 +++ src/modules/package/package.schemas.ts | 4 +++ src/modules/package/package.types.ts | 3 ++ 7 files changed, 69 insertions(+), 2 deletions(-) diff --git a/bun.lock b/bun.lock index b4ab542..6b90fd3 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "dependencies": { diff --git a/src/database/entities/package.entity.ts b/src/database/entities/package.entity.ts index dd8f100..c7acfd7 100644 --- a/src/database/entities/package.entity.ts +++ b/src/database/entities/package.entity.ts @@ -1,4 +1,5 @@ import { PackageDetail } from "@/database/entities/package-detail.entity"; +import { Partner } from "@/database/entities/partner.entity"; import { PackageClass } from "@/database/enums/package-class.enum"; import { PackageType } from "@/database/enums/package-type.enum"; import { @@ -6,9 +7,12 @@ import { Collection, Entity, Enum, + ManyToOne, OneToMany, PrimaryKey, Property, + Unique, + type Rel, } from "@mikro-orm/core"; @Entity() @@ -16,6 +20,10 @@ export class Package { @PrimaryKey({ type: "varchar", length: 30 }) id!: string; + @Property({ type: "varchar", length: 100 }) + @Unique() + slug!: string; + @Property({ type: "varchar", length: 100 }) name!: string; @@ -31,6 +39,9 @@ export class Package { @Property({ type: "boolean" }) useFastTrain!: boolean; + @ManyToOne(() => Partner) + partner!: Rel; + @Property({ type: "timestamp", onCreate: () => new Date(), diff --git a/src/modules/city/city.controller.ts b/src/modules/city/city.controller.ts index 3235766..3321961 100644 --- a/src/modules/city/city.controller.ts +++ b/src/modules/city/city.controller.ts @@ -142,7 +142,11 @@ export class CityController extends Controller { } const body = parseBodyResult.data; - const country = await orm.em.findOne(Country, { id: body.country_id }); + const country = await orm.em.findOne( + Country, + { id: body.country_id }, + { populate: ["*"] }, + ); if (!country) { return res.status(404).json({ data: null, diff --git a/src/modules/package/package.controller.ts b/src/modules/package/package.controller.ts index d509101..0ad53e0 100644 --- a/src/modules/package/package.controller.ts +++ b/src/modules/package/package.controller.ts @@ -22,6 +22,7 @@ import { } from "@/database/entities/package-itinerary-widget.entity"; import { PackageItinerary } from "@/database/entities/package-itinerary.entity"; import { Package } from "@/database/entities/package.entity"; +import { Partner } from "@/database/entities/partner.entity"; import { TransportationClass } from "@/database/entities/transportation-class.entity"; import { AdminPermission } from "@/database/enums/admin-permission.enum"; import { PackageItineraryWidgetType } from "@/database/enums/package-itinerary-widget-type.enum"; @@ -39,6 +40,7 @@ import type { } from "@/modules/package/package.types"; import { wrap } from "@mikro-orm/core"; import { Router, type Request, type Response } from "express"; +import slugify from "slugify"; import { ulid } from "ulid"; export class PackageController extends Controller { @@ -61,13 +63,29 @@ export class PackageController extends Controller { Buffer.from(body.thumbnail, "base64"), ); + const partner = await orm.em.findOne(Partner, { id: body.partner_id }); + if (!partner) { + return res.status(404).json({ + data: null, + errors: [ + { + path: "partner_id", + location: "body", + message: "Partner not found.", + }, + ], + } satisfies ErrorResponse); + } + const package_ = orm.em.create(Package, { id: ulid(), + slug: slugify(body.name), name: body.name, type: body.type, class: body.class, thumbnail: thumbnailFile.name, useFastTrain: body.use_fast_train, + partner, createdAt: new Date(), updatedAt: new Date(), }); @@ -156,10 +174,30 @@ export class PackageController extends Controller { } const body = parseBodyResult.data; + const partner = await orm.em.findOne( + Partner, + { id: body.partner_id }, + { populate: ["*"] }, + ); + if (!partner) { + return res.status(404).json({ + data: null, + errors: [ + { + path: "partner_id", + location: "body", + message: "Partner not found.", + }, + ], + } satisfies ErrorResponse); + } + const package_ = await orm.em.findOne( Package, { id: params.id }, - { populate: ["*"] }, + { + populate: ["*"], + }, ); if (!package_) { return res.status(404).json({ @@ -179,10 +217,12 @@ export class PackageController extends Controller { package_.thumbnail, ); + package_.slug = slugify(body.name); package_.name = body.name; package_.type = body.type; package_.class = body.class; package_.useFastTrain = body.use_fast_train; + package_.partner = partner; package_.updatedAt = new Date(); await orm.em.flush(); diff --git a/src/modules/package/package.mapper.ts b/src/modules/package/package.mapper.ts index 4921e89..ae163d1 100644 --- a/src/modules/package/package.mapper.ts +++ b/src/modules/package/package.mapper.ts @@ -20,11 +20,13 @@ import type { PackageItineraryWidgetResponse, PackageResponse, } from "@/modules/package/package.types"; +import type { PartnerMapper } from "@/modules/partner/partner.mapper"; import type { TransportationMapper } from "@/modules/transportation/transportation.mapper"; import * as dateFns from "date-fns"; export class PackageMapper { public constructor( + private readonly partnerMapper: PartnerMapper, private readonly flightMapper: FlightMapper, private readonly hotelMapper: HotelMapper, private readonly transportationMapper: TransportationMapper, @@ -33,11 +35,13 @@ export class PackageMapper { public mapEntityToResponse(package_: Package): PackageResponse { return { id: package_.id, + slug: package_.slug, name: package_.name, type: package_.type, class: package_.class, thumbnail: package_.thumbnail, use_fast_train: package_.useFastTrain, + partner: this.partnerMapper.mapEntityToResponse(package_.partner), created_at: package_.createdAt, updated_at: package_.updatedAt, }; diff --git a/src/modules/package/package.schemas.ts b/src/modules/package/package.schemas.ts index 98a73f0..b0bd775 100644 --- a/src/modules/package/package.schemas.ts +++ b/src/modules/package/package.schemas.ts @@ -15,6 +15,10 @@ export const packageRequestSchema = z.object({ ), thumbnail: z.base64("Must be base64 string.").nonempty("Must not empty."), use_fast_train: z.boolean("Must be boolean."), + partner_id: z + .ulid("Must be ulid string.") + .nonempty("Must not empty.") + .max(30, "Max 30 characters."), }); export const packageDetailRequestSchema = z.object({ diff --git a/src/modules/package/package.types.ts b/src/modules/package/package.types.ts index 35e81c2..77fa73b 100644 --- a/src/modules/package/package.types.ts +++ b/src/modules/package/package.types.ts @@ -8,6 +8,7 @@ import type { packageParamsSchema, packageRequestSchema, } from "@/modules/package/package.schemas"; +import type { PartnerResponse } from "@/modules/partner/partner.types"; import type { TransportationClassResponse } from "@/modules/transportation/transportation.types"; import z from "zod"; @@ -21,11 +22,13 @@ export type PackageDetailParams = z.infer; export type PackageResponse = { id: string; + slug: string; name: string; type: PackageType; class: PackageClass; thumbnail: string; use_fast_train: boolean; + partner: PartnerResponse; created_at: Date; updated_at: Date; };