diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx
index 16e01506f..42ae5fd3e 100644
--- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx
+++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx
@@ -86,6 +86,7 @@ export default async function DetailsPage({
hotelId: booking.hotelId,
packageCodes: room.packages,
startDate: booking.fromDate,
+ lang,
})
: null
diff --git a/components/HotelReservation/SelectRate/RoomsContainer/RoomsContainerSkeleton.tsx b/components/HotelReservation/SelectRate/RoomsContainer/RoomsContainerSkeleton.tsx
index dea2d20e5..f14ca0e06 100644
--- a/components/HotelReservation/SelectRate/RoomsContainer/RoomsContainerSkeleton.tsx
+++ b/components/HotelReservation/SelectRate/RoomsContainer/RoomsContainerSkeleton.tsx
@@ -6,7 +6,7 @@ type Props = {
count?: number
}
-export async function RoomsContainerSkeleton({ count = 4 }: Props) {
+export function RoomsContainerSkeleton({ count = 4 }: Props) {
return (
diff --git a/components/HotelReservation/SelectRate/RoomsContainer/index.tsx b/components/HotelReservation/SelectRate/RoomsContainer/index.tsx
index 2e5806d2f..fb14da42d 100644
--- a/components/HotelReservation/SelectRate/RoomsContainer/index.tsx
+++ b/components/HotelReservation/SelectRate/RoomsContainer/index.tsx
@@ -1,109 +1,59 @@
+"use client"
+import { useSession } from "next-auth/react"
+
import { dt } from "@/lib/dt"
-import {
- getHotel,
- getPackages,
- getRoomsAvailability,
-} from "@/lib/trpc/memoizedRequests"
-import { auth } from "@/auth"
-import { generateChildrenString } from "@/components/HotelReservation/utils"
+import useLang from "@/hooks/useLang"
import RatesProvider from "@/providers/RatesProvider"
-import { isValidSession } from "@/utils/session"
+import { isValidClientSession } from "@/utils/clientSession"
-import { combineRoomAvailabilities } from "../utils"
+import { useHotelPackages, useRoomsAvailability } from "../utils"
import RateSummary from "./RateSummary"
import Rooms from "./Rooms"
+import { RoomsContainerSkeleton } from "./RoomsContainerSkeleton"
-import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { RoomsContainerProps } from "@/types/components/hotelReservation/selectRate/roomsContainer"
-import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
-import type { Lang } from "@/constants/languages"
-export function preload(
- hotelId: string,
- lang: Lang,
- fromDate: string,
- toDate: string,
- adults: number[],
- children?: Child[]
-) {
- void getHotel({ hotelId, isCardOnlyPayment: false, language: lang })
- void getPackages({
- adults: adults[0],
- children: children ? children?.length : undefined,
- endDate: toDate,
- hotelId,
- packageCodes: [
- RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
- RoomPackageCodeEnum.PET_ROOM,
- RoomPackageCodeEnum.ALLERGY_ROOM,
- ],
- startDate: fromDate,
- })
-
- const uniqueAdultsCount = Array.from(new Set(adults))
- uniqueAdultsCount.forEach((adultsInRoom) => {
- void getRoomsAvailability({
- adults: adultsInRoom,
- children: children ? generateChildrenString(children) : undefined,
- hotelId: +hotelId,
- roomStayEndDate: toDate,
- roomStayStartDate: fromDate,
- })
- })
-}
-
-export async function RoomsContainer({
+export function RoomsContainer({
adultArray,
booking,
childArray,
fromDate,
hotelId,
- lang,
+ hotelData,
toDate,
}: RoomsContainerProps) {
- const session = await auth()
- const isUserLoggedIn = isValidSession(session)
+ const { data: session } = useSession()
+ const isUserLoggedIn = isValidClientSession(session)
+ const lang = useLang()
const fromDateString = dt(fromDate).format("YYYY-MM-DD")
const toDateString = dt(toDate).format("YYYY-MM-DD")
- const hotelData = await getHotel({
- hotelId: hotelId.toString(),
- isCardOnlyPayment: false,
- language: lang,
- })
-
const uniqueAdultsCount = Array.from(new Set(adultArray))
- const roomsAvailabilityResults = await Promise.allSettled(
- uniqueAdultsCount.map((adultCount) =>
- getRoomsAvailability({
- adults: adultCount,
- hotelId: hotelId,
- roomStayEndDate: toDateString,
- roomStayStartDate: fromDateString,
- children:
- childArray && childArray.length > 0
- ? generateChildrenString(childArray)
- : undefined,
- })
+
+ const { isPending: isLoadingAvailability, data: roomsAvailability } =
+ useRoomsAvailability(
+ uniqueAdultsCount,
+ hotelId,
+ fromDateString,
+ toDateString,
+ lang,
+ childArray
)
+
+ const { data: packages, isPending: isLoadingPackages } = useHotelPackages(
+ adultArray,
+ childArray,
+ fromDateString,
+ toDateString,
+ hotelId,
+ lang
)
- const roomsAvailability = combineRoomAvailabilities(roomsAvailabilityResults)
-
- const packages = await getPackages({
- adults: adultArray[0],
- children: childArray ? childArray.length : undefined,
- endDate: toDateString,
- hotelId: hotelId.toString(),
- packageCodes: [
- RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
- RoomPackageCodeEnum.PET_ROOM,
- RoomPackageCodeEnum.ALLERGY_ROOM,
- ],
- startDate: fromDateString,
- })
+ if (isLoadingAvailability || isLoadingPackages) {
+ return
+ }
if (!hotelData?.hotel) {
return null
diff --git a/components/HotelReservation/SelectRate/index.tsx b/components/HotelReservation/SelectRate/index.tsx
index 7c17ab1ed..a08773c36 100644
--- a/components/HotelReservation/SelectRate/index.tsx
+++ b/components/HotelReservation/SelectRate/index.tsx
@@ -10,11 +10,7 @@ import { getHotelSearchDetails } from "@/app/[lang]/(live)/(public)/hotelreserva
import HotelInfoCard, {
HotelInfoCardSkeleton,
} from "@/components/HotelReservation/SelectRate/HotelInfoCard"
-import {
- preload,
- RoomsContainer,
-} from "@/components/HotelReservation/SelectRate/RoomsContainer"
-import { RoomsContainerSkeleton } from "@/components/HotelReservation/SelectRate/RoomsContainer/RoomsContainerSkeleton"
+import { RoomsContainer } from "@/components/HotelReservation/SelectRate/RoomsContainer"
import TrackingSDK from "@/components/TrackingSDK"
import { setLang } from "@/i18n/serverContext"
import { convertSearchParamsToObj } from "@/utils/url"
@@ -45,15 +41,6 @@ export default async function SelectRatePage({
selectHotelParams.toDate
)
- preload(
- hotel.id,
- params.lang,
- fromDate.format("YYYY-MM-DD"),
- toDate.format("YYYY-MM-DD"),
- adultsInRoom,
- childrenInRoom
- )
-
const hotelData = await getHotel({
hotelId: hotel.id,
isCardOnlyPayment: false,
@@ -104,18 +91,17 @@ export default async function SelectRatePage({
-
}>
-
-
-
+
+
+
[]
@@ -37,3 +42,56 @@ export function getRates(
),
}
}
+
+export function useRoomsAvailability(
+ uniqueAdultsCount: number[],
+ hotelId: number,
+ fromDateString: string,
+ toDateString: string,
+ lang: Lang,
+ childArray?: Child[]
+) {
+ const returnValue =
+ trpc.hotel.availability.roomsCombinedAvailability.useQuery({
+ hotelId,
+ roomStayStartDate: fromDateString,
+ roomStayEndDate: toDateString,
+ uniqueAdultsCount,
+ childArray,
+ lang,
+ })
+
+ const combinedAvailability = returnValue.data?.length
+ ? combineRoomAvailabilities(
+ returnValue.data as PromiseSettledResult[]
+ )
+ : null
+
+ return {
+ ...returnValue,
+ data: combinedAvailability,
+ }
+}
+
+export function useHotelPackages(
+ adultArray: number[],
+ childArray: Child[] | undefined,
+ fromDateString: string,
+ toDateString: string,
+ hotelId: number,
+ lang: Lang
+) {
+ return trpc.hotel.packages.get.useQuery({
+ adults: adultArray[0], // Using the first adult count
+ children: childArray ? childArray.length : undefined,
+ endDate: toDateString,
+ hotelId: hotelId.toString(),
+ packageCodes: [
+ RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
+ RoomPackageCodeEnum.PET_ROOM,
+ RoomPackageCodeEnum.ALLERGY_ROOM,
+ ],
+ startDate: fromDateString,
+ lang: lang,
+ })
+}
diff --git a/lib/trpc/memoizedRequests/index.ts b/lib/trpc/memoizedRequests/index.ts
index 2b8aff066..c4fdaa547 100644
--- a/lib/trpc/memoizedRequests/index.ts
+++ b/lib/trpc/memoizedRequests/index.ts
@@ -15,7 +15,6 @@ import type {
import type { Lang } from "@/constants/languages"
import type {
GetHotelsByCSFilterInput,
- GetRoomsAvailabilityInput,
GetSelectedRoomAvailabilityInput,
} from "@/server/routers/hotels/input"
import type { GetSavedPaymentCardsInput } from "@/server/routers/user/input"
@@ -87,12 +86,6 @@ export const getHotelPage = cache(async function getMemoizedHotelPage() {
return serverClient().contentstack.hotelPage.get()
})
-export const getRoomsAvailability = cache(
- async function getMemoizedRoomAvailability(input: GetRoomsAvailabilityInput) {
- return serverClient().hotel.availability.rooms(input)
- }
-)
-
export const getSelectedRoomAvailability = cache(
function getMemoizedSelectedRoomAvailability(
input: GetSelectedRoomAvailabilityInput
diff --git a/server/routers/hotels/input.ts b/server/routers/hotels/input.ts
index 08bbd5cbf..481d04a18 100644
--- a/server/routers/hotels/input.ts
+++ b/server/routers/hotels/input.ts
@@ -2,6 +2,7 @@ import { z } from "zod"
import { Lang } from "@/constants/languages"
+import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { Country } from "@/types/enums/country"
@@ -23,14 +24,22 @@ export const getHotelsByHotelIdsAvailabilityInputSchema = z.object({
bookingCode: z.string().optional().default(""),
})
-export const roomsAvailabilityInputSchema = z.object({
+export const roomsCombinedAvailabilityInputSchema = z.object({
hotelId: z.number(),
roomStayStartDate: z.string(),
roomStayEndDate: z.string(),
- adults: z.number(),
- children: z.string().optional(),
+ uniqueAdultsCount: z.array(z.number()),
+ childArray: z
+ .array(
+ z.object({
+ bed: z.nativeEnum(ChildBedMapEnum),
+ age: z.number(),
+ })
+ )
+ .optional(),
bookingCode: z.string().optional(),
rateCode: z.string().optional(),
+ lang: z.nativeEnum(Lang),
})
export const selectedRoomAvailabilityInputSchema = z.object({
@@ -49,10 +58,6 @@ export type GetSelectedRoomAvailabilityInput = z.input<
typeof selectedRoomAvailabilityInputSchema
>
-export type GetRoomsAvailabilityInput = z.input<
- typeof roomsAvailabilityInputSchema
->
-
export const ratesInputSchema = z.object({
hotelId: z.string(),
})
@@ -109,6 +114,7 @@ export const roomPackagesInputSchema = z.object({
adults: z.number(),
children: z.number().optional().default(0),
packageCodes: z.array(z.string()).optional().default([]),
+ lang: z.nativeEnum(Lang),
})
export const cityCoordinatesInputSchema = z.object({
city: z.string(),
diff --git a/server/routers/hotels/metrics.ts b/server/routers/hotels/metrics.ts
index 2f8a10e37..a5919787e 100644
--- a/server/routers/hotels/metrics.ts
+++ b/server/routers/hotels/metrics.ts
@@ -70,10 +70,14 @@ export const metrics = {
fail: meter.createCounter("trpc.hotel.packages.get-fail"),
success: meter.createCounter("trpc.hotel.packages.get-success"),
},
- roomAvailability: {
- counter: meter.createCounter("trpc.hotel.availability.rooms"),
- fail: meter.createCounter("trpc.hotel.availability.rooms-fail"),
- success: meter.createCounter("trpc.hotel.availability.rooms-success"),
+ roomsCombinedAvailability: {
+ counter: meter.createCounter("trpc.hotel.roomsCombinedAvailability.rooms"),
+ fail: meter.createCounter(
+ "trpc.hotel.roomsCombinedAvailability.rooms-fail"
+ ),
+ success: meter.createCounter(
+ "trpc.hotel.roomsCombinedAvailability.rooms-success"
+ ),
},
selectedRoomAvailability: {
counter: meter.createCounter("trpc.hotel.availability.room"),
diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts
index 7311abbd0..324fab639 100644
--- a/server/routers/hotels/query.ts
+++ b/server/routers/hotels/query.ts
@@ -35,7 +35,7 @@ import {
nearbyHotelIdsInput,
ratesInputSchema,
roomPackagesInputSchema,
- roomsAvailabilityInputSchema,
+ roomsCombinedAvailabilityInputSchema,
selectedRoomAvailabilityInputSchema,
} from "./input"
import { metrics } from "./metrics"
@@ -470,128 +470,98 @@ export const hotelQueryRouter = router({
const apiLang = toApiLang(lang)
return getHotelsAvailabilityByHotelIds(input, apiLang, ctx.serviceToken)
}),
- rooms: serviceProcedure
- .input(roomsAvailabilityInputSchema)
+
+ roomsCombinedAvailability: serviceProcedure
+ .input(roomsCombinedAvailabilityInputSchema)
.query(async ({ input, ctx }) => {
- const { lang } = ctx
+ const { lang } = input
const apiLang = toApiLang(lang)
const {
hotelId,
roomStayStartDate,
roomStayEndDate,
- adults,
- children,
+ uniqueAdultsCount,
+ childArray,
bookingCode,
rateCode,
} = input
- const params: Record = {
- roomStayStartDate,
- roomStayEndDate,
- adults,
- ...(children && { children }),
- ...(bookingCode && { bookingCode }),
- language: apiLang,
- }
-
- metrics.roomAvailability.counter.add(1, {
+ const metricsData = {
hotelId,
roomStayStartDate,
roomStayEndDate,
- adults,
- children,
+ uniqueAdultsCount,
+ childArray: childArray ? JSON.stringify(childArray) : undefined,
bookingCode,
- })
+ }
+
+ metrics.roomsCombinedAvailability.counter.add(1, metricsData)
+
console.info(
- "api.hotels.roomsAvailability start",
- JSON.stringify({ query: { hotelId, params } })
- )
- const apiResponse = await api.get(
- api.endpoints.v1.Availability.hotel(hotelId.toString()),
- {
- headers: {
- Authorization: `Bearer ${ctx.serviceToken}`,
- },
- },
- params
+ "api.hotels.roomsCombinedAvailability start",
+ JSON.stringify({ query: { hotelId, params: metricsData } })
)
- if (!apiResponse.ok) {
- const text = await apiResponse.text()
- metrics.roomAvailability.fail.add(1, {
- hotelId,
- roomStayStartDate,
- roomStayEndDate,
- adults,
- children,
- bookingCode,
- error_type: "http_error",
- error: JSON.stringify({
- status: apiResponse.status,
- statusText: apiResponse.statusText,
- text,
- }),
- })
- console.error(
- "api.hotels.roomsAvailability error",
- JSON.stringify({
- query: { hotelId, params },
- error: {
- status: apiResponse.status,
- statusText: apiResponse.statusText,
- text,
+ const availabilityResponses = await Promise.allSettled(
+ uniqueAdultsCount.map(async (adultCount: number) => {
+ const params: Record = {
+ roomStayStartDate,
+ roomStayEndDate,
+ adults: adultCount,
+ ...(childArray &&
+ childArray.length > 0 && {
+ children: childArray.join(","),
+ }),
+ ...(bookingCode && { bookingCode }),
+ language: apiLang,
+ }
+
+ const apiResponse = await api.get(
+ api.endpoints.v1.Availability.hotel(hotelId.toString()),
+ {
+ headers: {
+ Authorization: `Bearer ${ctx.serviceToken}`,
+ },
},
- })
- )
- return null
- }
- const apiJson = await apiResponse.json()
+ params
+ )
- const validateAvailabilityData =
- roomsAvailabilitySchema.safeParse(apiJson)
- if (!validateAvailabilityData.success) {
- metrics.roomAvailability.fail.add(1, {
- hotelId,
- roomStayStartDate,
- roomStayEndDate,
- adults,
- children,
- bookingCode,
- error_type: "validation_error",
- error: JSON.stringify(validateAvailabilityData.error),
- })
- console.error(
- "api.hotels.roomsAvailability validation error",
- JSON.stringify({
- query: { hotelId, params },
- error: validateAvailabilityData.error,
- })
- )
- return null
- }
- metrics.roomAvailability.success.add(1, {
- hotelId,
- roomStayStartDate,
- roomStayEndDate,
- adults,
- children,
- bookingCode,
- })
- console.info(
- "api.hotels.roomsAvailability success",
- JSON.stringify({
- query: { hotelId, params: params },
+ if (!apiResponse.ok) {
+ const text = await apiResponse.text()
+ metrics.roomsCombinedAvailability.fail.add(1, metricsData)
+ console.error("Failed API call", { params, text })
+ return { error: "http_error", details: text }
+ }
+
+ const apiJson = await apiResponse.json()
+
+ const validateAvailabilityData =
+ roomsAvailabilitySchema.safeParse(apiJson)
+
+ if (!validateAvailabilityData.success) {
+ console.error("Validation error", {
+ params,
+ error: validateAvailabilityData.error,
+ })
+ metrics.roomsCombinedAvailability.fail.add(1, metricsData)
+ return {
+ error: "validation_error",
+ details: validateAvailabilityData.error,
+ }
+ }
+
+ if (rateCode) {
+ validateAvailabilityData.data.mustBeGuaranteed =
+ validateAvailabilityData.data.rateDefinitions.find(
+ (rate) => rate.rateCode === rateCode
+ )?.mustBeGuaranteed
+ }
+
+ return validateAvailabilityData.data
})
)
-
- if (rateCode) {
- validateAvailabilityData.data.mustBeGuaranteed =
- validateAvailabilityData.data.rateDefinitions.filter(
- (rate) => rate.rateCode === rateCode
- )[0].mustBeGuaranteed
- }
-
- return validateAvailabilityData.data
+ metrics.roomsCombinedAvailability.success.add(1, metricsData)
+ return availabilityResponses
}),
room: serviceProcedure
.input(selectedRoomAvailabilityInputSchema)
@@ -885,40 +855,35 @@ export const hotelQueryRouter = router({
}),
}),
rates: router({
- get: publicProcedure
- .input(ratesInputSchema)
- .query(async ({ input, ctx }) => {
- // TODO: Do a real API call when the endpoint is ready
- // const { hotelId } = input
+ get: publicProcedure.input(ratesInputSchema).query(async ({}) => {
+ // TODO: Do a real API call when the endpoint is ready
+ // const { hotelId } = input
- // const params = new URLSearchParams()
- // const apiLang = toApiLang(language)
- // params.set("hotelId", hotelId.toString())
- // params.set("language", apiLang)
+ // const params = new URLSearchParams()
+ // const apiLang = toApiLang(language)
+ // params.set("hotelId", hotelId.toString())
+ // params.set("language", apiLang)
- console.info("api.hotels.rates start", JSON.stringify({}))
- const validatedHotelData = ratesSchema.safeParse(tempRatesData)
+ console.info("api.hotels.rates start", JSON.stringify({}))
+ const validatedHotelData = ratesSchema.safeParse(tempRatesData)
- if (!tempRatesData) {
- console.error(
- "api.hotels.rates error",
- JSON.stringify({ error: null })
- )
- //Can't return null here since consuming component does not handle null yet
- // return null
- }
- if (!validatedHotelData.success) {
- console.error(
- "api.hotels.rates validation error",
- JSON.stringify({
- error: validatedHotelData.error,
- })
- )
- throw badRequestError()
- }
- console.info("api.hotels.rates success", JSON.stringify({}))
- return validatedHotelData.data
- }),
+ if (!tempRatesData) {
+ console.error("api.hotels.rates error", JSON.stringify({ error: null }))
+ //Can't return null here since consuming component does not handle null yet
+ // return null
+ }
+ if (!validatedHotelData.success) {
+ console.error(
+ "api.hotels.rates validation error",
+ JSON.stringify({
+ error: validatedHotelData.error,
+ })
+ )
+ throw badRequestError()
+ }
+ console.info("api.hotels.rates success", JSON.stringify({}))
+ return validatedHotelData.data
+ }),
}),
get: serviceProcedure
.input(hotelInputSchema)
@@ -1544,7 +1509,7 @@ export const hotelQueryRouter = router({
const { hotelId, startDate, endDate, adults, children, packageCodes } =
input
- const { lang } = ctx
+ const { lang } = input
const apiLang = toApiLang(lang)
diff --git a/server/routers/hotels/telemetry.ts b/server/routers/hotels/telemetry.ts
index 9351f3f8d..df9474896 100644
--- a/server/routers/hotels/telemetry.ts
+++ b/server/routers/hotels/telemetry.ts
@@ -35,16 +35,6 @@ export const hotelsByHotelIdAvailabilityFailCounter = meter.createCounter(
"trpc.hotel.availability.hotels-by-hotel-id-fail"
)
-export const roomsAvailabilityCounter = meter.createCounter(
- "trpc.hotel.availability.rooms"
-)
-export const roomsAvailabilitySuccessCounter = meter.createCounter(
- "trpc.hotel.availability.rooms-success"
-)
-export const roomsAvailabilityFailCounter = meter.createCounter(
- "trpc.hotel.availability.rooms-fail"
-)
-
export const selectedRoomAvailabilityCounter = meter.createCounter(
"trpc.hotel.availability.room"
)
diff --git a/types/components/hotelReservation/selectRate/roomsContainer.ts b/types/components/hotelReservation/selectRate/roomsContainer.ts
index 63fb1efdc..e1f5eebf2 100644
--- a/types/components/hotelReservation/selectRate/roomsContainer.ts
+++ b/types/components/hotelReservation/selectRate/roomsContainer.ts
@@ -1,4 +1,4 @@
-import type { Lang } from "@/constants/languages"
+import type { HotelData } from "@/types/hotel"
import type { Child, SelectRateSearchParams } from "./selectRate"
export interface RoomsContainerProps {
@@ -7,6 +7,6 @@ export interface RoomsContainerProps {
childArray?: Child[]
fromDate: Date
hotelId: number
- lang: Lang
toDate: Date
+ hotelData: HotelData | null
}