Merge branch 'master' into feature/tracking
This commit is contained in:
@@ -8,9 +8,7 @@ export const getHotelsAvailabilityInputSchema = z.object({
|
||||
roomStayEndDate: z.string(),
|
||||
adults: z.number(),
|
||||
children: z.string().optional(),
|
||||
promotionCode: z.string().optional().default(""),
|
||||
reservationProfileType: z.string().optional().default(""),
|
||||
attachedProfileId: z.string().optional().default(""),
|
||||
bookingCode: z.string().optional().default(""),
|
||||
})
|
||||
|
||||
export const getRoomsAvailabilityInputSchema = z.object({
|
||||
@@ -19,9 +17,7 @@ export const getRoomsAvailabilityInputSchema = z.object({
|
||||
roomStayEndDate: z.string(),
|
||||
adults: z.number(),
|
||||
children: z.string().optional(),
|
||||
promotionCode: z.string().optional(),
|
||||
reservationProfileType: z.string().optional().default(""),
|
||||
attachedProfileId: z.string().optional().default(""),
|
||||
bookingCode: z.string().optional(),
|
||||
rateCode: z.string().optional(),
|
||||
})
|
||||
|
||||
@@ -31,9 +27,7 @@ export const getSelectedRoomAvailabilityInputSchema = z.object({
|
||||
roomStayEndDate: z.string(),
|
||||
adults: z.number(),
|
||||
children: z.string().optional(),
|
||||
promotionCode: z.string().optional(),
|
||||
reservationProfileType: z.string().optional().default(""),
|
||||
attachedProfileId: z.string().optional().default(""),
|
||||
bookingCode: z.string().optional(),
|
||||
rateCode: z.string(),
|
||||
roomTypeCode: z.string(),
|
||||
packageCodes: z.array(z.nativeEnum(RoomPackageCodeEnum)).optional(),
|
||||
|
||||
@@ -13,7 +13,6 @@ import { AlertTypeEnum } from "@/types/enums/alert"
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
import { FacilityEnum } from "@/types/enums/facilities"
|
||||
import { PackageTypeEnum } from "@/types/enums/packages"
|
||||
import { PointOfInterestCategoryNameEnum } from "@/types/hotel"
|
||||
|
||||
const ratingsSchema = z
|
||||
.object({
|
||||
@@ -105,7 +104,7 @@ const hotelContentSchema = z.object({
|
||||
imageSizes: imageSizesSchema,
|
||||
}),
|
||||
texts: z.object({
|
||||
facilityInformation: z.string(),
|
||||
facilityInformation: z.string().optional(),
|
||||
surroundingInformation: z.string(),
|
||||
descriptions: z.object({
|
||||
short: z.string(),
|
||||
@@ -113,10 +112,10 @@ const hotelContentSchema = z.object({
|
||||
}),
|
||||
}),
|
||||
restaurantsOverviewPage: z.object({
|
||||
restaurantsOverviewPageLinkText: z.string(),
|
||||
restaurantsOverviewPageLink: z.string(),
|
||||
restaurantsContentDescriptionShort: z.string(),
|
||||
restaurantsContentDescriptionMedium: z.string(),
|
||||
restaurantsOverviewPageLinkText: z.string().optional(),
|
||||
restaurantsOverviewPageLink: z.string().optional(),
|
||||
restaurantsContentDescriptionShort: z.string().optional(),
|
||||
restaurantsContentDescriptionMedium: z.string().optional(),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -199,14 +198,12 @@ const rewardNightSchema = z.object({
|
||||
}),
|
||||
})
|
||||
|
||||
const poiCategoryNames = z.nativeEnum(PointOfInterestCategoryNameEnum)
|
||||
|
||||
export const pointOfInterestSchema = z
|
||||
.object({
|
||||
name: z.string(),
|
||||
distance: z.number(),
|
||||
category: z.object({
|
||||
name: poiCategoryNames,
|
||||
name: z.string(),
|
||||
group: z.string(),
|
||||
}),
|
||||
location: locationSchema,
|
||||
@@ -491,22 +488,6 @@ const occupancySchema = z.object({
|
||||
children: z.array(childrenSchema),
|
||||
})
|
||||
|
||||
const bestPricePerStaySchema = z.object({
|
||||
currency: z.string(),
|
||||
// TODO: remove optional when API is ready
|
||||
regularAmount: z.string().optional(),
|
||||
// TODO: remove optional when API is ready
|
||||
memberAmount: z.string().optional(),
|
||||
})
|
||||
|
||||
const bestPricePerNightSchema = z.object({
|
||||
currency: z.string(),
|
||||
// TODO: remove optional when API is ready
|
||||
regularAmount: z.string().optional(),
|
||||
// TODO: remove optional when API is ready
|
||||
memberAmount: z.string().optional(),
|
||||
})
|
||||
|
||||
const linksSchema = z.object({
|
||||
links: z.array(
|
||||
z.object({
|
||||
@@ -516,30 +497,6 @@ const linksSchema = z.object({
|
||||
),
|
||||
})
|
||||
|
||||
const hotelsAvailabilitySchema = z.object({
|
||||
data: z.array(
|
||||
z.object({
|
||||
attributes: z.object({
|
||||
checkInDate: z.string(),
|
||||
checkOutDate: z.string(),
|
||||
occupancy: occupancySchema.optional(),
|
||||
status: z.string(),
|
||||
hotelId: z.number(),
|
||||
ratePlanSet: z.string().optional(),
|
||||
bestPricePerStay: bestPricePerStaySchema.optional(),
|
||||
bestPricePerNight: bestPricePerNightSchema.optional(),
|
||||
}),
|
||||
relationships: linksSchema.optional(),
|
||||
type: z.string().optional(),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const getHotelsAvailabilitySchema = hotelsAvailabilitySchema
|
||||
export type HotelsAvailability = z.infer<typeof hotelsAvailabilitySchema>
|
||||
export type HotelsAvailabilityPrices =
|
||||
HotelsAvailability["data"][number]["attributes"]["bestPricePerNight"]
|
||||
|
||||
export const priceSchema = z.object({
|
||||
pricePerNight: z.coerce.number(),
|
||||
pricePerStay: z.coerce.number(),
|
||||
@@ -550,16 +507,53 @@ export const productTypePriceSchema = z.object({
|
||||
rateCode: z.string(),
|
||||
rateType: z.string().optional(),
|
||||
localPrice: priceSchema,
|
||||
requestedPrice: priceSchema,
|
||||
requestedPrice: priceSchema.optional(),
|
||||
})
|
||||
|
||||
const productSchema = z.object({
|
||||
productType: z.object({
|
||||
public: productTypePriceSchema,
|
||||
public: productTypePriceSchema.default({
|
||||
rateCode: "",
|
||||
rateType: "",
|
||||
localPrice: {
|
||||
currency: "SEK",
|
||||
pricePerNight: 0,
|
||||
pricePerStay: 0,
|
||||
},
|
||||
requestedPrice: undefined,
|
||||
}),
|
||||
member: productTypePriceSchema.optional(),
|
||||
}),
|
||||
})
|
||||
|
||||
const hotelsAvailabilitySchema = z.object({
|
||||
data: z.array(
|
||||
z.object({
|
||||
attributes: z.object({
|
||||
checkInDate: z.string(),
|
||||
checkOutDate: z.string(),
|
||||
occupancy: occupancySchema,
|
||||
status: z.string(),
|
||||
hotelId: z.number(),
|
||||
productType: z
|
||||
.object({
|
||||
public: productTypePriceSchema.optional(),
|
||||
member: productTypePriceSchema.optional(),
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
relationships: linksSchema.optional(),
|
||||
type: z.string().optional(),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const getHotelsAvailabilitySchema = hotelsAvailabilitySchema
|
||||
export type HotelsAvailability = z.infer<typeof hotelsAvailabilitySchema>
|
||||
export type ProductType =
|
||||
HotelsAvailability["data"][number]["attributes"]["productType"]
|
||||
export type ProductTypePrices = z.infer<typeof productTypePriceSchema>
|
||||
|
||||
const roomConfigurationSchema = z.object({
|
||||
status: z.string(),
|
||||
roomTypeCode: z.string(),
|
||||
@@ -870,22 +864,24 @@ export const packagesSchema = z.object({
|
||||
|
||||
export const getRoomPackagesSchema = z
|
||||
.object({
|
||||
data: z.object({
|
||||
attributes: z.object({
|
||||
hotelId: z.number(),
|
||||
packages: z.array(packagesSchema).optional().default([]),
|
||||
}),
|
||||
relationships: z
|
||||
.object({
|
||||
links: z.array(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
type: z.string(),
|
||||
})
|
||||
),
|
||||
})
|
||||
.optional(),
|
||||
type: z.string(),
|
||||
}),
|
||||
data: z
|
||||
.object({
|
||||
attributes: z.object({
|
||||
hotelId: z.number(),
|
||||
packages: z.array(packagesSchema).optional().default([]),
|
||||
}),
|
||||
relationships: z
|
||||
.object({
|
||||
links: z.array(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
type: z.string(),
|
||||
})
|
||||
),
|
||||
})
|
||||
.optional(),
|
||||
type: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.transform((data) => data.data.attributes.packages)
|
||||
.transform((data) => data.data?.attributes?.packages ?? [])
|
||||
|
||||
@@ -354,6 +354,7 @@ export const hotelQueryRouter = router({
|
||||
facilities,
|
||||
alerts: hotelAlerts,
|
||||
faq: contentstackData?.faq,
|
||||
healthFacilities: hotelAttributes.healthFacilities,
|
||||
}
|
||||
}),
|
||||
availability: router({
|
||||
@@ -368,9 +369,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
bookingCode,
|
||||
} = input
|
||||
|
||||
const params: Record<string, string | number> = {
|
||||
@@ -378,9 +377,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
...(children && { children }),
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
bookingCode,
|
||||
language: apiLang,
|
||||
}
|
||||
hotelsAvailabilityCounter.add(1, {
|
||||
@@ -389,8 +386,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelsAvailability start",
|
||||
@@ -413,8 +409,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
@@ -445,8 +440,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateAvailabilityData.error),
|
||||
})
|
||||
@@ -465,8 +459,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelsAvailability success",
|
||||
@@ -475,12 +468,9 @@ export const hotelQueryRouter = router({
|
||||
})
|
||||
)
|
||||
return {
|
||||
availability: validateAvailabilityData.data.data
|
||||
.filter(
|
||||
(hotels) =>
|
||||
hotels.attributes.status === AvailabilityEnum.Available
|
||||
)
|
||||
.flatMap((hotels) => hotels.attributes),
|
||||
availability: validateAvailabilityData.data.data.flatMap(
|
||||
(hotels) => hotels.attributes
|
||||
),
|
||||
}
|
||||
}),
|
||||
rooms: serviceProcedure
|
||||
@@ -492,9 +482,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
bookingCode,
|
||||
rateCode,
|
||||
} = input
|
||||
|
||||
@@ -503,9 +491,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
...(children && { children }),
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
bookingCode,
|
||||
}
|
||||
|
||||
roomsAvailabilityCounter.add(1, {
|
||||
@@ -514,8 +500,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.roomsAvailability start",
|
||||
@@ -539,8 +524,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
@@ -571,8 +555,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateAvailabilityData.error),
|
||||
})
|
||||
@@ -591,8 +574,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.roomsAvailability success",
|
||||
@@ -619,9 +601,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
bookingCode,
|
||||
rateCode,
|
||||
roomTypeCode,
|
||||
packageCodes,
|
||||
@@ -632,9 +612,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
...(children && { children }),
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
bookingCode,
|
||||
language: toApiLang(ctx.lang),
|
||||
}
|
||||
|
||||
@@ -644,8 +622,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.selectedRoomAvailability start",
|
||||
@@ -669,8 +646,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponseAvailability.status,
|
||||
@@ -701,8 +677,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateAvailabilityData.error),
|
||||
})
|
||||
@@ -750,9 +725,13 @@ export const hotelQueryRouter = router({
|
||||
return null
|
||||
}
|
||||
|
||||
const rateDetails = validateAvailabilityData.data.rateDefinitions.find(
|
||||
(rateDef) => rateDef.rateCode === rateCode
|
||||
)?.generalTerms
|
||||
|
||||
const rateTypes = selectedRoom.products.find(
|
||||
(rate) =>
|
||||
rate.productType.public.rateCode === rateCode ||
|
||||
rate.productType.public?.rateCode === rateCode ||
|
||||
rate.productType.member?.rateCode === rateCode
|
||||
)
|
||||
|
||||
@@ -796,8 +775,7 @@ export const hotelQueryRouter = router({
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.selectedRoomAvailability success",
|
||||
@@ -808,6 +786,7 @@ export const hotelQueryRouter = router({
|
||||
|
||||
return {
|
||||
selectedRoom,
|
||||
rateDetails,
|
||||
mustBeGuaranteed,
|
||||
cancellationText,
|
||||
memberRate: rates?.member,
|
||||
@@ -960,12 +939,10 @@ export const hotelQueryRouter = router({
|
||||
"api.hotels.packages error",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
throw serverErrorByStatus(apiResponse.status, apiResponse)
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validatedPackagesData = getRoomPackagesSchema.safeParse(apiJson)
|
||||
|
||||
if (!validatedPackagesData.success) {
|
||||
getHotelFailCounter.add(1, {
|
||||
hotelId,
|
||||
|
||||
62
server/routers/hotels/schemas/packages.ts
Normal file
62
server/routers/hotels/schemas/packages.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
|
||||
export const getRoomPackagesInputSchema = z.object({
|
||||
hotelId: z.string(),
|
||||
startDate: z.string(),
|
||||
endDate: z.string(),
|
||||
adults: z.number(),
|
||||
children: z.number().optional().default(0),
|
||||
packageCodes: z.array(z.string()).optional().default([]),
|
||||
})
|
||||
|
||||
export const packagePriceSchema = z
|
||||
.object({
|
||||
currency: z.nativeEnum(CurrencyEnum),
|
||||
price: z.string(),
|
||||
totalPrice: z.string(),
|
||||
})
|
||||
.optional()
|
||||
.default({
|
||||
currency: CurrencyEnum.SEK,
|
||||
price: "0",
|
||||
totalPrice: "0",
|
||||
}) // TODO: Remove optional and default when the API change has been deployed
|
||||
|
||||
export const packagesSchema = z.object({
|
||||
code: z.nativeEnum(RoomPackageCodeEnum),
|
||||
description: z.string(),
|
||||
localPrice: packagePriceSchema,
|
||||
requestedPrice: packagePriceSchema,
|
||||
inventories: z.array(
|
||||
z.object({
|
||||
date: z.string(),
|
||||
total: z.number(),
|
||||
available: z.number(),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const getRoomPackagesSchema = z
|
||||
.object({
|
||||
data: z.object({
|
||||
attributes: z.object({
|
||||
hotelId: z.number(),
|
||||
packages: z.array(packagesSchema).default([]),
|
||||
}),
|
||||
relationships: z
|
||||
.object({
|
||||
links: z.array(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
type: z.string(),
|
||||
})
|
||||
),
|
||||
})
|
||||
.optional(),
|
||||
type: z.string(),
|
||||
}),
|
||||
})
|
||||
.transform((data) => data.data.attributes.packages)
|
||||
@@ -11,8 +11,8 @@ const roomContentSchema = z.object({
|
||||
),
|
||||
texts: z.object({
|
||||
descriptions: z.object({
|
||||
short: z.string(),
|
||||
medium: z.string(),
|
||||
short: z.string().optional(),
|
||||
medium: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -13,38 +13,33 @@ import {
|
||||
} from "./output"
|
||||
|
||||
import type { RequestOptionsWithOutBody } from "@/types/fetch"
|
||||
import {
|
||||
PointOfInterestCategoryNameEnum,
|
||||
PointOfInterestGroupEnum,
|
||||
} from "@/types/hotel"
|
||||
import { PointOfInterestGroupEnum } from "@/types/hotel"
|
||||
import { HotelLocation } from "@/types/trpc/routers/hotel/locations"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
import type { Endpoint } from "@/lib/api/endpoints"
|
||||
|
||||
export function getPoiGroupByCategoryName(
|
||||
category: PointOfInterestCategoryNameEnum
|
||||
) {
|
||||
export function getPoiGroupByCategoryName(category: string) {
|
||||
switch (category) {
|
||||
case PointOfInterestCategoryNameEnum.AIRPORT:
|
||||
case PointOfInterestCategoryNameEnum.BUS_TERMINAL:
|
||||
case PointOfInterestCategoryNameEnum.TRANSPORTATIONS:
|
||||
case "Airport":
|
||||
case "Bus terminal":
|
||||
case "Transportations":
|
||||
return PointOfInterestGroupEnum.PUBLIC_TRANSPORT
|
||||
case PointOfInterestCategoryNameEnum.AMUSEMENT_PARK:
|
||||
case PointOfInterestCategoryNameEnum.MUSEUM:
|
||||
case PointOfInterestCategoryNameEnum.SPORTS:
|
||||
case PointOfInterestCategoryNameEnum.THEATRE:
|
||||
case PointOfInterestCategoryNameEnum.TOURIST:
|
||||
case PointOfInterestCategoryNameEnum.ZOO:
|
||||
case "Amusement park":
|
||||
case "Museum":
|
||||
case "Sports":
|
||||
case "Theatre":
|
||||
case "Tourist":
|
||||
case "Zoo":
|
||||
return PointOfInterestGroupEnum.ATTRACTIONS
|
||||
case PointOfInterestCategoryNameEnum.NEARBY_COMPANIES:
|
||||
case PointOfInterestCategoryNameEnum.FAIR:
|
||||
case "Nearby companies":
|
||||
case "Fair":
|
||||
return PointOfInterestGroupEnum.BUSINESS
|
||||
case PointOfInterestCategoryNameEnum.PARKING_GARAGE:
|
||||
case "Parking / Garage":
|
||||
return PointOfInterestGroupEnum.PARKING
|
||||
case PointOfInterestCategoryNameEnum.SHOPPING:
|
||||
case PointOfInterestCategoryNameEnum.RESTAURANT:
|
||||
case "Shopping":
|
||||
case "Restaurant":
|
||||
return PointOfInterestGroupEnum.SHOPPING_DINING
|
||||
case PointOfInterestCategoryNameEnum.HOSPITAL:
|
||||
case "Hospital":
|
||||
default:
|
||||
return PointOfInterestGroupEnum.LOCATION
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user