add core api
This commit is contained in:
1358
src/modules/package/package.controller.ts
Normal file
1358
src/modules/package/package.controller.ts
Normal file
File diff suppressed because it is too large
Load Diff
216
src/modules/package/package.mapper.ts
Normal file
216
src/modules/package/package.mapper.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import type { FlightSchedule } from "@/database/entities/flight-schedule.entity";
|
||||
import type { HotelSchedule } from "@/database/entities/hotel-schedule.entity";
|
||||
import type { PackageDetail } from "@/database/entities/package-detail.entity";
|
||||
import type { PackageItineraryDay } from "@/database/entities/package-itinerary-day.entity";
|
||||
import type {
|
||||
PackageItineraryWidget,
|
||||
PackageItineraryWidgetHotel,
|
||||
PackageItineraryWidgetInformation,
|
||||
PackageItineraryWidgetTransport,
|
||||
} from "@/database/entities/package-itinerary-widget.entity";
|
||||
import type { PackageItinerary } from "@/database/entities/package-itinerary.entity";
|
||||
import type { Package } from "@/database/entities/package.entity";
|
||||
import { PackageClass } from "@/database/enums/package-class.enum";
|
||||
import { PackageItineraryWidgetType } from "@/database/enums/package-itinerary-widget-type.enum";
|
||||
import { PackageType } from "@/database/enums/package-type.enum";
|
||||
import type { FlightMapper } from "@/modules/flight/flight.mapper";
|
||||
import type { FlightClassResponse } from "@/modules/flight/flight.types";
|
||||
import type { HotelMapper } from "@/modules/hotel/hotel.mapper";
|
||||
import type {
|
||||
PackageDetailResponse,
|
||||
PackageHotelResponse,
|
||||
PackageItineraryDayResponse,
|
||||
PackageItineraryResponse,
|
||||
PackageItineraryWidgetResponse,
|
||||
PackageRequest,
|
||||
PackageResponse,
|
||||
} from "@/modules/package/package.types";
|
||||
import type { TransportationMapper } from "@/modules/transportation/transportation.mapper";
|
||||
import * as dateFns from "date-fns";
|
||||
|
||||
export class PackageMapper {
|
||||
public constructor(
|
||||
private readonly flightMapper: FlightMapper,
|
||||
private readonly hotelMapper: HotelMapper,
|
||||
private readonly transportationMapper: TransportationMapper,
|
||||
) {}
|
||||
|
||||
public mapPackageType(packageType: PackageRequest["type"]): PackageType {
|
||||
switch (packageType) {
|
||||
case "reguler":
|
||||
return PackageType.reguler;
|
||||
case "plus":
|
||||
return PackageType.plus;
|
||||
}
|
||||
}
|
||||
|
||||
public mapPackageClass(packageClass: PackageRequest["class"]): PackageClass {
|
||||
switch (packageClass) {
|
||||
case "silver":
|
||||
return PackageClass.silver;
|
||||
case "gold":
|
||||
return PackageClass.gold;
|
||||
case "platinum":
|
||||
return PackageClass.platinum;
|
||||
}
|
||||
}
|
||||
|
||||
public mapEntityToResponse(package_: Package): PackageResponse {
|
||||
return {
|
||||
id: package_.id,
|
||||
name: package_.name,
|
||||
type: package_.type,
|
||||
class: package_.class,
|
||||
thumbnail: package_.thumbnail,
|
||||
use_fast_train: package_.useFastTrain,
|
||||
created_at: package_.createdAt,
|
||||
updated_at: package_.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
private mapFlightSchedule(
|
||||
flightSchedule: FlightSchedule,
|
||||
): FlightClassResponse[] {
|
||||
const flightClassResponses: FlightClassResponse[] = [];
|
||||
|
||||
let currentFlightSchedule: FlightSchedule | null = flightSchedule;
|
||||
while (currentFlightSchedule !== null) {
|
||||
flightClassResponses.push(
|
||||
this.flightMapper.mapClassEntityToResponse(
|
||||
currentFlightSchedule.flight,
|
||||
),
|
||||
);
|
||||
currentFlightSchedule = currentFlightSchedule.next;
|
||||
}
|
||||
|
||||
return flightClassResponses;
|
||||
}
|
||||
|
||||
private mapHotelSchedule(hotelSchedule: HotelSchedule): PackageHotelResponse {
|
||||
console.log(hotelSchedule.checkIn);
|
||||
const checkIn = dateFns.parse(hotelSchedule.checkIn, "H:mm:ss", new Date());
|
||||
|
||||
const checkOut = dateFns.parse(
|
||||
hotelSchedule.checkOut,
|
||||
"H:mm:ss",
|
||||
new Date(),
|
||||
);
|
||||
|
||||
return {
|
||||
hotel: this.hotelMapper.mapEntityToResponse(hotelSchedule.hotel),
|
||||
check_in: dateFns.format(checkIn, "HH:mm"),
|
||||
check_out: dateFns.format(checkOut, "HH:mm"),
|
||||
};
|
||||
}
|
||||
|
||||
private mapItineraryWidget(
|
||||
packageItineraryWidget: PackageItineraryWidget,
|
||||
): PackageItineraryWidgetResponse {
|
||||
switch (packageItineraryWidget.type) {
|
||||
case PackageItineraryWidgetType.transport:
|
||||
const transportWidget =
|
||||
packageItineraryWidget as PackageItineraryWidgetTransport;
|
||||
|
||||
return {
|
||||
type: "transport",
|
||||
transportation: transportWidget.transportation,
|
||||
from: transportWidget.from,
|
||||
to: transportWidget.to,
|
||||
};
|
||||
case PackageItineraryWidgetType.hotel:
|
||||
const hotelWidget =
|
||||
packageItineraryWidget as PackageItineraryWidgetHotel;
|
||||
|
||||
return {
|
||||
type: "hotel",
|
||||
hotel: this.hotelMapper.mapEntityToResponse(hotelWidget.hotel),
|
||||
};
|
||||
case PackageItineraryWidgetType.information:
|
||||
const informationWidget =
|
||||
packageItineraryWidget as PackageItineraryWidgetInformation;
|
||||
|
||||
return {
|
||||
type: "information",
|
||||
description: informationWidget.description,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private mapItineraryDay(
|
||||
packageItineraryDay: PackageItineraryDay,
|
||||
nth: number,
|
||||
): PackageItineraryDayResponse {
|
||||
return {
|
||||
nth,
|
||||
title: packageItineraryDay.title,
|
||||
description: packageItineraryDay.description,
|
||||
widgets: packageItineraryDay.widgets
|
||||
.getItems()
|
||||
.map((widget) => this.mapItineraryWidget(widget)),
|
||||
};
|
||||
}
|
||||
|
||||
private mapItinerary(
|
||||
packageItinerary: PackageItinerary,
|
||||
nthDay: number = 1,
|
||||
): PackageItineraryResponse {
|
||||
const days: PackageItineraryDayResponse[] = [];
|
||||
for (
|
||||
let currentItineraryDay: PackageItineraryDay | null =
|
||||
packageItinerary.day,
|
||||
index = 0;
|
||||
currentItineraryDay !== null;
|
||||
currentItineraryDay = currentItineraryDay.next, index++
|
||||
) {
|
||||
days.push(this.mapItineraryDay(currentItineraryDay, nthDay + index));
|
||||
}
|
||||
|
||||
return {
|
||||
location: packageItinerary.location,
|
||||
images: packageItinerary.images
|
||||
.getItems()
|
||||
.map((packageItineraryImage) => packageItineraryImage.src),
|
||||
days,
|
||||
};
|
||||
}
|
||||
|
||||
public mapDetailEntityToResponse(
|
||||
packageDetail: PackageDetail,
|
||||
): PackageDetailResponse {
|
||||
const itineraries: PackageItineraryResponse[] = [];
|
||||
for (
|
||||
let currentItinerary: PackageItinerary | null = packageDetail.itinerary,
|
||||
nthDay = 1;
|
||||
currentItinerary !== null;
|
||||
currentItinerary = currentItinerary.next
|
||||
) {
|
||||
const itineraryResponse = this.mapItinerary(currentItinerary, nthDay);
|
||||
itineraries.push(itineraryResponse);
|
||||
nthDay += itineraryResponse.days.length;
|
||||
}
|
||||
|
||||
return {
|
||||
id: packageDetail.id,
|
||||
package: this.mapEntityToResponse(packageDetail.package),
|
||||
departure_date: dateFns.format(packageDetail.departureDate, "yyyy-MM-dd"),
|
||||
tour_flights: packageDetail.tourFlight
|
||||
? this.mapFlightSchedule(packageDetail.tourFlight)
|
||||
: null,
|
||||
outbound_flights: this.mapFlightSchedule(packageDetail.outboundFlight),
|
||||
inbound_flights: this.mapFlightSchedule(packageDetail.inboundFlight),
|
||||
tour_hotels: packageDetail.tourHotels.map(this.mapHotelSchedule),
|
||||
makkah_hotel: this.mapHotelSchedule(packageDetail.makkahHotel),
|
||||
madinah_hotel: this.mapHotelSchedule(packageDetail.madinahHotel),
|
||||
transportation: this.transportationMapper.mapClassEntityToResponse(
|
||||
packageDetail.transportation,
|
||||
),
|
||||
quad_price: packageDetail.quadPrice,
|
||||
triple_price: packageDetail.triplePrice,
|
||||
double_price: packageDetail.doublePrice,
|
||||
infant_price: packageDetail.infantPrice,
|
||||
itineraries,
|
||||
created_at: packageDetail.createdAt,
|
||||
updated_at: packageDetail.updatedAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -17,53 +17,57 @@ export const packageRequestSchema = z.object({
|
||||
|
||||
export const packageDetailRequestSchema = z.object({
|
||||
departure_date: dateSchema,
|
||||
tour_flight_slugs: z
|
||||
tour_flight_ids: z
|
||||
.array(
|
||||
z
|
||||
.string("Must be string.")
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(420, "Max 420 characters."),
|
||||
.max(30, "Max 30 characters."),
|
||||
"Must be array.",
|
||||
)
|
||||
.nonempty("Must not empty.")
|
||||
.nullable(),
|
||||
outbound_flight_ids: z
|
||||
.array(
|
||||
z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(30, "Max 30 characters."),
|
||||
"Must be array.",
|
||||
)
|
||||
.nonempty("Must not empty."),
|
||||
outbound_flight_slugs: z
|
||||
inbound_flight_ids: z
|
||||
.array(
|
||||
z
|
||||
.string("Must be string.")
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(420, "Max 420 characters."),
|
||||
.max(30, "Max 30 characters."),
|
||||
"Must be array.",
|
||||
)
|
||||
.nonempty("Must not empty."),
|
||||
inbound_flight_slugs: z
|
||||
tour_hotels: z
|
||||
.array(
|
||||
z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(420, "Max 420 characters."),
|
||||
z.object(
|
||||
{
|
||||
hotel_id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(30, "Max 30 characters."),
|
||||
check_in: timeSchema,
|
||||
check_out: timeSchema,
|
||||
},
|
||||
"Must be object.",
|
||||
),
|
||||
"Must be array.",
|
||||
)
|
||||
.nonempty("Must not empty."),
|
||||
tour_hotels: z.array(
|
||||
z.object(
|
||||
{
|
||||
hotel_slug: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(200, "Max 200 characters."),
|
||||
check_in: timeSchema,
|
||||
check_out: timeSchema,
|
||||
},
|
||||
"Must be object.",
|
||||
),
|
||||
"Must be array.",
|
||||
),
|
||||
.nonempty("Must not empty.")
|
||||
.nullable(),
|
||||
makkah_hotel: z.object(
|
||||
{
|
||||
hotel_slug: z
|
||||
.string("Must be string.")
|
||||
hotel_id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(200, "Max 200 characters."),
|
||||
.max(30, "Max 30 characters."),
|
||||
check_in: timeSchema,
|
||||
check_out: timeSchema,
|
||||
},
|
||||
@@ -71,19 +75,19 @@ export const packageDetailRequestSchema = z.object({
|
||||
),
|
||||
madinah_hotel: z.object(
|
||||
{
|
||||
hotel_slug: z
|
||||
.string("Must be string.")
|
||||
hotel_id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(200, "Max 200 characters."),
|
||||
.max(30, "Max 30 characters."),
|
||||
check_in: timeSchema,
|
||||
check_out: timeSchema,
|
||||
},
|
||||
"Must be object.",
|
||||
),
|
||||
transportation_slug: z
|
||||
.string("Must be string.")
|
||||
transportation_id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(200, "Max 200 characters."),
|
||||
.max(30, "Max 30 characters."),
|
||||
quad_price: z
|
||||
.number("Must be number.")
|
||||
.int("Must be integer.")
|
||||
@@ -101,22 +105,97 @@ export const packageDetailRequestSchema = z.object({
|
||||
.int("Must be integer.")
|
||||
.positive("Must be positive.")
|
||||
.nullable(),
|
||||
itineraries: z.array(
|
||||
z.object(
|
||||
{
|
||||
location: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(100, "Max 100 characters."),
|
||||
images: z.array(
|
||||
z.base64("Must be base64 string.").nonempty("Must not empty."),
|
||||
"Must be array.",
|
||||
),
|
||||
days: z.array(
|
||||
z.object(
|
||||
{
|
||||
title: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(100, "Max 100 characters."),
|
||||
description: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(1000, "Max 1000 characters."),
|
||||
widgets: z.array(
|
||||
z.discriminatedUnion("type", [
|
||||
z.object(
|
||||
{
|
||||
type: z.literal("transport"),
|
||||
transportation: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(100, "Max 100 characters."),
|
||||
from: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(100, "Max 100 characters."),
|
||||
to: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(100, "Max 100 characters."),
|
||||
},
|
||||
"Must be object.",
|
||||
),
|
||||
z.object(
|
||||
{
|
||||
type: z.literal("hotel"),
|
||||
hotel_id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(30, "Max 30 characters."),
|
||||
},
|
||||
"Must be object.",
|
||||
),
|
||||
z.object(
|
||||
{
|
||||
type: z.literal("information"),
|
||||
description: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(1000, "Max 1000 characters."),
|
||||
},
|
||||
"Must be object.",
|
||||
),
|
||||
]),
|
||||
"Must be array.",
|
||||
),
|
||||
},
|
||||
"Must be object.",
|
||||
),
|
||||
"Must be array.",
|
||||
),
|
||||
},
|
||||
"Must be object.",
|
||||
),
|
||||
"Must be array.",
|
||||
),
|
||||
});
|
||||
|
||||
export const packageQuerySchema = z.object({
|
||||
detail_slug: z
|
||||
.string("Must be string.")
|
||||
export const packageParamsSchema = z.object({
|
||||
id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(200, "Max 200 characters."),
|
||||
slug: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(200, "Max 200 characters."),
|
||||
.max(30, "Max 30 characters."),
|
||||
});
|
||||
|
||||
export const packageDetailQuerySchema = z.object({
|
||||
slug: z
|
||||
.string("Must be string.")
|
||||
export const packageDetailParamsSchema = z.object({
|
||||
package_id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(200, "Max 200 characters."),
|
||||
.max(30, "Max 30 characters."),
|
||||
id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(30, "Max 30 characters."),
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FlightClassResponse } from "@/modules/flight/flight.types";
|
||||
import type { HotelResponse } from "@/modules/hotel/hotel.types";
|
||||
import type {
|
||||
packageDetailQuerySchema,
|
||||
packageDetailParamsSchema,
|
||||
packageDetailRequestSchema,
|
||||
packageQuerySchema,
|
||||
packageParamsSchema,
|
||||
packageRequestSchema,
|
||||
} from "@/modules/package/package.schemas";
|
||||
import type { TransportationClassResponse } from "@/modules/transportation/transportation.types";
|
||||
@@ -13,13 +13,12 @@ export type PackageRequest = z.infer<typeof packageRequestSchema>;
|
||||
|
||||
export type PackageDetailRequest = z.infer<typeof packageDetailRequestSchema>;
|
||||
|
||||
export type PackageQuery = z.infer<typeof packageQuerySchema>;
|
||||
export type PackageParams = z.infer<typeof packageParamsSchema>;
|
||||
|
||||
export type PackageDetailQuery = z.infer<typeof packageDetailQuerySchema>;
|
||||
export type PackageDetailParams = z.infer<typeof packageDetailParamsSchema>;
|
||||
|
||||
export type PackageResponse = {
|
||||
id: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
type: "reguler" | "plus";
|
||||
class: "silver" | "gold" | "platinum";
|
||||
@@ -35,22 +34,51 @@ export type PackageHotelResponse = {
|
||||
check_out: string;
|
||||
};
|
||||
|
||||
export type PackageItineraryWidgetResponse =
|
||||
| {
|
||||
type: "transport";
|
||||
transportation: string;
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
| {
|
||||
type: "hotel";
|
||||
hotel: HotelResponse;
|
||||
}
|
||||
| {
|
||||
type: "information";
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type PackageItineraryDayResponse = {
|
||||
nth: number;
|
||||
title: string;
|
||||
description: string;
|
||||
widgets: PackageItineraryWidgetResponse[];
|
||||
};
|
||||
|
||||
export type PackageItineraryResponse = {
|
||||
location: string;
|
||||
images: string[];
|
||||
days: PackageItineraryDayResponse[];
|
||||
};
|
||||
|
||||
export type PackageDetailResponse = {
|
||||
id: string;
|
||||
slug: string;
|
||||
package: PackageResponse;
|
||||
departure_date: string;
|
||||
tour_flights: FlightClassResponse[];
|
||||
tour_flights: FlightClassResponse[] | null;
|
||||
outbound_flights: FlightClassResponse[];
|
||||
inbound_flights: FlightClassResponse[];
|
||||
tour_hotels: PackageHotelResponse[];
|
||||
tour_hotels: PackageHotelResponse[] | null;
|
||||
makkah_hotel: PackageHotelResponse;
|
||||
medina_hotel: PackageHotelResponse;
|
||||
madinah_hotel: PackageHotelResponse;
|
||||
transportation: TransportationClassResponse;
|
||||
quad_price: number;
|
||||
triple_price: number;
|
||||
double_price: number;
|
||||
infant_price: number | null;
|
||||
itineraries: PackageItineraryResponse[];
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user