Merge branch 'master' into feature/tracking

This commit is contained in:
Linus Flood
2024-11-21 07:53:58 +01:00
213 changed files with 3486 additions and 1990 deletions

View File

@@ -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(),

View File

@@ -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 ?? [])

View File

@@ -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,

View 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)

View File

@@ -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(),
}),
}),
})

View File

@@ -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
}