This creates the alternative hotels page. It is mostly a copy of the select hotel page, and most of the contents of the pages lives under the same component in /components.
Merged in feat/sw-397-alternative-hotels (pull request #1211) Feat/sw 397 alternative hotels * fix(SW-397): create alternative hotels page * update types * Adapt to new changes for fetching data * Make bookingcode optional * Code review fixes Approved-by: Simon.Emanuelsson
This commit is contained in:
@@ -12,6 +12,15 @@ export const getHotelsAvailabilityInputSchema = z.object({
|
||||
bookingCode: z.string().optional().default(""),
|
||||
})
|
||||
|
||||
export const getHotelsByHotelIdsAvailabilityInputSchema = z.object({
|
||||
hotelIds: z.array(z.number()),
|
||||
roomStayStartDate: z.string(),
|
||||
roomStayEndDate: z.string(),
|
||||
adults: z.number(),
|
||||
children: z.string().optional(),
|
||||
bookingCode: z.string().optional().default(""),
|
||||
})
|
||||
|
||||
export const getRoomsAvailabilityInputSchema = z.object({
|
||||
hotelId: z.number(),
|
||||
roomStayStartDate: z.string(),
|
||||
@@ -66,6 +75,10 @@ export const getHotelsInput = z.object({
|
||||
})
|
||||
export interface GetHotelsInput extends z.infer<typeof getHotelsInput> {}
|
||||
|
||||
export const nearbyHotelIdsInput = z.object({
|
||||
hotelId: z.string(),
|
||||
})
|
||||
|
||||
export const getBreakfastPackageInputSchema = z.object({
|
||||
adults: z.number().min(1, { message: "at least one adult is required" }),
|
||||
fromDate: z
|
||||
|
||||
@@ -542,6 +542,8 @@ export type HotelsAvailability = z.infer<typeof hotelsAvailabilitySchema>
|
||||
export type ProductType =
|
||||
HotelsAvailability["data"][number]["attributes"]["productType"]
|
||||
export type ProductTypePrices = z.infer<typeof productTypePriceSchema>
|
||||
export type HotelsAvailabilityItem =
|
||||
HotelsAvailability["data"][number]["attributes"]
|
||||
|
||||
const roomConfigurationSchema = z.object({
|
||||
status: z.string(),
|
||||
@@ -889,6 +891,17 @@ export const getHotelIdsByCityIdSchema = z
|
||||
})
|
||||
.transform((data) => data.data.map((hotel) => hotel.id))
|
||||
|
||||
export const getNearbyHotelIdsSchema = z
|
||||
.object({
|
||||
data: z.array(
|
||||
z.object({
|
||||
// We only care about the hotel id
|
||||
id: z.string(),
|
||||
})
|
||||
),
|
||||
})
|
||||
.transform((data) => data.data.map((hotel) => hotel.id))
|
||||
|
||||
export const getMeetingRoomsSchema = z.object({
|
||||
data: z.array(
|
||||
z.object({
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
getCityCoordinatesInputSchema,
|
||||
getHotelDataInputSchema,
|
||||
getHotelsAvailabilityInputSchema,
|
||||
getHotelsByHotelIdsAvailabilityInputSchema,
|
||||
getHotelsInput,
|
||||
getMeetingRoomsInputSchema,
|
||||
getRatesInputSchema,
|
||||
@@ -30,12 +31,14 @@ import {
|
||||
getRoomsAvailabilityInputSchema,
|
||||
getSelectedRoomAvailabilityInputSchema,
|
||||
type HotelDataInput,
|
||||
nearbyHotelIdsInput,
|
||||
} from "./input"
|
||||
import {
|
||||
breakfastPackagesSchema,
|
||||
getHotelDataSchema,
|
||||
getHotelsAvailabilitySchema,
|
||||
getMeetingRoomsSchema,
|
||||
getNearbyHotelIdsSchema,
|
||||
getRatesSchema,
|
||||
getRoomPackagesSchema,
|
||||
getRoomsAvailabilitySchema,
|
||||
@@ -59,9 +62,15 @@ import {
|
||||
hotelsAvailabilityCounter,
|
||||
hotelsAvailabilityFailCounter,
|
||||
hotelsAvailabilitySuccessCounter,
|
||||
hotelsByHotelIdAvailabilityCounter,
|
||||
hotelsByHotelIdAvailabilityFailCounter,
|
||||
hotelsByHotelIdAvailabilitySuccessCounter,
|
||||
meetingRoomsCounter,
|
||||
meetingRoomsFailCounter,
|
||||
meetingRoomsSuccessCounter,
|
||||
nearbyHotelIdsCounter,
|
||||
nearbyHotelIdsFailCounter,
|
||||
nearbyHotelIdsSuccessCounter,
|
||||
roomsAvailabilityCounter,
|
||||
roomsAvailabilityFailCounter,
|
||||
roomsAvailabilitySuccessCounter,
|
||||
@@ -204,7 +213,7 @@ export const getHotelData = cache(
|
||||
|
||||
export const hotelQueryRouter = router({
|
||||
availability: router({
|
||||
hotels: serviceProcedure
|
||||
hotelsByCity: serviceProcedure
|
||||
.input(getHotelsAvailabilityInputSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { lang } = ctx
|
||||
@@ -319,6 +328,122 @@ export const hotelQueryRouter = router({
|
||||
),
|
||||
}
|
||||
}),
|
||||
hotelsByHotelIds: serviceProcedure
|
||||
.input(getHotelsByHotelIdsAvailabilityInputSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { lang } = ctx
|
||||
const apiLang = toApiLang(lang)
|
||||
const {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
} = input
|
||||
|
||||
const params: Record<string, string | number | number[]> = {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
...(children && { children }),
|
||||
...(bookingCode && { bookingCode }),
|
||||
language: apiLang,
|
||||
}
|
||||
hotelsByHotelIdAvailabilityCounter.add(1, {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelsByHotelIdAvailability start",
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Availability.hotels(),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
hotelsByHotelIdAvailabilityFailCounter.add(1, {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsByHotelIdAvailability error",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateAvailabilityData =
|
||||
getHotelsAvailabilitySchema.safeParse(apiJson)
|
||||
if (!validateAvailabilityData.success) {
|
||||
hotelsByHotelIdAvailabilityFailCounter.add(1, {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateAvailabilityData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsByHotelIdAvailability validation error",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: validateAvailabilityData.error,
|
||||
})
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
hotelsByHotelIdAvailabilitySuccessCounter.add(1, {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelsByHotelIdAvailability success",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
})
|
||||
)
|
||||
return {
|
||||
availability: validateAvailabilityData.data.data.flatMap(
|
||||
(hotels) => hotels.attributes
|
||||
),
|
||||
}
|
||||
}),
|
||||
rooms: serviceProcedure
|
||||
.input(getRoomsAvailabilityInputSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
@@ -907,6 +1032,85 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
}),
|
||||
}),
|
||||
nearbyHotelIds: serviceProcedure
|
||||
.input(nearbyHotelIdsInput)
|
||||
.query(async function ({ ctx, input }) {
|
||||
const { lang } = ctx
|
||||
const apiLang = toApiLang(lang)
|
||||
|
||||
const { hotelId } = input
|
||||
const params: Record<string, string | number> = {
|
||||
language: apiLang,
|
||||
}
|
||||
nearbyHotelIdsCounter.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.nearbyHotelIds start",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.Hotels.nearbyHotels(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
nearbyHotelIdsFailCounter.add(1, {
|
||||
hotelId,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.nearbyHotelIds error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateHotelData = getNearbyHotelIdsSchema.safeParse(apiJson)
|
||||
if (!validateHotelData.success) {
|
||||
nearbyHotelIdsFailCounter.add(1, {
|
||||
hotelId,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateHotelData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.nearbyHotelIds validation error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: validateHotelData.error,
|
||||
})
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
nearbyHotelIdsSuccessCounter.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.nearbyHotelIds success",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
})
|
||||
)
|
||||
|
||||
return validateHotelData.data.map((id: string) => parseInt(id, 10))
|
||||
}),
|
||||
locations: router({
|
||||
get: serviceProcedure.query(async function ({ ctx }) {
|
||||
const searchParams = new URLSearchParams()
|
||||
|
||||
@@ -25,6 +25,16 @@ export const hotelsAvailabilityFailCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-fail"
|
||||
)
|
||||
|
||||
export const hotelsByHotelIdAvailabilityCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-by-hotel-id"
|
||||
)
|
||||
export const hotelsByHotelIdAvailabilitySuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-by-hotel-id-success"
|
||||
)
|
||||
export const hotelsByHotelIdAvailabilityFailCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-by-hotel-id-fail"
|
||||
)
|
||||
|
||||
export const roomsAvailabilityCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.rooms"
|
||||
)
|
||||
@@ -73,6 +83,16 @@ export const getHotelIdsFailCounter = meter.createCounter(
|
||||
"trpc.hotel.hotel-ids.get-fail"
|
||||
)
|
||||
|
||||
export const nearbyHotelIdsCounter = meter.createCounter(
|
||||
"trpc.hotel.nearby-hotel-ids.get"
|
||||
)
|
||||
export const nearbyHotelIdsSuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.nearby-hotel-ids.get-success"
|
||||
)
|
||||
export const nearbyHotelIdsFailCounter = meter.createCounter(
|
||||
"trpc.hotel.nearby-hotel-ids.get-fail"
|
||||
)
|
||||
|
||||
export const meetingRoomsCounter = meter.createCounter(
|
||||
"trpc.hotels.meetingRooms"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user