Merge branch 'develop' into feature/tracking
This commit is contained in:
@@ -62,7 +62,7 @@ export const bookingMutationRouter = router({
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
}
|
||||
|
||||
const apiResponse = await api.post(api.endpoints.v1.booking, {
|
||||
const apiResponse = await api.post(api.endpoints.v1.Booking.bookings, {
|
||||
headers,
|
||||
body: input,
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ export const bookingQueryRouter = router({
|
||||
getBookingConfirmationCounter.add(1, { confirmationNumber })
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.booking}/${confirmationNumber}`,
|
||||
api.endpoints.v1.Booking.booking(confirmationNumber),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -142,7 +142,7 @@ export const bookingQueryRouter = router({
|
||||
getBookingStatusCounter.add(1, { confirmationNumber })
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.booking}/${confirmationNumber}/status`,
|
||||
api.endpoints.v1.Booking.status(confirmationNumber),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
|
||||
@@ -26,7 +26,7 @@ export const bookingwidgetQueryRouter = router({
|
||||
const failedResponse = { hideBookingWidget: false }
|
||||
const { contentType, uid, lang } = ctx
|
||||
|
||||
// This condition is to handle 404 page case
|
||||
// This condition is to handle 404 page case and booking flow
|
||||
if (!contentType || !uid) {
|
||||
console.log("No proper params defined: ", contentType, uid)
|
||||
return failedResponse
|
||||
|
||||
@@ -19,7 +19,11 @@ import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
import type { GetContentPageSchema } from "@/types/trpc/routers/contentstack/contentPage"
|
||||
import { ContentPageEnum } from "@/types/enums/contentPage"
|
||||
import type {
|
||||
GetBlock,
|
||||
GetContentPageSchema,
|
||||
} from "@/types/trpc/routers/contentstack/contentPage"
|
||||
|
||||
export const contentPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
@@ -79,21 +83,36 @@ export const contentPageQueryRouter = router({
|
||||
),
|
||||
])
|
||||
|
||||
const blocksOrder = mainResponse.data.content_page.blocks?.map(
|
||||
(block) => block.__typename
|
||||
)
|
||||
|
||||
let sortedBlocks
|
||||
if (blocksOrder) {
|
||||
const blocks = [
|
||||
blocksResponse1.data.content_page.blocks,
|
||||
blocksResponse2.data.content_page.blocks,
|
||||
]
|
||||
.flat(2)
|
||||
.filter((obj) => !(obj && Object.keys(obj).length < 2))
|
||||
// Remove empty objects and objects with only typename
|
||||
|
||||
sortedBlocks = blocksOrder
|
||||
.map((typename: ContentPageEnum.ContentStack.blocks) =>
|
||||
blocks.find((block) => block?.__typename === typename)
|
||||
)
|
||||
.filter((block): block is GetBlock => !!block)
|
||||
}
|
||||
|
||||
const responseData = {
|
||||
...mainResponse.data,
|
||||
content_page: {
|
||||
...mainResponse.data.content_page,
|
||||
blocks: [
|
||||
blocksResponse1.data.content_page.blocks,
|
||||
blocksResponse2.data.content_page.blocks,
|
||||
]
|
||||
.flat(2)
|
||||
.filter((obj) => !(obj && Object.keys(obj).length < 2)), // Remove empty objects and objects with only typename
|
||||
blocks: sortedBlocks,
|
||||
},
|
||||
}
|
||||
|
||||
const contentPage = contentPageSchema.safeParse(responseData)
|
||||
|
||||
if (!contentPage.success) {
|
||||
console.error(
|
||||
`Failed to validate Contentpage Data - (lang: ${lang}, uid: ${uid})`
|
||||
|
||||
@@ -75,7 +75,7 @@ function getUniqueRewardIds(rewardIds: string[]) {
|
||||
|
||||
const getAllCachedApiRewards = unstable_cache(
|
||||
async function (token) {
|
||||
const apiResponse = await api.get(api.endpoints.v1.tierRewards, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.tierRewards, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
@@ -194,7 +194,7 @@ export const rewardQueryRouter = router({
|
||||
|
||||
const { limit, cursor } = input
|
||||
|
||||
const apiResponse = await api.get(api.endpoints.v1.rewards, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.reward, {
|
||||
cache: undefined, // override defaultOptions
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -393,7 +393,7 @@ export const rewardQueryRouter = router({
|
||||
surprises: contentStackBaseWithProtectedProcedure.query(async ({ ctx }) => {
|
||||
getCurrentRewardCounter.add(1)
|
||||
|
||||
const apiResponse = await api.get(api.endpoints.v1.rewards, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.reward, {
|
||||
cache: undefined, // override defaultOptions
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
|
||||
@@ -11,7 +11,7 @@ export const getHotelsAvailabilityInputSchema = z.object({
|
||||
roomStayStartDate: z.string(),
|
||||
roomStayEndDate: z.string(),
|
||||
adults: z.number(),
|
||||
children: z.number().optional().default(0),
|
||||
children: z.string().optional(),
|
||||
promotionCode: z.string().optional().default(""),
|
||||
reservationProfileType: z.string().optional().default(""),
|
||||
attachedProfileId: z.string().optional().default(""),
|
||||
@@ -22,10 +22,11 @@ export const getRoomsAvailabilityInputSchema = z.object({
|
||||
roomStayStartDate: z.string(),
|
||||
roomStayEndDate: z.string(),
|
||||
adults: z.number(),
|
||||
children: z.number().optional().default(0),
|
||||
children: z.string().optional(),
|
||||
promotionCode: z.string().optional(),
|
||||
reservationProfileType: z.string().optional().default(""),
|
||||
attachedProfileId: z.string().optional().default(""),
|
||||
rateCode: z.string().optional(),
|
||||
})
|
||||
|
||||
export const getRatesInputSchema = z.object({
|
||||
@@ -35,7 +36,12 @@ export const getRatesInputSchema = z.object({
|
||||
export const getlHotelDataInputSchema = z.object({
|
||||
hotelId: z.string(),
|
||||
language: z.string(),
|
||||
isCardOnlyPayment: z.boolean().optional(),
|
||||
include: z
|
||||
.array(z.enum(["RoomCategories", "NearbyHotels", "Restaurants", "City"]))
|
||||
.optional(),
|
||||
})
|
||||
|
||||
export const getBreakfastPackageInput = z.object({
|
||||
hotelId: z.string().min(1, { message: "hotelId is required" }),
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { BedTypeEnum } from "@/constants/booking"
|
||||
import { dt } from "@/lib/dt"
|
||||
import { toLang } from "@/server/utils"
|
||||
|
||||
@@ -7,8 +8,11 @@ import { imageMetaDataSchema, imageSizesSchema } from "./schemas/image"
|
||||
import { roomSchema } from "./schemas/room"
|
||||
import { getPoiGroupByCategoryName } from "./utils"
|
||||
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
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
|
||||
@@ -162,30 +166,20 @@ export const facilitySchema = z.object({
|
||||
),
|
||||
})
|
||||
|
||||
export const imageSchema = z.object({
|
||||
metaData: imageMetaDataSchema,
|
||||
imageSizes: imageSizesSchema,
|
||||
})
|
||||
|
||||
export const gallerySchema = z.object({
|
||||
heroImages: z.array(
|
||||
z.object({
|
||||
metaData: imageMetaDataSchema,
|
||||
imageSizes: imageSizesSchema,
|
||||
})
|
||||
),
|
||||
smallerImages: z.array(
|
||||
z.object({
|
||||
metaData: imageMetaDataSchema,
|
||||
imageSizes: imageSizesSchema,
|
||||
})
|
||||
),
|
||||
heroImages: z.array(imageSchema),
|
||||
smallerImages: z.array(imageSchema),
|
||||
})
|
||||
|
||||
const healthFacilitySchema = z.object({
|
||||
type: z.string(),
|
||||
content: z.object({
|
||||
images: z.array(
|
||||
z.object({
|
||||
metaData: imageMetaDataSchema,
|
||||
imageSizes: imageSizesSchema,
|
||||
})
|
||||
),
|
||||
images: z.array(imageSchema),
|
||||
texts: z.object({
|
||||
facilityInformation: z.string().optional(),
|
||||
surroundingInformation: z.string().optional(),
|
||||
@@ -465,9 +459,14 @@ export const getHotelDataSchema = z.object({
|
||||
included: z.array(roomSchema).optional(),
|
||||
})
|
||||
|
||||
export const childrenSchema = z.object({
|
||||
age: z.number(),
|
||||
bedType: z.nativeEnum(BedTypeEnum),
|
||||
})
|
||||
|
||||
const occupancySchema = z.object({
|
||||
adults: z.number(),
|
||||
children: z.number(),
|
||||
children: z.array(childrenSchema),
|
||||
})
|
||||
|
||||
const bestPricePerStaySchema = z.object({
|
||||
@@ -545,7 +544,16 @@ const roomConfigurationSchema = z.object({
|
||||
roomTypeCode: z.string().optional(),
|
||||
roomType: z.string(),
|
||||
roomsLeft: z.number(),
|
||||
features: z.array(z.object({ inventory: z.number(), code: z.string() })),
|
||||
features: z.array(
|
||||
z.object({
|
||||
inventory: z.number(),
|
||||
code: z.enum([
|
||||
RoomPackageCodeEnum.PET_ROOM,
|
||||
RoomPackageCodeEnum.ALLERGY_ROOM,
|
||||
RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
|
||||
]),
|
||||
})
|
||||
),
|
||||
products: z.array(productSchema),
|
||||
})
|
||||
|
||||
@@ -570,6 +578,7 @@ const roomsAvailabilitySchema = z
|
||||
hotelId: z.number(),
|
||||
roomConfigurations: z.array(roomConfigurationSchema),
|
||||
rateDefinitions: z.array(rateDefinitionSchema),
|
||||
mustBeGuaranteed: z.boolean().optional(),
|
||||
}),
|
||||
relationships: linksSchema.optional(),
|
||||
type: z.string().optional(),
|
||||
@@ -653,7 +662,7 @@ export const apiCountriesSchema = z.object({
|
||||
name: z.string(),
|
||||
}),
|
||||
hotelInformationSystemId: z.number().optional(),
|
||||
id: z.string().optional(),
|
||||
id: z.string().optional().default(""),
|
||||
language: z.string().optional(),
|
||||
type: z.literal("countries"),
|
||||
})
|
||||
@@ -794,3 +803,30 @@ export const apiLocationsSchema = z.object({
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const breakfastPackageSchema = z.object({
|
||||
code: z.string(),
|
||||
currency: z.nativeEnum(CurrencyEnum),
|
||||
description: z.string(),
|
||||
originalPrice: z.number().default(0),
|
||||
packagePrice: z.number(),
|
||||
packageType: z.enum([
|
||||
PackageTypeEnum.BreakfastAdult,
|
||||
PackageTypeEnum.BreakfastChildren,
|
||||
]),
|
||||
totalPrice: z.number(),
|
||||
})
|
||||
|
||||
export const breakfastPackagesSchema = z
|
||||
.object({
|
||||
data: z.object({
|
||||
attributes: z.object({
|
||||
hotelId: z.number(),
|
||||
packages: z.array(breakfastPackageSchema),
|
||||
}),
|
||||
type: z.literal("breakfastpackage"),
|
||||
}),
|
||||
})
|
||||
.transform(({ data }) =>
|
||||
data.attributes.packages.filter((pkg) => pkg.code.match(/^(BRF\d+)$/gm))
|
||||
)
|
||||
|
||||
@@ -9,11 +9,11 @@ import {
|
||||
notFound,
|
||||
serverErrorByStatus,
|
||||
} from "@/server/errors/trpc"
|
||||
import { extractHotelImages } from "@/server/routers/utils/hotels"
|
||||
import {
|
||||
contentStackUidWithServiceProcedure,
|
||||
publicProcedure,
|
||||
router,
|
||||
safeProtectedServiceProcedure,
|
||||
serviceProcedure,
|
||||
} from "@/server/trpc"
|
||||
import { toApiLang } from "@/server/utils"
|
||||
@@ -25,7 +25,13 @@ import {
|
||||
getHotelPageCounter,
|
||||
validateHotelPageRefs,
|
||||
} from "../contentstack/hotelPage/utils"
|
||||
import { getVerifiedUser, parsedUser } from "../user/query"
|
||||
import {
|
||||
getRoomPackagesInputSchema,
|
||||
getRoomPackagesSchema,
|
||||
} from "./schemas/packages"
|
||||
import {
|
||||
getBreakfastPackageInput,
|
||||
getHotelInputSchema,
|
||||
getHotelsAvailabilityInputSchema,
|
||||
getlHotelDataInputSchema,
|
||||
@@ -33,6 +39,7 @@ import {
|
||||
getRoomsAvailabilityInputSchema,
|
||||
} from "./input"
|
||||
import {
|
||||
breakfastPackagesSchema,
|
||||
getHotelDataSchema,
|
||||
getHotelsAvailabilitySchema,
|
||||
getRatesSchema,
|
||||
@@ -48,6 +55,7 @@ import {
|
||||
|
||||
import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities"
|
||||
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import type { RequestOptionsWithOutBody } from "@/types/fetch"
|
||||
import type { Facility } from "@/types/hotel"
|
||||
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
|
||||
@@ -57,6 +65,14 @@ const getHotelCounter = meter.createCounter("trpc.hotel.get")
|
||||
const getHotelSuccessCounter = meter.createCounter("trpc.hotel.get-success")
|
||||
const getHotelFailCounter = meter.createCounter("trpc.hotel.get-fail")
|
||||
|
||||
const getPackagesCounter = meter.createCounter("trpc.hotel.packages.get")
|
||||
const getPackagesSuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.packages.get-success"
|
||||
)
|
||||
const getPackagesFailCounter = meter.createCounter(
|
||||
"trpc.hotel.packages.get-fail"
|
||||
)
|
||||
|
||||
const hotelsAvailabilityCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels"
|
||||
)
|
||||
@@ -77,6 +93,14 @@ const roomsAvailabilityFailCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.rooms-fail"
|
||||
)
|
||||
|
||||
const breakfastPackagesCounter = meter.createCounter("trpc.package.breakfast")
|
||||
const breakfastPackagesSuccessCounter = meter.createCounter(
|
||||
"trpc.package.breakfast-success"
|
||||
)
|
||||
const breakfastPackagesFailCounter = meter.createCounter(
|
||||
"trpc.package.breakfast-fail"
|
||||
)
|
||||
|
||||
async function getContentstackData(lang: Lang, uid?: string | null) {
|
||||
if (!uid) {
|
||||
return null
|
||||
@@ -158,7 +182,7 @@ export const hotelQueryRouter = router({
|
||||
})
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotels}/${hotelId}`,
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -218,7 +242,7 @@ export const hotelQueryRouter = router({
|
||||
const included = validatedHotelData.data.included || []
|
||||
|
||||
const hotelAttributes = validatedHotelData.data.data.attributes
|
||||
const images = extractHotelImages(hotelAttributes)
|
||||
const images = hotelAttributes.gallery?.smallerImages
|
||||
const hotelAlerts = hotelAttributes.meta?.specialAlerts || []
|
||||
|
||||
const roomCategories = included
|
||||
@@ -288,13 +312,12 @@ export const hotelQueryRouter = router({
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
...(children && { children }),
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
language: apiLang,
|
||||
}
|
||||
|
||||
hotelsAvailabilityCounter.add(1, {
|
||||
cityId,
|
||||
roomStayStartDate,
|
||||
@@ -309,7 +332,7 @@ export const hotelQueryRouter = router({
|
||||
JSON.stringify({ query: { cityId, params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotelsAvailability}/${cityId}`,
|
||||
api.endpoints.v1.Availability.city(cityId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -407,13 +430,14 @@ export const hotelQueryRouter = router({
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
rateCode,
|
||||
} = input
|
||||
|
||||
const params: Record<string, string | number | undefined> = {
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
...(children && { children }),
|
||||
promotionCode,
|
||||
reservationProfileType,
|
||||
attachedProfileId,
|
||||
@@ -433,7 +457,7 @@ export const hotelQueryRouter = router({
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.roomsAvailability}/${hotelId}`,
|
||||
api.endpoints.v1.Availability.hotel(hotelId.toString()),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -441,6 +465,7 @@ export const hotelQueryRouter = router({
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
roomsAvailabilityFailCounter.add(1, {
|
||||
@@ -510,6 +535,14 @@ export const hotelQueryRouter = router({
|
||||
query: { hotelId, params: params },
|
||||
})
|
||||
)
|
||||
|
||||
if (rateCode) {
|
||||
validateAvailabilityData.data.mustBeGuaranteed =
|
||||
validateAvailabilityData.data.rateDefinitions.filter(
|
||||
(rate) => rate.rateCode === rateCode
|
||||
)[0].mustBeGuaranteed
|
||||
}
|
||||
|
||||
return validateAvailabilityData.data
|
||||
}),
|
||||
}),
|
||||
@@ -553,7 +586,7 @@ export const hotelQueryRouter = router({
|
||||
get: serviceProcedure
|
||||
.input(getlHotelDataInputSchema)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const { hotelId, language, include } = input
|
||||
const { hotelId, language, include, isCardOnlyPayment } = input
|
||||
|
||||
const params: Record<string, string> = {
|
||||
hotelId,
|
||||
@@ -575,7 +608,7 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotels}/${hotelId}`,
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -645,6 +678,11 @@ export const hotelQueryRouter = router({
|
||||
})
|
||||
)
|
||||
|
||||
if (isCardOnlyPayment) {
|
||||
validateHotelData.data.data.attributes.merchantInformationData.alternatePaymentOptions =
|
||||
[]
|
||||
}
|
||||
|
||||
return validateHotelData.data
|
||||
}),
|
||||
}),
|
||||
@@ -693,4 +731,198 @@ export const hotelQueryRouter = router({
|
||||
return locations
|
||||
}),
|
||||
}),
|
||||
packages: router({
|
||||
get: serviceProcedure
|
||||
.input(getRoomPackagesInputSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { hotelId, startDate, endDate, adults, children, packageCodes } =
|
||||
input
|
||||
|
||||
const searchParams = new URLSearchParams({
|
||||
startDate,
|
||||
endDate,
|
||||
adults: adults.toString(),
|
||||
children: children.toString(),
|
||||
})
|
||||
|
||||
packageCodes.forEach((code) => {
|
||||
searchParams.append("packageCodes", code)
|
||||
})
|
||||
|
||||
const params = searchParams.toString()
|
||||
|
||||
getPackagesCounter.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.packages start",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Package.Packages.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
getPackagesFailCounter.add(1, {
|
||||
hotelId,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"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,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedPackagesData.error),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotels.packages validation error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: validatedPackagesData.error,
|
||||
})
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
getPackagesSuccessCounter.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.packages success",
|
||||
JSON.stringify({ query: { hotelId, params: params } })
|
||||
)
|
||||
|
||||
return validatedPackagesData.data
|
||||
}),
|
||||
breakfast: safeProtectedServiceProcedure
|
||||
.input(getBreakfastPackageInput)
|
||||
.query(async function ({ ctx, input }) {
|
||||
const params = {
|
||||
Adults: 2,
|
||||
EndDate: "2024-10-28",
|
||||
StartDate: "2024-10-25",
|
||||
}
|
||||
const metricsData = { ...input, ...params }
|
||||
breakfastPackagesCounter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.breakfast start",
|
||||
JSON.stringify({ query: metricsData })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Package.Breakfast.hotel(input.hotelId),
|
||||
{
|
||||
cache: undefined,
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
},
|
||||
next: {
|
||||
revalidate: 60,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
breakfastPackagesFailCounter.add(1, {
|
||||
...metricsData,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsAvailability error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const breakfastPackages = breakfastPackagesSchema.safeParse(apiJson)
|
||||
if (!breakfastPackages.success) {
|
||||
hotelsAvailabilityFailCounter.add(1, {
|
||||
...metricsData,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(breakfastPackages.error),
|
||||
})
|
||||
console.error(
|
||||
"api.package.breakfast validation error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: breakfastPackages.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
breakfastPackagesSuccessCounter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.breakfast success",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
})
|
||||
)
|
||||
|
||||
if (ctx.session?.token) {
|
||||
const apiUser = await getVerifiedUser({ session: ctx.session })
|
||||
if (apiUser && !("error" in apiUser)) {
|
||||
const user = parsedUser(apiUser.data, false)
|
||||
if (
|
||||
user.membership &&
|
||||
["L6", "L7"].includes(user.membership.membershipLevel)
|
||||
) {
|
||||
const originalBreakfastPackage = breakfastPackages.data.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
||||
)
|
||||
const freeBreakfastPackage = breakfastPackages.data.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
)
|
||||
if (freeBreakfastPackage) {
|
||||
if (originalBreakfastPackage) {
|
||||
freeBreakfastPackage.originalPrice =
|
||||
originalBreakfastPackage.packagePrice
|
||||
}
|
||||
return [freeBreakfastPackage]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return breakfastPackages.data.filter(
|
||||
(pkg) => pkg.code !== BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
)
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
55
server/routers/hotels/schemas/packages.ts
Normal file
55
server/routers/hotels/schemas/packages.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
|
||||
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([]),
|
||||
})
|
||||
|
||||
const packagesSchema = z.array(
|
||||
z.object({
|
||||
code: z.enum([
|
||||
RoomPackageCodeEnum.PET_ROOM,
|
||||
RoomPackageCodeEnum.ALLERGY_ROOM,
|
||||
RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
|
||||
]),
|
||||
itemCode: z.string(),
|
||||
description: z.string(),
|
||||
currency: z.string(),
|
||||
calculatedPrice: z.number(),
|
||||
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: packagesSchema,
|
||||
}),
|
||||
relationships: z
|
||||
.object({
|
||||
links: z.array(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
type: z.string(),
|
||||
})
|
||||
),
|
||||
})
|
||||
.optional(),
|
||||
type: z.string(),
|
||||
}),
|
||||
})
|
||||
.transform((data) => data.data.attributes.packages)
|
||||
@@ -96,7 +96,7 @@ export async function getCountries(
|
||||
return unstable_cache(
|
||||
async function (searchParams) {
|
||||
const countryResponse = await api.get(
|
||||
api.endpoints.v1.countries,
|
||||
api.endpoints.v1.Hotel.countries,
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
@@ -136,7 +136,7 @@ export async function getCitiesByCountry(
|
||||
await Promise.all(
|
||||
searchedCountries.data.map(async (country) => {
|
||||
const countryResponse = await api.get(
|
||||
`${api.endpoints.v1.citiesCountry}/${country.name}`,
|
||||
api.endpoints.v1.Hotel.Cities.country(country.name),
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
@@ -182,7 +182,7 @@ export async function getLocations(
|
||||
groupedCitiesByCountry: CitiesGroupedByCountry | null
|
||||
) {
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.locations,
|
||||
api.endpoints.v1.Hotel.locations,
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
|
||||
@@ -35,16 +35,19 @@ export const userMutationRouter = router({
|
||||
"api.user.creditCard.add start",
|
||||
JSON.stringify({ query: { language: input.language } })
|
||||
)
|
||||
const apiResponse = await api.post(api.endpoints.v1.initiateSaveCard, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
body: {
|
||||
language: input.language,
|
||||
mobileToken: false,
|
||||
redirectUrl: `api/web/add-card-callback/${input.language}`,
|
||||
},
|
||||
})
|
||||
const apiResponse = await api.post(
|
||||
api.endpoints.v1.Profile.CreditCards.initiateSaveCard,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
body: {
|
||||
language: input.language,
|
||||
mobileToken: false,
|
||||
redirectUrl: `api/web/add-card-callback/${input.language}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
@@ -85,7 +88,7 @@ export const userMutationRouter = router({
|
||||
.mutation(async function ({ ctx, input }) {
|
||||
console.info("api.user.creditCard.save start", JSON.stringify({}))
|
||||
const apiResponse = await api.post(
|
||||
`${api.endpoints.v1.creditCards}/${input.transactionId}`,
|
||||
api.endpoints.v1.Profile.CreditCards.transaction(input.transactionId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -118,7 +121,9 @@ export const userMutationRouter = router({
|
||||
JSON.stringify({ query: {} })
|
||||
)
|
||||
const apiResponse = await api.remove(
|
||||
`${api.endpoints.v1.creditCards}/${input.creditCardId}`,
|
||||
api.endpoints.v1.Profile.CreditCards.deleteCreditCard(
|
||||
input.creditCardId
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -149,7 +154,7 @@ export const userMutationRouter = router({
|
||||
ctx,
|
||||
}) {
|
||||
generatePreferencesLinkCounter.add(1)
|
||||
const apiResponse = await api.get(api.endpoints.v1.subscriberId, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.subscriberId, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@ export const getVerifiedUser = cache(
|
||||
}
|
||||
getVerifiedUserCounter.add(1)
|
||||
console.info("api.user.profile getVerifiedUser start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.profile, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.profile, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
@@ -163,7 +163,7 @@ export const getVerifiedUser = cache(
|
||||
}
|
||||
)
|
||||
|
||||
function parsedUser(data: User, isMFA: boolean) {
|
||||
export function parsedUser(data: User, isMFA: boolean) {
|
||||
const country = countries.find((c) => c.code === data.address.countryCode)
|
||||
|
||||
const user = {
|
||||
@@ -211,7 +211,7 @@ function parsedUser(data: User, isMFA: boolean) {
|
||||
async function getCreditCards(session: Session) {
|
||||
getCreditCardsCounter.add(1)
|
||||
console.info("api.profile.creditCards start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.creditCards, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.creditCards, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
@@ -354,7 +354,7 @@ export const userQueryRouter = router({
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
const previousStaysResponse = await api.get(
|
||||
api.endpoints.v1.previousStays,
|
||||
api.endpoints.v1.Booking.Stays.past,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -430,7 +430,7 @@ export const userQueryRouter = router({
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.previousStays,
|
||||
api.endpoints.v1.Booking.Stays.past,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -525,7 +525,7 @@ export const userQueryRouter = router({
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.upcomingStays,
|
||||
api.endpoints.v1.Booking.Stays.future,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -611,13 +611,16 @@ export const userQueryRouter = router({
|
||||
"api.transaction.friendTransactions start",
|
||||
JSON.stringify({})
|
||||
)
|
||||
const apiResponse = await api.get(api.endpoints.v1.friendTransactions, {
|
||||
cache: undefined, // override defaultOptions
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
next: { revalidate: 30 * 60 * 1000 },
|
||||
})
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Profile.Transaction.friendTransactions,
|
||||
{
|
||||
cache: undefined, // override defaultOptions
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
next: { revalidate: 30 * 60 * 1000 },
|
||||
}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
// switch (apiResponse.status) {
|
||||
@@ -740,7 +743,7 @@ export const userQueryRouter = router({
|
||||
membershipCards: protectedProcedure.query(async function ({ ctx }) {
|
||||
getProfileCounter.add(1)
|
||||
console.info("api.profile start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.profile, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.profile, {
|
||||
cache: "no-store",
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
|
||||
@@ -35,7 +35,7 @@ async function updateStaysBookingUrl(
|
||||
// Temporary API call needed till we have user name in ctx session data
|
||||
getProfileCounter.add(1)
|
||||
console.info("api.user.profile updatebookingurl start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.profile, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.profile, {
|
||||
cache: "no-store",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { ImageItem } from "@/types/components/lightbox/lightbox"
|
||||
import type { Hotel } from "@/types/hotel"
|
||||
|
||||
export function extractHotelImages(hotelData: Hotel): ImageItem[] {
|
||||
const images: ImageItem[] = []
|
||||
|
||||
if (hotelData.hotelContent?.images) {
|
||||
images.push({
|
||||
url: hotelData.hotelContent.images.imageSizes.large,
|
||||
alt: hotelData.hotelContent.images.metaData.altText,
|
||||
title:
|
||||
hotelData.hotelContent.images.metaData.title ||
|
||||
hotelData.hotelContent.images.metaData.altText,
|
||||
})
|
||||
}
|
||||
|
||||
if (hotelData.healthFacilities) {
|
||||
hotelData.healthFacilities.forEach((facility) => {
|
||||
facility.content.images.forEach((image) => {
|
||||
images.push({
|
||||
url: image.imageSizes.large,
|
||||
alt: image.metaData.altText,
|
||||
title: image.metaData.title || image.metaData.altText,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return images
|
||||
}
|
||||
Reference in New Issue
Block a user