)
}
diff --git a/components/HotelReservation/SelectRate/Rooms/index.tsx b/components/HotelReservation/SelectRate/Rooms/index.tsx
index 387541b98..aa3d46b06 100644
--- a/components/HotelReservation/SelectRate/Rooms/index.tsx
+++ b/components/HotelReservation/SelectRate/Rooms/index.tsx
@@ -25,14 +25,14 @@ import {
} from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { SelectRateProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
-import type { RoomConfiguration } from "@/server/routers/hotels/output"
+import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
export default function Rooms({
- roomsAvailability,
- roomCategories = [],
availablePackages,
hotelType,
isUserLoggedIn,
+ roomsAvailability,
+ roomCategories = [],
}: SelectRateProps) {
const router = useRouter()
const pathname = usePathname()
diff --git a/components/HotelReservation/SelectRate/Rooms/utils.ts b/components/HotelReservation/SelectRate/Rooms/utils.ts
index d84aae3a8..4ff593f1b 100644
--- a/components/HotelReservation/SelectRate/Rooms/utils.ts
+++ b/components/HotelReservation/SelectRate/Rooms/utils.ts
@@ -1,5 +1,4 @@
-import type { RoomParam } from "@/types/components/hotelReservation/selectRate/section"
-import type { RoomConfiguration } from "@/server/routers/hotels/output"
+import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
/**
* Get the lowest priced room for each room type that appears more than once.
@@ -64,16 +63,16 @@ export function filterDuplicateRoomTypesByLowestPrice(
if (
!previousLowest ||
currentRequestedPrice <
- Math.min(
- Number(
- previousLowest.products[0].productType.public.requestedPrice
- ?.pricePerNight
- ) ?? Infinity,
- Number(
- previousLowest.products[0].productType.member?.requestedPrice
- ?.pricePerNight
- ) ?? Infinity
- ) ||
+ Math.min(
+ Number(
+ previousLowest.products[0].productType.public.requestedPrice
+ ?.pricePerNight
+ ) ?? Infinity,
+ Number(
+ previousLowest.products[0].productType.member?.requestedPrice
+ ?.pricePerNight
+ ) ?? Infinity
+ ) ||
(currentRequestedPrice ===
Math.min(
Number(
@@ -86,16 +85,16 @@ export function filterDuplicateRoomTypesByLowestPrice(
) ?? Infinity
) &&
currentLocalPrice <
- Math.min(
- Number(
- previousLowest.products[0].productType.public.localPrice
- ?.pricePerNight
- ) ?? Infinity,
- Number(
- previousLowest.products[0].productType.member?.localPrice
- ?.pricePerNight
- ) ?? Infinity
- ))
+ Math.min(
+ Number(
+ previousLowest.products[0].productType.public.localPrice
+ ?.pricePerNight
+ ) ?? Infinity,
+ Number(
+ previousLowest.products[0].productType.member?.localPrice
+ ?.pricePerNight
+ ) ?? Infinity
+ ))
) {
roomMap.set(roomType, room)
}
diff --git a/components/HotelReservation/SidePeek/index.tsx b/components/HotelReservation/SidePeek/index.tsx
index bf75ed04b..36781e954 100644
--- a/components/HotelReservation/SidePeek/index.tsx
+++ b/components/HotelReservation/SidePeek/index.tsx
@@ -7,13 +7,11 @@ import HotelSidePeek from "@/components/SidePeeks/HotelSidePeek"
import RoomSidePeek from "@/components/SidePeeks/RoomSidePeek"
import useLang from "@/hooks/useLang"
-import type { HotelData } from "@/types/hotel"
+import type { HotelReservationSidePeekProps } from "@/types/components/hotelReservation/sidePeek"
export default function HotelReservationSidePeek({
hotel,
-}: {
- hotel: HotelData | null
-}) {
+}: HotelReservationSidePeekProps) {
const activeSidePeek = useSidePeekStore((state) => state.activeSidePeek)
const hotelId = useSidePeekStore((state) => state.hotelId)
const roomTypeCode = useSidePeekStore((state) => state.roomTypeCode)
@@ -21,7 +19,7 @@ export default function HotelReservationSidePeek({
const close = useSidePeekStore((state) => state.closeSidePeek)
const lang = useLang()
- const { data: hotelData } = trpc.hotel.hotelData.get.useQuery(
+ const { data: hotelData } = trpc.hotel.get.useQuery(
{
hotelId: hotelId ?? "",
language: lang,
@@ -32,7 +30,7 @@ export default function HotelReservationSidePeek({
}
)
- const selectedRoom = hotelData?.included.rooms?.find((room) =>
+ const selectedRoom = hotelData?.roomCategories.find((room) =>
room.roomTypes.some((type) => type.code === roomTypeCode)
)
@@ -41,8 +39,8 @@ export default function HotelReservationSidePeek({
<>
{hotelData && (
(GetHotelPageMetadata, variables)
const hotelPageData = hotelPageResponse.hotel_page
const hotelData = hotelPageData.hotel_page_id
- ? await getHotelData(
- { hotelId: hotelPageData.hotel_page_id, language: ctx.lang },
+ ? await getHotel(
+ {
+ hotelId: hotelPageData.hotel_page_id,
+ isCardOnlyPayment: false,
+ language: ctx.lang,
+ },
ctx.serviceToken
)
: null
return getTransformedMetadata({
...hotelPageData,
- hotelData: hotelData?.data.attributes,
+ hotelData: hotelData?.hotel,
})
default:
return null
diff --git a/server/routers/hotels/input.ts b/server/routers/hotels/input.ts
index 5b49eb355..22252dc80 100644
--- a/server/routers/hotels/input.ts
+++ b/server/routers/hotels/input.ts
@@ -1,9 +1,11 @@
import { z } from "zod"
+import { Lang } from "@/constants/languages"
+
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { Country } from "@/types/enums/country"
-export const getHotelsAvailabilityInputSchema = z.object({
+export const hotelsAvailabilityInputSchema = z.object({
cityId: z.string(),
roomStayStartDate: z.string(),
roomStayEndDate: z.string(),
@@ -21,7 +23,7 @@ export const getHotelsByHotelIdsAvailabilityInputSchema = z.object({
bookingCode: z.string().optional().default(""),
})
-export const getRoomsAvailabilityInputSchema = z.object({
+export const roomsAvailabilityInputSchema = z.object({
hotelId: z.number(),
roomStayStartDate: z.string(),
roomStayEndDate: z.string(),
@@ -31,7 +33,7 @@ export const getRoomsAvailabilityInputSchema = z.object({
rateCode: z.string().optional(),
})
-export const getSelectedRoomAvailabilityInputSchema = z.object({
+export const selectedRoomAvailabilityInputSchema = z.object({
hotelId: z.string(),
roomStayStartDate: z.string(),
roomStayEndDate: z.string(),
@@ -44,25 +46,23 @@ export const getSelectedRoomAvailabilityInputSchema = z.object({
})
export type GetSelectedRoomAvailabilityInput = z.input<
- typeof getSelectedRoomAvailabilityInputSchema
+ typeof selectedRoomAvailabilityInputSchema
>
export type GetRoomsAvailabilityInput = z.input<
- typeof getRoomsAvailabilityInputSchema
+ typeof roomsAvailabilityInputSchema
>
-export const getRatesInputSchema = z.object({
+export const ratesInputSchema = z.object({
hotelId: z.string(),
})
-export const getHotelDataInputSchema = z.object({
+export const hotelInputSchema = z.object({
hotelId: z.string(),
- language: z.string(),
- isCardOnlyPayment: z.boolean().optional(),
+ isCardOnlyPayment: z.boolean().default(false),
+ language: z.nativeEnum(Lang),
})
-export type HotelDataInput = z.input
-
export const getHotelsInput = z.object({
locationFilter: z
.object({
@@ -73,13 +73,13 @@ export const getHotelsInput = z.object({
.nullable(),
hotelsToInclude: z.array(z.string()),
})
-export interface GetHotelsInput extends z.infer {}
+export interface GetHotelsInput extends z.infer { }
export const nearbyHotelIdsInput = z.object({
hotelId: z.string(),
})
-export const getBreakfastPackageInputSchema = z.object({
+export const breakfastPackageInputSchema = z.object({
adults: z.number().min(1, { message: "at least one adult is required" }),
fromDate: z
.string()
@@ -92,7 +92,7 @@ export const getBreakfastPackageInputSchema = z.object({
.pipe(z.coerce.date()),
})
-export const getRoomPackagesInputSchema = z.object({
+export const roomPackagesInputSchema = z.object({
hotelId: z.string(),
startDate: z.string(),
endDate: z.string(),
@@ -100,7 +100,7 @@ export const getRoomPackagesInputSchema = z.object({
children: z.number().optional().default(0),
packageCodes: z.array(z.string()).optional().default([]),
})
-export const getCityCoordinatesInputSchema = z.object({
+export const cityCoordinatesInputSchema = z.object({
city: z.string(),
hotel: z.object({
address: z.string().optional(),
diff --git a/server/routers/hotels/metrics.ts b/server/routers/hotels/metrics.ts
new file mode 100644
index 000000000..85c15a06d
--- /dev/null
+++ b/server/routers/hotels/metrics.ts
@@ -0,0 +1,65 @@
+import { metrics as opentelemetryMetrics } from "@opentelemetry/api"
+
+const meter = opentelemetryMetrics.getMeter("trpc.hotels")
+export const metrics = {
+ additionalData: {
+ counter: meter.createCounter("trpc.hotels.additionalData"),
+ fail: meter.createCounter("trpc.hotels.additionalData-fail"),
+ success: meter.createCounter("trpc.hotels.additionalData-success"),
+ },
+ breakfastPackage: {
+ counter: meter.createCounter("trpc.package.breakfast"),
+ fail: meter.createCounter("trpc.package.breakfast-fail"),
+ success: meter.createCounter("trpc.package.breakfast-success"),
+ },
+ hotel: {
+ counter: meter.createCounter("trpc.hotel.get"),
+ fail: meter.createCounter("trpc.hotel.get-fail"),
+ success: meter.createCounter("trpc.hotel.get-success"),
+ },
+ hotels: {
+ counter: meter.createCounter("trpc.hotel.hotels.get"),
+ fail: meter.createCounter("trpc.hotel.hotels.get-fail"),
+ success: meter.createCounter("trpc.hotel.hotels.get-success"),
+ },
+ hotelIds: {
+ counter: meter.createCounter("trpc.hotel.hotel-ids.get"),
+ fail: meter.createCounter("trpc.hotel.hotel-ids.get-fail"),
+ success: meter.createCounter("trpc.hotel.hotel-ids.get-success"),
+ },
+ hotelsAvailability: {
+ counter: meter.createCounter("trpc.hotel.availability.hotels"),
+ fail: meter.createCounter("trpc.hotel.availability.hotels-fail"),
+ success: meter.createCounter("trpc.hotel.availability.hotels-success"),
+ },
+ hotelsByHotelIdAvailability: {
+ counter: meter.createCounter("trpc.hotel.availability.hotels-by-hotel-id"),
+ fail: meter.createCounter("trpc.hotel.availability.hotels-by-hotel-id-fail"),
+ success: meter.createCounter("trpc.hotel.availability.hotels-by-hotel-id-success"),
+ },
+ meetingRooms: {
+ counter: meter.createCounter("trpc.hotels.meetingRooms"),
+ fail: meter.createCounter("trpc.hotels.meetingRooms-fail"),
+ success: meter.createCounter("trpc.hotels.meetingRooms-success"),
+ },
+ nearbyHotelIds: {
+ counter: meter.createCounter("trpc.hotel.nearby-hotel-ids.get"),
+ fail: meter.createCounter("trpc.hotel.nearby-hotel-ids.get-fail"),
+ success: meter.createCounter("trpc.hotel.nearby-hotel-ids.get-success"),
+ },
+ packages: {
+ counter: meter.createCounter("trpc.hotel.packages.get"),
+ 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"),
+ },
+ selectedRoomAvailability: {
+ counter: meter.createCounter("trpc.hotel.availability.room"),
+ fail: meter.createCounter("trpc.hotel.availability.room-fail"),
+ success: meter.createCounter("trpc.hotel.availability.room-success"),
+ },
+}
diff --git a/server/routers/hotels/output.ts b/server/routers/hotels/output.ts
index 40c12ea20..a25b7fb12 100644
--- a/server/routers/hotels/output.ts
+++ b/server/routers/hotels/output.ts
@@ -1,668 +1,120 @@
import { z } from "zod"
-import { ChildBedTypeEnum, type PaymentMethodEnum } from "@/constants/booking"
import { toLang } from "@/server/utils"
-import { additionalDataSchema } from "./schemas/additionalData"
-import { imageSchema } from "./schemas/image"
-import { restaurantSchema } from "./schemas/restaurants"
-import { roomSchema } from "./schemas/room"
-import { specialAlertsSchema } from "./schemas/specialAlerts"
-import { getPoiGroupByCategoryName } from "./utils"
+import { occupancySchema } from "./schemas/availability/occupancy"
+import { productTypeSchema } from "./schemas/availability/productType"
+import { citySchema } from "./schemas/city"
+import {
+ attributesSchema,
+ includesSchema,
+ relationshipsSchema as hotelRelationshipsSchema,
+} from "./schemas/hotel"
+import { locationCitySchema } from "./schemas/location/city"
+import { locationHotelSchema } from "./schemas/location/hotel"
+import { breakfastPackageSchema, packageSchema } from "./schemas/packages"
+import { rateSchema } from "./schemas/rate"
+import { relationshipsSchema } from "./schemas/relationships"
+import { roomConfigurationSchema } from "./schemas/roomAvailability/configuration"
+import { rateDefinitionSchema } from "./schemas/roomAvailability/rateDefinition"
-import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
-import { FacilityEnum } from "@/types/enums/facilities"
-import { PackageTypeEnum } from "@/types/enums/packages"
-import type { AdditionalData, RestaurantData, RoomData } from "@/types/hotel"
-
-const ratingsSchema = z
- .object({
- tripAdvisor: z.object({
- numberOfReviews: z.number(),
- rating: z.number(),
- ratingImageUrl: z.string(),
- webUrl: z.string(),
- awards: z.array(
- z.object({
- displayName: z.string(),
- images: z.object({
- small: z.string(),
- medium: z.string(),
- large: z.string(),
- }),
- })
- ),
- reviews: z
- .object({
- widgetHtmlTagId: z.string(),
- widgetScriptEmbedUrlIframe: z.string(),
- widgetScriptEmbedUrlJavaScript: z.string(),
- })
- .optional(),
- }),
- })
- .optional()
-
-const addressSchema = z.object({
- streetAddress: z.string(),
- city: z.string(),
- zipCode: z.string(),
- country: z.string(),
-})
-
-const contactInformationSchema = z.object({
- phoneNumber: z.string(),
- faxNumber: z.string().optional(),
- email: z.string(),
- websiteUrl: z.string(),
-})
-
-export const checkinSchema = z.object({
- checkInTime: z.string(),
- checkOutTime: z.string(),
- onlineCheckOutAvailableFrom: z.string().nullable().optional(),
- onlineCheckout: z.boolean(),
-})
-
-const ecoLabelsSchema = z.object({
- euEcoLabel: z.boolean(),
- greenGlobeLabel: z.boolean(),
- nordicEcoLabel: z.boolean(),
- svanenEcoLabelCertificateNumber: z.string().optional(),
-})
-
-const interiorSchema = z.object({
- numberOfBeds: z.number(),
- numberOfCribs: z.number(),
- numberOfFloors: z.number(),
- numberOfRooms: z.object({
- connected: z.number(),
- forAllergics: z.number().optional(),
- forDisabled: z.number(),
- nonSmoking: z.number(),
- pet: z.number(),
- withExtraBeds: z.number(),
- total: z.number(),
- }),
-})
-
-const receptionHoursSchema = z.object({
- alwaysOpen: z.boolean(),
- isClosed: z.boolean(),
- openingTime: z.string().optional(),
- closingTime: z.string().optional(),
-})
-
-const locationSchema = z.object({
- distanceToCentre: z.number(),
- latitude: z.number(),
- longitude: z.number(),
-})
-
-const hotelContentSchema = z.object({
- images: imageSchema,
- texts: z.object({
- facilityInformation: z.string().optional(),
- surroundingInformation: z.string(),
- descriptions: z.object({
- short: z.string(),
- medium: z.string(),
- }),
- meetingDescription: z
- .object({
- short: z.string().optional(),
- medium: z.string().optional(),
- })
- .optional(),
- }),
- restaurantsOverviewPage: z.object({
- restaurantsOverviewPageLinkText: z.string().optional(),
- restaurantsOverviewPageLink: z.string().optional(),
- restaurantsContentDescriptionShort: z.string().optional(),
- restaurantsContentDescriptionMedium: z.string().optional(),
- }), // TODO remove, use new /additionalData endpoint
-})
-
-const detailedFacilitySchema = z.object({
- id: z.number(),
- name: z.string(),
- public: z.boolean(),
- sortOrder: z.number(),
- filter: z.string().optional(),
- icon: z.string().optional(),
-})
-
-export const facilitySchema = z.object({
- headingText: z.string().default(""),
- heroImages: z.array(imageSchema),
-}) // TODO remove, use new /additionalData endpoint
-
-export const gallerySchema = z.object({
- heroImages: z.array(imageSchema),
- smallerImages: z.array(imageSchema),
-}) // TODO remove, use new /additionalData endpoint
-
-const healthFacilitySchema = z.object({
- type: z.string(),
- content: z.object({
- images: z.array(imageSchema),
- texts: z.object({
- facilityInformation: z.string().optional(),
- surroundingInformation: z.string().optional(),
- descriptions: z.object({
- short: z.string(),
- medium: z.string(),
- }),
- }),
- }),
- openingDetails: z.object({
- useManualOpeningHours: z.boolean(),
- manualOpeningHours: z.string().optional(),
- openingHours: z.object({
- ordinary: z.object({
- alwaysOpen: z.boolean(),
- isClosed: z.boolean(),
- openingTime: z.string().optional(),
- closingTime: z.string().optional(),
- sortOrder: z.number().optional(),
- }),
- weekends: z.object({
- alwaysOpen: z.boolean(),
- isClosed: z.boolean(),
- openingTime: z.string().optional(),
- closingTime: z.string().optional(),
- sortOrder: z.number().optional(),
- }),
- }),
- }),
- details: z.array(
- z.object({
- name: z.string(),
- type: z.string(),
- value: z.string().optional(),
- })
- ),
-})
-
-const rewardNightSchema = z.object({
- points: z.number(),
- campaign: z.object({
- start: z.string(),
- end: z.string(),
- points: z.number(),
- }),
-})
-
-export const pointOfInterestSchema = z
- .object({
- name: z.string().optional(),
- distance: z.number().optional(),
- category: z.object({
- name: z.string().optional(),
- group: z.string().optional(),
- }),
- location: locationSchema.optional(),
- isHighlighted: z.boolean().optional(),
- })
- .transform((poi) => ({
- name: poi.name,
- distance: poi.distance,
- categoryName: poi.category.name,
- group: getPoiGroupByCategoryName(poi.category.name),
- coordinates: {
- lat: poi.location?.latitude ?? 0,
- lng: poi.location?.longitude ?? 0,
- },
- }))
-
-const parkingPricingSchema = z.object({
- freeParking: z.boolean(),
- paymentType: z.string().optional(),
- localCurrency: z.object({
- currency: z.string().default("N/A"),
- range: z.object({
- min: z.number().optional(),
- max: z.number().optional(),
- }),
- ordinary: z
- .array(
- z.object({
- period: z.string().optional(),
- amount: z.number().optional(),
- startTime: z.string().optional(),
- endTime: z.string().optional(),
- })
- )
- .optional(),
- weekend: z
- .array(
- z.object({
- period: z.string().optional(),
- amount: z.number().optional(),
- startTime: z.string().optional(),
- endTime: z.string().optional(),
- })
- )
- .optional(),
- }),
- requestedCurrency: z
- .object({
- currency: z.string().default("N/A"),
- range: z
- .object({
- min: z.number().optional(),
- max: z.number().optional(),
- })
- .optional(),
- ordinary: z
- .array(
- z.object({
- period: z.string().optional(),
- amount: z.number().optional(),
- startTime: z.string().optional(),
- endTime: z.string().optional(),
- })
- )
- .optional(),
- weekend: z
- .array(
- z.object({
- period: z.string().optional(),
- amount: z.number().optional(),
- startTime: z.string().optional(),
- endTime: z.string().optional(),
- })
- )
- .optional(),
- })
- .optional(),
-})
-
-export const parkingSchema = z.object({
- type: z.string().optional(),
- name: z.string().optional(),
- address: z.string().optional(),
- numberOfParkingSpots: z.number().optional(),
- numberOfChargingSpaces: z.number().optional(),
- distanceToHotel: z.number().optional(),
- canMakeReservation: z.boolean(),
- externalParkingUrl: z.string().optional(),
- pricing: parkingPricingSchema,
-})
-
-const socialMediaSchema = z.object({
- instagram: z.string().optional(),
- facebook: z.string().optional(),
-})
-
-const relationshipsSchema = z.object({
- restaurants: z.object({
- links: z.object({
- related: z.string(),
- }),
- }),
- nearbyHotels: z.object({
- links: z.object({
- related: z.string(),
- }),
- }),
- roomCategories: z.object({
- links: z.object({
- related: z.string(),
- }),
- }),
- meetingRooms: z.object({
- links: z.object({
- related: z.string(),
- }),
- }),
-})
-
-const merchantInformationSchema = z.object({
- webMerchantId: z.string(),
- cards: z.record(z.string(), z.boolean()).transform((val) => {
- return Object.entries(val)
- .filter(([_, enabled]) => enabled)
- .map(([key]) => key)
- .filter((key): key is PaymentMethodEnum => !!key)
- }),
- alternatePaymentOptions: z
- .record(z.string(), z.boolean())
- .transform((val) => {
- return Object.entries(val)
- .filter(([_, enabled]) => enabled)
- .map(([key]) => key)
- .filter((key): key is PaymentMethodEnum => !!key)
- }),
-})
-
-const hotelFacilityDetailSchema = z
- .object({
- description: z.string(),
- heading: z.string(),
- })
- .optional()
-
-/** Possibly more values */
-const hotelFacilityDetailsSchema = z.object({
- breakfast: hotelFacilityDetailSchema,
- checkout: hotelFacilityDetailSchema,
- gym: hotelFacilityDetailSchema,
- internet: hotelFacilityDetailSchema,
- laundry: hotelFacilityDetailSchema,
- luggage: hotelFacilityDetailSchema,
- shop: hotelFacilityDetailSchema,
- telephone: hotelFacilityDetailSchema,
-})
-
-const hotelInformationSchema = z
- .object({
- description: z.string(),
- heading: z.string(),
- link: z.string().optional(),
- })
- .optional()
-
-const hotelInformationsSchema = z.object({
- accessibility: hotelInformationSchema,
- safety: hotelInformationSchema,
- sustainability: hotelInformationSchema,
-})
-
-const hotelFactsSchema = z.object({
- checkin: checkinSchema,
- ecoLabels: ecoLabelsSchema,
- hotelFacilityDetail: hotelFacilityDetailsSchema.default({}),
- hotelInformation: hotelInformationsSchema.default({}),
- interior: interiorSchema,
- receptionHours: receptionHoursSchema,
- yearBuilt: z.string(),
-})
-
-type DetailedFacility = { id: FacilityEnum } & z.infer<
- typeof detailedFacilitySchema
->
-export const hotelAttributesSchema = z.object({
- address: addressSchema,
- cityId: z.string(),
- cityName: z.string(),
- conferencesAndMeetings: facilitySchema.optional(), // TODO remove, use new /additionalData endpoint
- contactInformation: contactInformationSchema,
- detailedFacilities: z.array(detailedFacilitySchema).transform(
- (facilities) =>
- facilities
- // Filter away facilities with ID:s that we don't recognize
- .filter(
- (f) => f.id !== undefined && f.id !== null && f.id in FacilityEnum
- )
- .sort((a, b) => b.sortOrder - a.sortOrder) as DetailedFacility[]
- ),
- gallery: gallerySchema.optional(), // TODO remove, use new /additionalData endpoint
- galleryImages: z.array(imageSchema).optional(),
- healthFacilities: z.array(healthFacilitySchema),
- hotelContent: hotelContentSchema,
- hotelFacts: hotelFactsSchema,
- hotelType: z.string().optional(),
- isActive: z.boolean(),
- isPublished: z.boolean(),
- keywords: z.array(z.string()),
- location: locationSchema,
- merchantInformationData: merchantInformationSchema,
- name: z.string(),
- operaId: z.string(),
- parking: z.array(parkingSchema),
- pointsOfInterest: z
- .array(pointOfInterestSchema)
- .transform((pois) =>
- pois.sort((a, b) => (a.distance ?? 0) - (b.distance ?? 0))
- ),
- ratings: ratingsSchema,
- rewardNight: rewardNightSchema,
- restaurantImages: facilitySchema.optional(), // TODO remove, use new /additionalData endpoint
- socialMedia: socialMediaSchema,
- specialAlerts: specialAlertsSchema,
- vat: z.number(),
-})
-
-const includedSchema = z
- .array(z.union([roomSchema, restaurantSchema, additionalDataSchema]))
- .transform((data) => {
- const rooms = data.filter((d) => d.type === "roomcategories") as RoomData[]
- const restaurants = data.filter(
- (d) => d.type === "restaurants"
- ) as RestaurantData[]
- const additionalData = data.filter(
- (d) => d.type === "additionalData"
- ) as AdditionalData[]
- return {
- rooms,
- restaurants,
- additionalData,
- }
- })
+import type { AdditionalData, City, NearbyHotel, Restaurant, Room } from "@/types/hotel"
// NOTE: Find schema at: https://aks-test.scandichotels.com/hotel/swagger/v1/index.html
-export const getHotelDataSchema = z.object({
- data: z.object({
- id: z.string(),
- type: z.literal("hotels"), // No enum here but the standard return appears to be "hotels".
- language: z.string().transform((val) => {
- const lang = toLang(val)
- if (!lang) {
- throw new Error("Invalid language")
- }
- return lang
+export const hotelSchema = z
+ .object({
+ data: z.object({
+ attributes: attributesSchema,
+ id: z.string(),
+ language: z.string().transform((val) => {
+ const lang = toLang(val)
+ if (!lang) {
+ throw new Error("Invalid language")
+ }
+ return lang
+ }),
+ relationships: hotelRelationshipsSchema,
+ type: z.literal("hotels"), // No enum here but the standard return appears to be "hotels".
}),
- attributes: hotelAttributesSchema,
- relationships: relationshipsSchema,
- }),
- // NOTE: We can pass an "include" param to the hotel API to retrieve
- // additional data for an individual hotel.
- included: includedSchema.optional().transform((incl) => {
+ // NOTE: We can pass an "include" param to the hotel API to retrieve
+ // additional data for an individual hotel.
+ included: includesSchema,
+ })
+ .transform(({ data: { attributes, ...data }, included }) => {
+ const additionalData = included.find((inc): inc is AdditionalData => inc!.type === "additionalData")
+ const cities = included.filter((inc): inc is City => inc!.type === "cities")
+ const nearbyHotels = included.filter(
+ (inc): inc is NearbyHotel => inc!.type === "hotels"
+ )
+ const restaurants = included.filter(
+ (inc): inc is Restaurant => inc!.type === "restaurants"
+ )
+ const roomCategories = included.filter(
+ (inc): inc is Room => inc!.type === "roomcategories"
+ )
return {
- restaurants: incl?.restaurants,
- rooms: incl?.rooms,
- additionalData: incl?.additionalData[0],
- }
- }),
-})
-
-export const childrenSchema = z.object({
- age: z.number(),
- bedType: z.nativeEnum(ChildBedTypeEnum),
-})
-
-const occupancySchema = z.object({
- adults: z.number(),
- children: z.array(childrenSchema).default([]),
-})
-
-const linksSchema = z.object({
- links: z.array(
- z.object({
- url: z.string().url(),
- type: z.string(),
- })
- ),
-})
-
-export const priceSchema = z.object({
- pricePerNight: z.coerce.number(),
- pricePerStay: z.coerce.number(),
- currency: z.string().default("N/A"),
-})
-
-export const productTypePriceSchema = z.object({
- rateCode: z.string(),
- rateType: z.string().optional(),
- localPrice: priceSchema,
- requestedPrice: priceSchema.optional(),
-})
-
-const productSchema = z.object({
- productType: z.object({
- public: productTypePriceSchema.default({
- rateCode: "",
- rateType: "",
- localPrice: {
- currency: "N/A",
- pricePerNight: 0,
- pricePerStay: 0,
+ additionalData,
+ cities,
+ hotel: {
+ ...data,
+ ...attributes,
},
- requestedPrice: undefined,
- }),
- member: productTypePriceSchema.optional(),
- }),
-})
+ nearbyHotels,
+ restaurants,
+ roomCategories,
+ }
+ })
-const hotelsAvailabilitySchema = z.object({
+export 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(),
+ occupancy: occupancySchema,
+ productType: productTypeSchema,
+ status: z.string(),
}),
- relationships: linksSchema.optional(),
+ relationships: relationshipsSchema.optional(),
type: z.string().optional(),
})
),
})
-export const getHotelsAvailabilitySchema = hotelsAvailabilitySchema
-export type HotelsAvailability = z.infer
-export type ProductType =
- HotelsAvailability["data"][number]["attributes"]["productType"]
-export type ProductTypePrices = z.infer
-export type HotelsAvailabilityItem =
- HotelsAvailability["data"][number]["attributes"]
-
-const roomConfigurationSchema = z.object({
- status: z.string(),
- roomTypeCode: z.string(),
- roomType: z.string(),
- roomsLeft: z.number(),
- features: z
- .array(
- z.object({
- inventory: z.number(),
- code: z.enum([
- RoomPackageCodeEnum.PET_ROOM,
- RoomPackageCodeEnum.ALLERGY_ROOM,
- RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
- ]),
- })
- )
- .default([]),
- products: z.array(productSchema).default([]),
-})
-
-const rateDefinitionSchema = z.object({
- title: z.string(),
- breakfastIncluded: z.boolean(),
- rateType: z.string().optional(),
- rateCode: z.string(),
- generalTerms: z.array(z.string()),
- cancellationRule: z.string(),
- cancellationText: z.string(),
- mustBeGuaranteed: z.boolean(),
-})
-
-const roomsAvailabilitySchema = z
+export const roomsAvailabilitySchema = z
.object({
data: z.object({
attributes: z.object({
checkInDate: z.string(),
checkOutDate: z.string(),
- occupancy: occupancySchema.optional(),
hotelId: z.number(),
- bookingCode: z.string().optional(),
- roomConfigurations: z.array(roomConfigurationSchema),
- rateDefinitions: z.array(rateDefinitionSchema),
mustBeGuaranteed: z.boolean().optional(),
+ occupancy: occupancySchema.optional(),
+ rateDefinitions: z.array(rateDefinitionSchema),
+ roomConfigurations: z.array(roomConfigurationSchema),
}),
- relationships: linksSchema.optional(),
+ relationships: relationshipsSchema.optional(),
type: z.string().optional(),
}),
})
.transform((o) => o.data.attributes)
-export const getRoomsAvailabilitySchema = roomsAvailabilitySchema
-export type RoomsAvailability = z.infer
-export type RoomConfiguration = z.infer
-export type Product = z.infer
-export type RateDefinition = z.infer
+export const ratesSchema = z.array(rateSchema)
-const flexibilityPrice = z.object({
- standard: z.number(),
- member: z.number(),
-})
-
-const rate = z.object({
- id: z.number(),
- name: z.string(),
- description: z.string(),
- size: z.string(),
- imageSrc: z.string(),
- breakfastIncluded: z.boolean(),
- prices: z.object({
- currency: z.string(),
- nonRefundable: flexibilityPrice,
- freeRebooking: flexibilityPrice,
- freeCancellation: flexibilityPrice,
- }),
-})
-
-export const getRatesSchema = z.array(rate)
-export type Rate = z.infer
-
-const hotelFilter = z.object({
- roomFacilities: z.array(z.string()),
- hotelFacilities: z.array(z.string()),
- hotelSurroundings: z.array(z.string()),
-})
-
-export const getFiltersSchema = hotelFilter
-export type HotelFilter = z.infer
-
-export const apiCitiesByCountrySchema = z.object({
+export const citiesByCountrySchema = z.object({
data: z.array(
- z
- .object({
- attributes: z.object({
- cityIdentifier: z.string().optional(),
- name: z.string(),
- keywords: z.array(z.string()).optional(),
- timeZoneId: z.string().optional(),
- ianaTimeZoneId: z.string().optional(),
- isPublished: z.boolean().optional().default(false),
- }),
- id: z.string(),
- type: z.literal("cities"),
- })
- .transform((data) => {
- return {
- ...data.attributes,
- id: data.id,
- type: data.type,
- }
- })
+ citySchema.transform((data) => {
+ return {
+ ...data.attributes,
+ id: data.id,
+ type: data.type,
+ }
+ })
),
})
-export interface CitiesByCountry
- extends z.output {}
-export type CitiesGroupedByCountry = Record
-
-export const apiCountriesSchema = z.object({
+export const countriesSchema = z.object({
data: z
.array(
z.object({
@@ -689,35 +141,9 @@ export const apiCountriesSchema = z.object({
}),
})
-export interface Countries extends z.output {}
-
-export const apiLocationCitySchema = z.object({
- attributes: z.object({
- cityIdentifier: z.string().optional(),
- keyWords: z.array(z.string()).optional(),
- name: z.string().optional().default(""),
- }),
- country: z.string().optional().default(""),
- id: z.string().optional().default(""),
- type: z.literal("cities"),
-})
-
-export const apiCitySchema = z
+export const citiesSchema = z
.object({
- data: z.array(
- z.object({
- attributes: z.object({
- cityIdentifier: z.string().optional(),
- name: z.string(),
- keywords: z.array(z.string()),
- timeZoneId: z.string().optional(),
- ianaTimeZoneId: z.string().optional(),
- isPublished: z.boolean().optional().default(false),
- }),
- id: z.string().optional(),
- type: z.literal("cities"),
- })
- ),
+ data: z.array(citySchema),
})
.transform(({ data }) => {
if (data.length) {
@@ -731,46 +157,11 @@ export const apiCitySchema = z
return null
})
-export const apiLocationHotelSchema = z.object({
- attributes: z.object({
- distanceToCentre: z.number().optional(),
- images: z
- .object({
- large: z.string().optional(),
- medium: z.string().optional(),
- small: z.string().optional(),
- tiny: z.string().optional(),
- })
- .optional(),
- keyWords: z.array(z.string()).optional(),
- name: z.string().optional().default(""),
- operaId: z.string().optional(),
- }),
- id: z.string().optional().default(""),
- relationships: z
- .object({
- city: z
- .object({
- links: z
- .object({
- related: z.string().optional(),
- })
- .optional(),
- })
- .optional(),
- })
- .optional(),
- type: z.literal("hotels"),
-})
-
-export const apiLocationsSchema = z.object({
+export const locationsSchema = z.object({
data: z
.array(
z
- .discriminatedUnion("type", [
- apiLocationCitySchema,
- apiLocationHotelSchema,
- ])
+ .discriminatedUnion("type", [locationCitySchema, locationHotelSchema])
.transform((location) => {
if (location.type === "cities") {
return {
@@ -813,20 +204,6 @@ export const apiLocationsSchema = z.object({
),
})
-export const packagePriceSchema = z.object({
- currency: z.string().default("N/A"),
- price: z.string(),
- totalPrice: z.string(),
-})
-
-export const breakfastPackageSchema = z.object({
- code: z.string(),
- description: z.string(),
- localPrice: packagePriceSchema,
- requestedPrice: packagePriceSchema,
- packageType: z.literal(PackageTypeEnum.BreakfastAdult),
-})
-
export const breakfastPackagesSchema = z
.object({
data: z.object({
@@ -838,38 +215,23 @@ export const breakfastPackagesSchema = z
}),
})
.transform(({ data }) =>
- data.attributes.packages.filter((pkg) => pkg.code.match(/^(BRF\d+)$/gm))
+ data.attributes.packages.filter((pkg) => pkg.code?.match(/^(BRF\d+)$/gm))
)
-export const packagesSchema = z.object({
- code: z.nativeEnum(RoomPackageCodeEnum),
- itemCode: z.string().optional(),
- 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
+export const packagesSchema = z
.object({
data: z
.object({
attributes: z.object({
hotelId: z.number(),
- packages: z.array(packagesSchema).optional().default([]),
+ packages: z.array(packageSchema).default([]),
}),
relationships: z
.object({
links: z.array(
z.object({
- url: z.string(),
type: z.string(),
+ url: z.string(),
})
),
})
@@ -878,7 +240,7 @@ export const getRoomPackagesSchema = z
})
.optional(),
})
- .transform((data) => data.data?.attributes?.packages ?? [])
+ .transform(({ data }) => data?.attributes.packages)
export const getHotelIdsByCityIdSchema = z
.object({
@@ -900,50 +262,4 @@ export const getNearbyHotelIdsSchema = z
})
),
})
- .transform((data) => data.data.map((hotel) => hotel.id))
-
-export const getMeetingRoomsSchema = z.object({
- data: z.array(
- z.object({
- attributes: z.object({
- name: z.string(),
- email: z.string().optional(),
- phoneNumber: z.string(),
- size: z.number(),
- doorWidth: z.number(),
- doorHeight: z.number(),
- length: z.number(),
- width: z.number(),
- height: z.number(),
- floorNumber: z.number(),
- content: z.object({
- images: z.array(imageSchema),
- texts: z.object({
- facilityInformation: z.string().optional(),
- surroundingInformation: z.string().optional(),
- descriptions: z.object({
- short: z.string().optional(),
- medium: z.string().optional(),
- }),
- meetingDescription: z
- .object({
- short: z.string().optional(),
- medium: z.string().optional(),
- })
- .optional(),
- }),
- }),
- seatings: z.array(
- z.object({
- type: z.string(),
- capacity: z.number(),
- })
- ),
- lighting: z.string(),
- sortOrder: z.number().optional(),
- }),
- id: z.string(),
- type: z.string(),
- })
- ),
-})
+ .transform((data) => data.data.map((hotel) => hotel.id))
\ No newline at end of file
diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts
index 9a4a87aa7..52fe76898 100644
--- a/server/routers/hotels/query.ts
+++ b/server/routers/hotels/query.ts
@@ -1,5 +1,4 @@
import { ApiLang } from "@/constants/languages"
-import { env } from "@/env/server"
import * as api from "@/lib/api"
import { dt } from "@/lib/dt"
import { badRequestError } from "@/server/errors/trpc"
@@ -17,67 +16,30 @@ import { cache } from "@/utils/cache"
import { getHotelPageUrl } from "../contentstack/hotelPage/utils"
import { getVerifiedUser, parsedUser } from "../user/query"
import {
+ breakfastPackageInputSchema,
+ cityCoordinatesInputSchema,
getAdditionalDataInputSchema,
- getBreakfastPackageInputSchema,
- getCityCoordinatesInputSchema,
- getHotelDataInputSchema,
- getHotelsAvailabilityInputSchema,
getHotelsByHotelIdsAvailabilityInputSchema,
getHotelsInput,
getMeetingRoomsInputSchema,
- getRatesInputSchema,
- getRoomPackagesInputSchema,
- getRoomsAvailabilityInputSchema,
- getSelectedRoomAvailabilityInputSchema,
- type HotelDataInput,
+ hotelInputSchema,
+ hotelsAvailabilityInputSchema,
nearbyHotelIdsInput,
+ ratesInputSchema,
+ roomPackagesInputSchema,
+ roomsAvailabilityInputSchema,
+ selectedRoomAvailabilityInputSchema,
} from "./input"
+import { metrics } from "./metrics"
import {
breakfastPackagesSchema,
- getHotelDataSchema,
- getHotelsAvailabilitySchema,
- getMeetingRoomsSchema,
getNearbyHotelIdsSchema,
- getRatesSchema,
- getRoomPackagesSchema,
- getRoomsAvailabilitySchema,
+ hotelsAvailabilitySchema,
+ hotelSchema,
+ packagesSchema,
+ ratesSchema,
+ roomsAvailabilitySchema,
} from "./output"
-import { additionalDataSchema } from "./schemas/additionalData"
-import {
- additionalDataCounter,
- additionalDataFailCounter,
- additionalDataSuccessCounter,
- breakfastPackagesCounter,
- breakfastPackagesFailCounter,
- breakfastPackagesSuccessCounter,
- getHotelCounter,
- getHotelFailCounter,
- getHotelsCounter,
- getHotelsFailCounter,
- getHotelsSuccessCounter,
- getHotelSuccessCounter,
- getPackagesCounter,
- getPackagesFailCounter,
- getPackagesSuccessCounter,
- hotelsAvailabilityCounter,
- hotelsAvailabilityFailCounter,
- hotelsAvailabilitySuccessCounter,
- hotelsByHotelIdAvailabilityCounter,
- hotelsByHotelIdAvailabilityFailCounter,
- hotelsByHotelIdAvailabilitySuccessCounter,
- meetingRoomsCounter,
- meetingRoomsFailCounter,
- meetingRoomsSuccessCounter,
- nearbyHotelIdsCounter,
- nearbyHotelIdsFailCounter,
- nearbyHotelIdsSuccessCounter,
- roomsAvailabilityCounter,
- roomsAvailabilityFailCounter,
- roomsAvailabilitySuccessCounter,
- selectedRoomAvailabilityCounter,
- selectedRoomAvailabilityFailCounter,
- selectedRoomAvailabilitySuccessCounter,
-} from "./telemetry"
import tempRatesData from "./tempRatesData.json"
import {
getCitiesByCountry,
@@ -92,23 +54,33 @@ import type { BedTypeSelection } from "@/types/components/hotelReservation/enter
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import { HotelTypeEnum } from "@/types/enums/hotelType"
import type { RequestOptionsWithOutBody } from "@/types/fetch"
-import type { Hotel } from "@/types/hotel"
+import type { HotelInput } from "@/types/trpc/routers/hotel/hotel"
+import type { HotelData } from "@/types/hotel"
import type { HotelPageUrl } from "@/types/trpc/routers/contentstack/hotelPage"
-import type { CityLocation } from "@/types/trpc/routers/hotel/locations"
+import { CityLocation } from "@/types/trpc/routers/hotel/locations"
+import { meetingRoomsSchema } from "./schemas/meetingRoom"
+import { env } from "@/env/server"
+import { additionalDataSchema } from "./schemas/additionalData"
-export const getHotelData = cache(
- async (input: HotelDataInput, serviceToken: string) => {
- const { hotelId, language, isCardOnlyPayment } = input
-
- const includes = ["RoomCategories", "Restaurants", "AdditionalData"] //"NearbyHotels","Restaurants","City",
- const params = new URLSearchParams({
- hotelId,
- language,
- })
-
- includes.forEach((include) => params.append("include", include))
-
- getHotelCounter.add(1, {
+export const getHotel = cache(
+ async (input: HotelInput, serviceToken: string) => {
+ const { hotelId, isCardOnlyPayment, language } = input
+ /**
+ * Since API expects the params appended and not just
+ * a comma separated string we need to initialize the
+ * SearchParams with a sequence of pairs
+ * (include=City&include=NearbyHotels&include=Restaurants etc.)
+ **/
+ const params = new URLSearchParams([
+ ["hotelId", hotelId],
+ ["include", "AdditionalData"],
+ ["include", "City"],
+ ["include", "NearbyHotels"],
+ ["include", "Restaurants"],
+ ["include", "RoomCategories"],
+ ["language", toApiLang(language)],
+ ])
+ metrics.hotel.counter.add(1, {
hotelId,
language,
})
@@ -128,6 +100,7 @@ export const getHotelData = cache(
cache: undefined,
next: {
revalidate: env.CACHE_TIME_HOTELDATA,
+ tags: [`${language}:hotel:${hotelId}`],
},
},
params
@@ -135,7 +108,8 @@ export const getHotelData = cache(
if (!apiResponse.ok) {
const text = await apiResponse.text()
- getHotelFailCounter.add(1, {
+ console.log({ text })
+ metrics.hotel.fail.add(1, {
hotelId,
language,
error_type: "http_error",
@@ -160,10 +134,10 @@ export const getHotelData = cache(
}
const apiJson = await apiResponse.json()
- const validateHotelData = getHotelDataSchema.safeParse(apiJson)
+ const validateHotelData = hotelSchema.safeParse(apiJson)
if (!validateHotelData.success) {
- getHotelFailCounter.add(1, {
+ metrics.hotel.fail.add(1, {
hotelId,
language,
error_type: "validation_error",
@@ -180,7 +154,7 @@ export const getHotelData = cache(
throw badRequestError()
}
- getHotelSuccessCounter.add(1, {
+ metrics.hotel.success.add(1, {
hotelId,
language,
})
@@ -193,18 +167,17 @@ export const getHotelData = cache(
const hotelData = validateHotelData.data
if (isCardOnlyPayment) {
- hotelData.data.attributes.merchantInformationData.alternatePaymentOptions =
- []
+ hotelData.hotel.merchantInformationData.alternatePaymentOptions = []
}
- const gallery = hotelData.included.additionalData?.gallery
+ const gallery = hotelData.additionalData?.gallery
if (gallery) {
const smallerImages = gallery.smallerImages
const hotelGalleryImages =
- hotelData.data.attributes.hotelType === HotelTypeEnum.Signature
+ hotelData.hotel.hotelType === HotelTypeEnum.Signature
? smallerImages.slice(0, 10)
: smallerImages.slice(0, 6)
- hotelData.data.attributes.galleryImages = hotelGalleryImages
+ hotelData.hotel.galleryImages = hotelGalleryImages
}
return hotelData
@@ -214,7 +187,7 @@ export const getHotelData = cache(
export const hotelQueryRouter = router({
availability: router({
hotelsByCity: serviceProcedure
- .input(getHotelsAvailabilityInputSchema)
+ .input(hotelsAvailabilityInputSchema)
.query(async ({ input, ctx }) => {
const { lang } = ctx
const apiLang = toApiLang(lang)
@@ -235,7 +208,7 @@ export const hotelQueryRouter = router({
...(bookingCode && { bookingCode }),
language: apiLang,
}
- hotelsAvailabilityCounter.add(1, {
+ metrics.hotelsAvailability.counter.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
@@ -258,7 +231,7 @@ export const hotelQueryRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
- hotelsAvailabilityFailCounter.add(1, {
+ metrics.hotelsAvailability.fail.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
@@ -287,9 +260,9 @@ export const hotelQueryRouter = router({
}
const apiJson = await apiResponse.json()
const validateAvailabilityData =
- getHotelsAvailabilitySchema.safeParse(apiJson)
+ hotelsAvailabilitySchema.safeParse(apiJson)
if (!validateAvailabilityData.success) {
- hotelsAvailabilityFailCounter.add(1, {
+ metrics.hotelsAvailability.fail.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
@@ -308,7 +281,7 @@ export const hotelQueryRouter = router({
)
throw badRequestError()
}
- hotelsAvailabilitySuccessCounter.add(1, {
+ metrics.hotelsAvailability.success.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
@@ -351,7 +324,7 @@ export const hotelQueryRouter = router({
...(bookingCode && { bookingCode }),
language: apiLang,
}
- hotelsByHotelIdAvailabilityCounter.add(1, {
+ metrics.hotelsByHotelIdAvailability.counter.add(1, {
hotelIds,
roomStayStartDate,
roomStayEndDate,
@@ -374,7 +347,7 @@ export const hotelQueryRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
- hotelsByHotelIdAvailabilityFailCounter.add(1, {
+ metrics.hotelsByHotelIdAvailability.fail.add(1, {
hotelIds,
roomStayStartDate,
roomStayEndDate,
@@ -403,9 +376,9 @@ export const hotelQueryRouter = router({
}
const apiJson = await apiResponse.json()
const validateAvailabilityData =
- getHotelsAvailabilitySchema.safeParse(apiJson)
+ hotelsAvailabilitySchema.safeParse(apiJson)
if (!validateAvailabilityData.success) {
- hotelsByHotelIdAvailabilityFailCounter.add(1, {
+ metrics.hotelsByHotelIdAvailability.fail.add(1, {
hotelIds,
roomStayStartDate,
roomStayEndDate,
@@ -424,7 +397,7 @@ export const hotelQueryRouter = router({
)
throw badRequestError()
}
- hotelsByHotelIdAvailabilitySuccessCounter.add(1, {
+ metrics.hotelsByHotelIdAvailability.success.add(1, {
hotelIds,
roomStayStartDate,
roomStayEndDate,
@@ -445,7 +418,7 @@ export const hotelQueryRouter = router({
}
}),
rooms: serviceProcedure
- .input(getRoomsAvailabilityInputSchema)
+ .input(roomsAvailabilityInputSchema)
.query(async ({ input, ctx }) => {
const { lang } = ctx
const apiLang = toApiLang(lang)
@@ -468,7 +441,7 @@ export const hotelQueryRouter = router({
language: apiLang,
}
- roomsAvailabilityCounter.add(1, {
+ metrics.roomAvailability.counter.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -492,7 +465,7 @@ export const hotelQueryRouter = router({
if (!apiResponse.ok) {
const text = await apiResponse.text()
- roomsAvailabilityFailCounter.add(1, {
+ metrics.roomAvailability.fail.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -522,9 +495,9 @@ export const hotelQueryRouter = router({
const apiJson = await apiResponse.json()
const validateAvailabilityData =
- getRoomsAvailabilitySchema.safeParse(apiJson)
+ roomsAvailabilitySchema.safeParse(apiJson)
if (!validateAvailabilityData.success) {
- roomsAvailabilityFailCounter.add(1, {
+ metrics.roomAvailability.fail.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -543,7 +516,7 @@ export const hotelQueryRouter = router({
)
throw badRequestError()
}
- roomsAvailabilitySuccessCounter.add(1, {
+ metrics.roomAvailability.success.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -568,7 +541,7 @@ export const hotelQueryRouter = router({
return validateAvailabilityData.data
}),
room: serviceProcedure
- .input(getSelectedRoomAvailabilityInputSchema)
+ .input(selectedRoomAvailabilityInputSchema)
.query(async ({ input, ctx }) => {
const {
hotelId,
@@ -591,7 +564,7 @@ export const hotelQueryRouter = router({
language: toApiLang(ctx.lang),
}
- selectedRoomAvailabilityCounter.add(1, {
+ metrics.selectedRoomAvailability.counter.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -615,7 +588,7 @@ export const hotelQueryRouter = router({
if (!apiResponseAvailability.ok) {
const text = await apiResponseAvailability.text()
- selectedRoomAvailabilityFailCounter.add(1, {
+ metrics.selectedRoomAvailability.fail.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -644,9 +617,9 @@ export const hotelQueryRouter = router({
}
const apiJsonAvailability = await apiResponseAvailability.json()
const validateAvailabilityData =
- getRoomsAvailabilitySchema.safeParse(apiJsonAvailability)
+ roomsAvailabilitySchema.safeParse(apiJsonAvailability)
if (!validateAvailabilityData.success) {
- selectedRoomAvailabilityFailCounter.add(1, {
+ metrics.selectedRoomAvailability.fail.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -666,10 +639,11 @@ export const hotelQueryRouter = router({
throw badRequestError()
}
- const hotelData = await getHotelData(
+ const hotelData = await getHotel(
{
hotelId,
- language: toApiLang(ctx.lang),
+ isCardOnlyPayment: false,
+ language: ctx.lang,
},
ctx.serviceToken
)
@@ -696,7 +670,7 @@ export const hotelQueryRouter = router({
(room) => room.roomType === selectedRoom?.roomType
)
if (!selectedRoom) {
- selectedRoomAvailabilityFailCounter.add(1, {
+ metrics.selectedRoomAvailability.fail.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -717,7 +691,7 @@ export const hotelQueryRouter = router({
)
if (!rateTypes) {
- selectedRoomAvailabilityFailCounter.add(1, {
+ metrics.selectedRoomAvailability.fail.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -739,7 +713,7 @@ export const hotelQueryRouter = router({
const bedTypes = availableRoomsInCategory
.map((availRoom) => {
- const matchingRoom = hotelData?.included.rooms
+ const matchingRoom = hotelData?.roomCategories
?.find((room) =>
room.roomTypes
.map((roomType) => roomType.code)
@@ -757,16 +731,16 @@ export const hotelQueryRouter = router({
type: matchingRoom.mainBed.type,
extraBed: matchingRoom.fixedExtraBed
? {
- type: matchingRoom.fixedExtraBed.type,
- description: matchingRoom.fixedExtraBed.description,
- }
+ type: matchingRoom.fixedExtraBed.type,
+ description: matchingRoom.fixedExtraBed.description,
+ }
: undefined,
}
}
})
.filter((bed): bed is BedTypeSelection => Boolean(bed))
- selectedRoomAvailabilitySuccessCounter.add(1, {
+ metrics.selectedRoomAvailability.success.add(1, {
hotelId,
roomStayStartDate,
roomStayEndDate,
@@ -795,7 +769,7 @@ export const hotelQueryRouter = router({
}),
rates: router({
get: publicProcedure
- .input(getRatesInputSchema)
+ .input(ratesInputSchema)
.query(async ({ input, ctx }) => {
// TODO: Do a real API call when the endpoint is ready
// const { hotelId } = input
@@ -806,7 +780,7 @@ export const hotelQueryRouter = router({
// params.set("language", apiLang)
console.info("api.hotels.rates start", JSON.stringify({}))
- const validatedHotelData = getRatesSchema.safeParse(tempRatesData)
+ const validatedHotelData = ratesSchema.safeParse(tempRatesData)
if (!tempRatesData) {
console.error(
@@ -829,13 +803,11 @@ export const hotelQueryRouter = router({
return validatedHotelData.data
}),
}),
- hotelData: router({
- get: serviceProcedure
- .input(getHotelDataInputSchema)
- .query(async ({ ctx, input }) => {
- return getHotelData(input, ctx.serviceToken)
- }),
- }),
+ get: serviceProcedure
+ .input(hotelInputSchema)
+ .query(async ({ ctx, input }) => {
+ return getHotel(input, ctx.serviceToken)
+ }),
hotels: router({
get: contentStackBaseWithServiceProcedure
.input(getHotelsInput)
@@ -858,7 +830,7 @@ export const hotelQueryRouter = router({
let hotelsToFetch: string[] = []
- getHotelsCounter.add(1, {
+ metrics.hotels.counter.add(1, {
input: JSON.stringify(input),
language,
})
@@ -889,11 +861,11 @@ export const hotelQueryRouter = router({
}
const cityId = locations
- .filter((loc): loc is CityLocation => loc.type === "cities")
+ .filter((loc): loc is CityLocation => "type" in loc && loc.type === "cities")
.find((loc) => loc.cityIdentifier === locationFilter.city)?.id
if (!cityId) {
- getHotelsFailCounter.add(1, {
+ metrics.hotels.fail.add(1, {
input: JSON.stringify(input),
language,
error_type: "not_found",
@@ -921,7 +893,7 @@ export const hotelQueryRouter = router({
)
if (!hotelIds?.length) {
- getHotelsFailCounter.add(1, {
+ metrics.hotels.fail.add(1, {
cityId,
language,
error_type: "not_found",
@@ -956,7 +928,7 @@ export const hotelQueryRouter = router({
)
if (!hotelIds?.length) {
- getHotelsFailCounter.add(1, {
+ metrics.hotels.fail.add(1, {
country: locationFilter.country,
language,
error_type: "not_found",
@@ -981,7 +953,7 @@ export const hotelQueryRouter = router({
}
if (!hotelsToFetch.length) {
- getHotelsFailCounter.add(1, {
+ metrics.hotels.fail.add(1, {
input: JSON.stringify(input),
language,
error_type: "not_found",
@@ -1001,18 +973,18 @@ export const hotelQueryRouter = router({
const hotels = await Promise.all(
hotelsToFetch.map(async (hotelId) => {
const [hotelData, url] = await Promise.all([
- getHotelData({ hotelId, language }, ctx.serviceToken),
+ getHotel({ hotelId, language }, ctx.serviceToken),
getHotelPageUrl(language, hotelId),
])
return {
- data: hotelData?.data.attributes,
+ data: hotelData,
url,
}
})
)
- getHotelsSuccessCounter.add(1, {
+ metrics.hotels.success.add(1, {
input: JSON.stringify(input),
language,
})
@@ -1028,7 +1000,7 @@ export const hotelQueryRouter = router({
)
return hotels.filter(
- (hotel): hotel is { data: Hotel; url: HotelPageUrl } => !!hotel.data
+ (hotel): hotel is { data: HotelData; url: HotelPageUrl } => !!hotel.data
)
}),
}),
@@ -1042,7 +1014,7 @@ export const hotelQueryRouter = router({
const params: Record = {
language: apiLang,
}
- nearbyHotelIdsCounter.add(1, {
+ metrics.nearbyHotelIds.counter.add(1, {
hotelId,
})
console.info(
@@ -1060,7 +1032,7 @@ export const hotelQueryRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
- nearbyHotelIdsFailCounter.add(1, {
+ metrics.nearbyHotelIds.fail.add(1, {
hotelId,
error_type: "http_error",
error: JSON.stringify({
@@ -1085,7 +1057,7 @@ export const hotelQueryRouter = router({
const apiJson = await apiResponse.json()
const validateHotelData = getNearbyHotelIdsSchema.safeParse(apiJson)
if (!validateHotelData.success) {
- nearbyHotelIdsFailCounter.add(1, {
+ metrics.nearbyHotelIds.fail.add(1, {
hotelId,
error_type: "validation_error",
error: JSON.stringify(validateHotelData.error),
@@ -1099,7 +1071,7 @@ export const hotelQueryRouter = router({
)
throw badRequestError()
}
- nearbyHotelIdsSuccessCounter.add(1, {
+ metrics.nearbyHotelIds.success.add(1, {
hotelId,
})
console.info(
@@ -1156,204 +1128,9 @@ 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 { lang } = ctx
-
- const apiLang = toApiLang(lang)
-
- const searchParams = new URLSearchParams({
- startDate,
- endDate,
- adults: adults.toString(),
- children: children.toString(),
- language: apiLang,
- })
-
- 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}`,
- },
- },
- searchParams
- )
-
- 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 } })
- )
- }
-
- 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(getBreakfastPackageInputSchema)
- .query(async function ({ ctx, input }) {
- const { lang } = ctx
-
- const apiLang = toApiLang(lang)
- const params = {
- Adults: input.adults,
- EndDate: dt(input.toDate).format("YYYY-MM-DD"),
- StartDate: dt(input.fromDate).format("YYYY-MM-DD"),
- language: apiLang,
- }
-
- const metricsData = { ...params, hotelId: input.hotelId }
- 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 freeBreakfastPackage = breakfastPackages.data.find(
- (pkg) => pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
- )
- if (freeBreakfastPackage?.localPrice) {
- return [freeBreakfastPackage]
- }
- }
- }
- }
-
- return breakfastPackages.data.filter(
- (pkg) => pkg.code !== BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
- )
- }),
- }),
map: router({
city: serviceProcedure
- .input(getCityCoordinatesInputSchema)
+ .input(cityCoordinatesInputSchema)
.query(async function ({ input }) {
const apiKey = process.env.GOOGLE_STATIC_MAP_KEY
const { city, hotel } = input
@@ -1399,7 +1176,7 @@ export const hotelQueryRouter = router({
language,
}
const metricsData = { ...params, hotelId: input.hotelId }
- meetingRoomsCounter.add(1, metricsData)
+ metrics.meetingRooms.counter.add(1, metricsData)
console.info(
"api.hotels.meetingRooms start",
JSON.stringify({ query: { hotelId, params } })
@@ -1421,7 +1198,7 @@ export const hotelQueryRouter = router({
if (!apiResponse.ok) {
const text = await apiResponse.text()
- meetingRoomsFailCounter.add(1, {
+ metrics.meetingRooms.fail.add(1, {
...metricsData,
error_type: "http_error",
error: JSON.stringify({
@@ -1445,7 +1222,7 @@ export const hotelQueryRouter = router({
}
const apiJson = await apiResponse.json()
- const validatedMeetingRooms = getMeetingRoomsSchema.safeParse(apiJson)
+ const validatedMeetingRooms = meetingRoomsSchema.safeParse(apiJson)
if (!validatedMeetingRooms.success) {
console.error(
@@ -1457,7 +1234,7 @@ export const hotelQueryRouter = router({
)
throw badRequestError()
}
- meetingRoomsSuccessCounter.add(1, {
+ metrics.meetingRooms.success.add(1, {
hotelId,
})
console.info(
@@ -1477,7 +1254,7 @@ export const hotelQueryRouter = router({
language,
}
const metricsData = { ...params, hotelId: input.hotelId }
- additionalDataCounter.add(1, metricsData)
+ metrics.additionalData.counter.add(1, metricsData)
console.info(
"api.hotels.additionalData start",
JSON.stringify({ query: { hotelId, params } })
@@ -1499,7 +1276,7 @@ export const hotelQueryRouter = router({
if (!apiResponse.ok) {
const text = await apiResponse.text()
- additionalDataFailCounter.add(1, {
+ metrics.additionalData.fail.add(1, {
...metricsData,
error_type: "http_error",
error: JSON.stringify({
@@ -1535,7 +1312,7 @@ export const hotelQueryRouter = router({
)
throw badRequestError()
}
- additionalDataSuccessCounter.add(1, {
+ metrics.additionalData.success.add(1, {
hotelId,
})
console.info(
@@ -1545,4 +1322,199 @@ export const hotelQueryRouter = router({
return validatedAdditionalData.data
}),
+ packages: router({
+ get: serviceProcedure
+ .input(roomPackagesInputSchema)
+ .query(async ({ input, ctx }) => {
+ const { hotelId, startDate, endDate, adults, children, packageCodes } =
+ input
+
+ const { lang } = ctx
+
+ const apiLang = toApiLang(lang)
+
+ const searchParams = new URLSearchParams({
+ startDate,
+ endDate,
+ adults: adults.toString(),
+ children: children.toString(),
+ language: apiLang,
+ })
+
+ packageCodes.forEach((code) => {
+ searchParams.append("packageCodes", code)
+ })
+
+ const params = searchParams.toString()
+
+ metrics.packages.counter.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}`,
+ },
+ },
+ searchParams
+ )
+
+ if (!apiResponse.ok) {
+ metrics.packages.fail.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 } })
+ )
+ }
+
+ const apiJson = await apiResponse.json()
+ const validatedPackagesData = packagesSchema.safeParse(apiJson)
+ if (!validatedPackagesData.success) {
+ metrics.packages.fail.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()
+ }
+
+ metrics.packages.success.add(1, {
+ hotelId,
+ })
+ console.info(
+ "api.hotels.packages success",
+ JSON.stringify({ query: { hotelId, params: params } })
+ )
+
+ return validatedPackagesData.data
+ }),
+ breakfast: safeProtectedServiceProcedure
+ .input(breakfastPackageInputSchema)
+ .query(async function ({ ctx, input }) {
+ const { lang } = ctx
+
+ const apiLang = toApiLang(lang)
+ const params = {
+ Adults: input.adults,
+ EndDate: dt(input.toDate).format("YYYY-MM-DD"),
+ StartDate: dt(input.fromDate).format("YYYY-MM-DD"),
+ language: apiLang,
+ }
+
+ const metricsData = { ...params, hotelId: input.hotelId }
+ metrics.breakfastPackage.counter.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()
+ metrics.breakfastPackage.fail.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) {
+ metrics.breakfastPackage.fail.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
+ }
+
+ metrics.breakfastPackage.success.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 freeBreakfastPackage = breakfastPackages.data.find(
+ (pkg) => pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
+ )
+ if (freeBreakfastPackage?.localPrice) {
+ return [freeBreakfastPackage]
+ }
+ }
+ }
+ }
+
+ return breakfastPackages.data.filter(
+ (pkg) => pkg.code !== BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
+ )
+ }),
+ }),
})
diff --git a/server/routers/hotels/schemas/additionalData.ts b/server/routers/hotels/schemas/additionalData.ts
index ab2f26020..86c0d14eb 100644
--- a/server/routers/hotels/schemas/additionalData.ts
+++ b/server/routers/hotels/schemas/additionalData.ts
@@ -60,8 +60,13 @@ export const additionalDataSchema = z
}),
type: z.literal("additionalData"),
})
- .transform(({ attributes, type }) => ({
- ...attributes,
- type,
- id: attributes.id,
- }))
+
+export function transformAdditionalData(
+ data: z.output
+) {
+ return {
+ ...data.attributes,
+ id: data.attributes.id,
+ type: data.type
+ }
+}
\ No newline at end of file
diff --git a/server/routers/hotels/schemas/availability/occupancy.ts b/server/routers/hotels/schemas/availability/occupancy.ts
new file mode 100644
index 000000000..c066008c3
--- /dev/null
+++ b/server/routers/hotels/schemas/availability/occupancy.ts
@@ -0,0 +1,13 @@
+import { z } from "zod"
+
+import { ChildBedTypeEnum } from "@/constants/booking"
+
+export const childrenSchema = z.object({
+ age: z.number(),
+ bedType: z.nativeEnum(ChildBedTypeEnum),
+})
+
+export const occupancySchema = z.object({
+ adults: z.number(),
+ children: z.array(childrenSchema),
+})
diff --git a/server/routers/hotels/schemas/availability/productType.ts b/server/routers/hotels/schemas/availability/productType.ts
new file mode 100644
index 000000000..aa5d6a1e5
--- /dev/null
+++ b/server/routers/hotels/schemas/availability/productType.ts
@@ -0,0 +1,10 @@
+import { z } from "zod"
+
+import { productTypePriceSchema } from "../productTypePrice"
+
+export const productTypeSchema = z
+ .object({
+ public: productTypePriceSchema.optional(),
+ member: productTypePriceSchema.optional(),
+ })
+ .optional()
diff --git a/server/routers/hotels/schemas/city.ts b/server/routers/hotels/schemas/city.ts
new file mode 100644
index 000000000..aa31775b6
--- /dev/null
+++ b/server/routers/hotels/schemas/city.ts
@@ -0,0 +1,14 @@
+import { z } from "zod"
+
+export const citySchema = z.object({
+ attributes: z.object({
+ cityIdentifier: z.string().default(""),
+ ianaTimeZoneId: z.string().default(""),
+ isPublished: z.boolean().default(false),
+ keywords: z.array(z.string()).default([]),
+ name: z.string(),
+ timeZoneId: z.string().default(""),
+ }),
+ id: z.string(),
+ type: z.literal("cities"),
+})
diff --git a/server/routers/hotels/schemas/hotel.ts b/server/routers/hotels/schemas/hotel.ts
new file mode 100644
index 000000000..6e5257097
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel.ts
@@ -0,0 +1,73 @@
+import { z } from "zod"
+
+import { addressSchema } from "./hotel/address"
+import { contactInformationSchema } from "./hotel/contactInformation"
+import { hotelContentSchema } from "./hotel/content"
+import { detailedFacilitiesSchema } from "./hotel/detailedFacility"
+import { hotelFactsSchema } from "./hotel/facts"
+import { gallerySchema } from "./hotel/gallery"
+import { healthFacilitySchema } from "./hotel/healthFacilities"
+import { includeSchema } from "./hotel/include/include"
+import { locationSchema } from "./hotel/location"
+import { merchantInformationSchema } from "./hotel/merchantInformation"
+import { parkingSchema } from "./hotel/parking"
+import { pointOfInterestsSchema } from "./hotel/poi"
+import { ratingsSchema } from "./hotel/rating"
+import { rewardNightSchema } from "./hotel/rewardNight"
+import { socialMediaSchema } from "./hotel/socialMedia"
+import { specialAlertsSchema } from "./hotel/specialAlerts"
+import { specialNeedGroupSchema } from "./hotel/specialNeedGroups"
+import { imageSchema } from "./image"
+import { facilitySchema } from "./additionalData"
+
+export const attributesSchema = z.object({
+ accessibilityElevatorPitchText: z.string().optional(),
+ address: addressSchema,
+ cityId: z.string(),
+ cityName: z.string(),
+ conferencesAndMeetings: facilitySchema.optional(),
+ contactInformation: contactInformationSchema,
+ detailedFacilities: detailedFacilitiesSchema,
+ gallery: gallerySchema.optional(),
+ galleryImages: z.array(imageSchema).optional(),
+ healthAndWellness: facilitySchema.optional(),
+ healthFacilities: z.array(healthFacilitySchema),
+ hotelContent: hotelContentSchema,
+ hotelFacts: hotelFactsSchema,
+ hotelRoomElevatorPitchText: z.string().optional(),
+ hotelType: z.string().optional(),
+ isActive: z.boolean(),
+ isPublished: z.boolean(),
+ keywords: z.array(z.string()),
+ location: locationSchema,
+ merchantInformationData: merchantInformationSchema,
+ name: z.string(),
+ operaId: z.string(),
+ parking: z.array(parkingSchema),
+ pointsOfInterest: pointOfInterestsSchema,
+ ratings: ratingsSchema,
+ rewardNight: rewardNightSchema,
+ restaurantImages: facilitySchema.optional(),
+ socialMedia: socialMediaSchema,
+ specialAlerts: specialAlertsSchema,
+ specialNeedGroups: z.array(specialNeedGroupSchema),
+ vat: z.number(),
+})
+
+export const includesSchema = z
+ .array(includeSchema)
+ .default([])
+ .transform((data) => data.filter((item) => !!item))
+
+const relationshipSchema = z.object({
+ links: z.object({
+ related: z.string(),
+ }),
+})
+
+export const relationshipsSchema = z.object({
+ meetingRooms: relationshipSchema,
+ nearbyHotels: relationshipSchema,
+ restaurants: relationshipSchema,
+ roomCategories: relationshipSchema,
+})
diff --git a/server/routers/hotels/schemas/hotel/address.ts b/server/routers/hotels/schemas/hotel/address.ts
new file mode 100644
index 000000000..401734a53
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/address.ts
@@ -0,0 +1,8 @@
+import { z } from "zod"
+
+export const addressSchema = z.object({
+ city: z.string(),
+ country: z.string(),
+ streetAddress: z.string(),
+ zipCode: z.string(),
+})
diff --git a/server/routers/hotels/schemas/hotel/contactInformation.ts b/server/routers/hotels/schemas/hotel/contactInformation.ts
new file mode 100644
index 000000000..c3bff3cc6
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/contactInformation.ts
@@ -0,0 +1,8 @@
+import { z } from "zod"
+
+export const contactInformationSchema = z.object({
+ email: z.string(),
+ faxNumber: z.string().optional(),
+ phoneNumber: z.string(),
+ websiteUrl: z.string(),
+})
diff --git a/server/routers/hotels/schemas/hotel/content.ts b/server/routers/hotels/schemas/hotel/content.ts
new file mode 100644
index 000000000..e6d1757e1
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/content.ts
@@ -0,0 +1,40 @@
+import { z } from "zod"
+
+import { imageSchema } from "../image"
+
+export const hotelContentSchema = z.object({
+ images: imageSchema.default({
+ metaData: {
+ altText: "default image",
+ altText_En: "default image",
+ copyRight: "default image",
+ title: "default image",
+ },
+ imageSizes: {
+ large: "https://placehold.co/1280x720",
+ medium: "https://placehold.co/1280x720",
+ small: "https://placehold.co/1280x720",
+ tiny: "https://placehold.co/1280x720",
+ },
+ }),
+ restaurantsOverviewPage: z.object({
+ restaurantsContentDescriptionMedium: z.string().optional(),
+ restaurantsContentDescriptionShort: z.string().optional(),
+ restaurantsOverviewPageLink: z.string().optional(),
+ restaurantsOverviewPageLinkText: z.string().optional(),
+ }),
+ texts: z.object({
+ descriptions: z.object({
+ medium: z.string(),
+ short: z.string(),
+ }),
+ facilityInformation: z.string().optional(),
+ meetingDescription: z
+ .object({
+ medium: z.string().optional(),
+ short: z.string().optional(),
+ })
+ .optional(),
+ surroundingInformation: z.string(),
+ }),
+})
diff --git a/server/routers/hotels/schemas/hotel/detailedFacility.ts b/server/routers/hotels/schemas/hotel/detailedFacility.ts
new file mode 100644
index 000000000..e30353df5
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/detailedFacility.ts
@@ -0,0 +1,18 @@
+import { z } from "zod"
+
+import { FacilityEnum } from "@/types/enums/facilities"
+
+const detailedFacilitySchema = z.object({
+ filter: z.string().optional(),
+ icon: z.string().optional(),
+ id: z.nativeEnum(FacilityEnum),
+ name: z.string(),
+ public: z.boolean(),
+ sortOrder: z.number(),
+})
+
+export const detailedFacilitiesSchema = z
+ .array(detailedFacilitySchema)
+ .transform((facilities) =>
+ facilities.sort((a, b) => b.sortOrder - a.sortOrder)
+ )
diff --git a/server/routers/hotels/schemas/hotel/facts.ts b/server/routers/hotels/schemas/hotel/facts.ts
new file mode 100644
index 000000000..f538b26f7
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/facts.ts
@@ -0,0 +1,80 @@
+import { z } from "zod"
+
+const ecoLabelsSchema = z.object({
+ euEcoLabel: z.boolean(),
+ greenGlobeLabel: z.boolean(),
+ nordicEcoLabel: z.boolean(),
+ svanenEcoLabelCertificateNumber: z.string().optional(),
+})
+
+export const checkinSchema = z.object({
+ checkInTime: z.string(),
+ checkOutTime: z.string(),
+ onlineCheckout: z.boolean(),
+ onlineCheckOutAvailableFrom: z.string().nullable().optional(),
+})
+
+const hotelFacilityDetailSchema = z
+ .object({
+ description: z.string(),
+ heading: z.string(),
+ })
+ .optional()
+
+/** Possibly more values */
+const hotelFacilityDetailsSchema = z.object({
+ breakfast: hotelFacilityDetailSchema,
+ checkout: hotelFacilityDetailSchema,
+ gym: hotelFacilityDetailSchema,
+ internet: hotelFacilityDetailSchema,
+ laundry: hotelFacilityDetailSchema,
+ luggage: hotelFacilityDetailSchema,
+ shop: hotelFacilityDetailSchema,
+ telephone: hotelFacilityDetailSchema,
+})
+
+const hotelInformationSchema = z
+ .object({
+ description: z.string(),
+ heading: z.string(),
+ link: z.string().optional(),
+ })
+ .optional()
+
+const hotelInformationsSchema = z.object({
+ accessibility: hotelInformationSchema,
+ safety: hotelInformationSchema,
+ sustainability: hotelInformationSchema,
+})
+
+const interiorSchema = z.object({
+ numberOfBeds: z.number(),
+ numberOfCribs: z.number(),
+ numberOfFloors: z.number(),
+ numberOfRooms: z.object({
+ connected: z.number(),
+ forAllergics: z.number().optional(),
+ forDisabled: z.number(),
+ nonSmoking: z.number(),
+ pet: z.number(),
+ withExtraBeds: z.number(),
+ total: z.number(),
+ }),
+})
+
+const receptionHoursSchema = z.object({
+ alwaysOpen: z.boolean(),
+ isClosed: z.boolean(),
+ openingTime: z.string().optional(),
+ closingTime: z.string().optional(),
+})
+
+export const hotelFactsSchema = z.object({
+ checkin: checkinSchema,
+ ecoLabels: ecoLabelsSchema,
+ hotelFacilityDetail: hotelFacilityDetailsSchema.default({}),
+ hotelInformation: hotelInformationsSchema.default({}),
+ interior: interiorSchema,
+ receptionHours: receptionHoursSchema,
+ yearBuilt: z.string(),
+})
diff --git a/server/routers/hotels/schemas/hotel/gallery.ts b/server/routers/hotels/schemas/hotel/gallery.ts
new file mode 100644
index 000000000..82c01c729
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/gallery.ts
@@ -0,0 +1,8 @@
+import { z } from "zod"
+
+import { imageSchema } from "../image"
+
+export const gallerySchema = z.object({
+ heroImages: z.array(imageSchema),
+ smallerImages: z.array(imageSchema),
+})
diff --git a/server/routers/hotels/schemas/hotel/healthFacilities.ts b/server/routers/hotels/schemas/hotel/healthFacilities.ts
new file mode 100644
index 000000000..37f767ab9
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/healthFacilities.ts
@@ -0,0 +1,41 @@
+import { z } from "zod"
+
+import { imageSchema } from "../image"
+
+const healthFacilitiesOpenHoursSchema = z.object({
+ alwaysOpen: z.boolean(),
+ closingTime: z.string().optional(),
+ isClosed: z.boolean(),
+ openingTime: z.string().optional(),
+ sortOrder: z.number().optional(),
+})
+
+export const healthFacilitySchema = z.object({
+ content: z.object({
+ images: z.array(imageSchema),
+ texts: z.object({
+ descriptions: z.object({
+ short: z.string(),
+ medium: z.string(),
+ }),
+ facilityInformation: z.string().optional(),
+ surroundingInformation: z.string().optional(),
+ }),
+ }),
+ details: z.array(
+ z.object({
+ name: z.string(),
+ type: z.string(),
+ value: z.string().optional(),
+ })
+ ),
+ openingDetails: z.object({
+ manualOpeningHours: z.string().optional(),
+ openingHours: z.object({
+ ordinary: healthFacilitiesOpenHoursSchema,
+ weekends: healthFacilitiesOpenHoursSchema,
+ }),
+ useManualOpeningHours: z.boolean(),
+ }),
+ type: z.string(),
+})
diff --git a/server/routers/hotels/schemas/hotel/include/include.ts b/server/routers/hotels/schemas/hotel/include/include.ts
new file mode 100644
index 000000000..6c5ba45e9
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/include/include.ts
@@ -0,0 +1,37 @@
+import { z } from "zod"
+
+import { citySchema } from "@/server/routers/hotels/schemas/city"
+import { nearbyHotelsSchema } from "@/server/routers/hotels/schemas/hotel/include/nearbyHotels"
+import { restaurantsSchema } from "@/server/routers/hotels/schemas/hotel/include/restaurants"
+import {
+ roomCategoriesSchema,
+ transformRoomCategories,
+} from "@/server/routers/hotels/schemas/hotel/include/roomCategories"
+import { additionalDataSchema, transformAdditionalData } from "../../additionalData"
+
+export const includeSchema = z
+ .discriminatedUnion("type", [
+ additionalDataSchema,
+ citySchema,
+ nearbyHotelsSchema,
+ restaurantsSchema,
+ roomCategoriesSchema,
+ ])
+ .transform((data) => {
+ switch (data.type) {
+ case "additionalData":
+ return transformAdditionalData(data)
+ case "cities":
+ case "hotels":
+ case "restaurants":
+ return {
+ ...data.attributes,
+ id: data.id,
+ type: data.type,
+ }
+ case "roomcategories":
+ return transformRoomCategories(data)
+ default:
+ return null
+ }
+ })
diff --git a/server/routers/hotels/schemas/hotel/include/nearbyHotels.ts b/server/routers/hotels/schemas/hotel/include/nearbyHotels.ts
new file mode 100644
index 000000000..175cd806a
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/include/nearbyHotels.ts
@@ -0,0 +1,41 @@
+import { z } from "zod"
+
+import { attributesSchema } from "@/server/routers/hotels/schemas/hotel"
+
+export const nearbyHotelsSchema = z.object({
+ attributes: z.lazy(() =>
+ z
+ .object({
+ displayWebPage: z
+ .object({
+ healthGym: z.boolean().default(false),
+ meetingRoom: z.boolean().default(false),
+ parking: z.boolean().default(false),
+ specialNeeds: z.boolean().default(false),
+ })
+ .default({
+ healthGym: false,
+ meetingRoom: false,
+ parking: false,
+ specialNeeds: false,
+ }),
+ })
+ .merge(
+ attributesSchema.pick({
+ address: true,
+ cityId: true,
+ cityName: true,
+ detailedFacilities: true,
+ hotelContent: true,
+ isActive: true,
+ isPublished: true,
+ location: true,
+ name: true,
+ operaId: true,
+ ratings: true,
+ })
+ )
+ ),
+ id: z.string(),
+ type: z.literal("hotels"),
+})
diff --git a/server/routers/hotels/schemas/hotel/include/restaurants.ts b/server/routers/hotels/schemas/hotel/include/restaurants.ts
new file mode 100644
index 000000000..849e9fd4c
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/include/restaurants.ts
@@ -0,0 +1,72 @@
+import { z } from "zod"
+
+import { imageSchema } from "@/server/routers/hotels/schemas/image"
+
+import { CurrencyEnum } from "@/types/enums/currency"
+
+const contentSchema = z.object({
+ images: z.array(imageSchema).default([]),
+ texts: z.object({
+ descriptions: z.object({
+ medium: z.string().default(""),
+ short: z.string().default(""),
+ }),
+ }),
+})
+
+const externalBreakfastSchema = z.object({
+ isAvailable: z.boolean().default(false),
+ localPriceForExternalGuests: z.object({
+ amount: z.number().default(0),
+ currency: z.nativeEnum(CurrencyEnum).default(CurrencyEnum.SEK),
+ }),
+})
+
+const menuItemSchema = z.object({
+ name: z.string(),
+ url: z.string().url(),
+})
+
+const daySchema = z.object({
+ alwaysOpen: z.boolean().default(false),
+ closingTime: z.string().default(""),
+ isClosed: z.boolean().default(false),
+ openingTime: z.string().default(""),
+ sortOrder: z.number().int().default(0),
+})
+
+const openingDetailsSchema = z.object({
+ alternateOpeningHours: z.object({
+ isActive: z.boolean().default(false),
+ }),
+ openingHours: z.object({
+ friday: daySchema,
+ isActive: z.boolean().default(false),
+ monday: daySchema,
+ name: z.string().default(""),
+ saturday: daySchema,
+ sunday: daySchema,
+ thursday: daySchema,
+ tuesday: daySchema,
+ wednesday: daySchema,
+ }),
+})
+
+export const restaurantsSchema = z.object({
+ attributes: z.object({
+ bookTableUrl: z.string().default(""),
+ content: contentSchema,
+ // When using .email().default("") is not sufficent
+ // so .optional also needs to be chained
+ email: z.string().email().optional(),
+ externalBreakfast: externalBreakfastSchema,
+ isPublished: z.boolean().default(false),
+ menus: z.array(menuItemSchema).default([]),
+ name: z.string().default(""),
+ openingDetails: z.array(openingDetailsSchema).default([]),
+ restaurantPage: z.boolean().default(false),
+ specialAlerts: z.array(z.object({})).default([]),
+ }),
+ id: z.string(),
+ type: z.literal("restaurants"),
+})
diff --git a/server/routers/hotels/schemas/hotel/include/roomCategories.ts b/server/routers/hotels/schemas/hotel/include/roomCategories.ts
new file mode 100644
index 000000000..42574835a
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/include/roomCategories.ts
@@ -0,0 +1,97 @@
+import { z } from "zod"
+
+import { imageMetaDataSchema, imageSizesSchema } from "../../image"
+
+const minMaxSchema = z.object({
+ max: z.number(),
+ min: z.number(),
+})
+
+const bedTypeSchema = z.object({
+ description: z.string().default(""),
+ type: z.string(),
+ widthRange: minMaxSchema,
+})
+
+const occupancySchema = z.object({
+ adults: z.number(),
+ children: z.number(),
+ total: z.number(),
+})
+
+const roomContentSchema = z.object({
+ images: z.array(
+ z.object({
+ imageSizes: imageSizesSchema,
+ metaData: imageMetaDataSchema,
+ })
+ ),
+ texts: z.object({
+ descriptions: z.object({
+ medium: z.string().optional(),
+ short: z.string().optional(),
+ }),
+ }),
+})
+
+const roomTypesSchema = z.object({
+ code: z.string(),
+ description: z.string(),
+ fixedExtraBed: bedTypeSchema,
+ isLackingCribs: z.boolean(),
+ isLackingExtraBeds: z.boolean(),
+ mainBed: bedTypeSchema,
+ name: z.string(),
+ occupancy: occupancySchema,
+ roomCount: z.number(),
+ roomSize: minMaxSchema,
+})
+
+const roomFacilitiesSchema = z.object({
+ availableInAllRooms: z.boolean(),
+ icon: z.string().optional(),
+ isUniqueSellingPoint: z.boolean(),
+ name: z.string(),
+ sortOrder: z.number(),
+})
+
+export const roomCategoriesSchema = z.object({
+ attributes: z.object({
+ content: roomContentSchema,
+ name: z.string(),
+ occupancy: minMaxSchema,
+ roomFacilities: z.array(roomFacilitiesSchema),
+ roomSize: minMaxSchema,
+ roomTypes: z.array(roomTypesSchema),
+ sortOrder: z.number(),
+ }),
+ id: z.string(),
+ type: z.literal("roomcategories"),
+})
+
+export function transformRoomCategories(
+ data: z.output
+) {
+ return {
+ descriptions: data.attributes.content.texts.descriptions,
+ id: data.id,
+ images: data.attributes.content.images,
+ name: data.attributes.name,
+ occupancy: data.attributes.occupancy,
+ roomFacilities: data.attributes.roomFacilities,
+ roomSize: data.attributes.roomSize,
+ roomTypes: data.attributes.roomTypes,
+ sortOrder: data.attributes.sortOrder,
+ type: data.type,
+ totalOccupancy:
+ data.attributes.occupancy.min === data.attributes.occupancy.max
+ ? {
+ max: data.attributes.occupancy.max,
+ range: `${data.attributes.occupancy.max}`,
+ }
+ : {
+ max: data.attributes.occupancy.max,
+ range: `${data.attributes.occupancy.min}-${data.attributes.occupancy.max}`,
+ },
+ }
+}
diff --git a/server/routers/hotels/schemas/hotel/location.ts b/server/routers/hotels/schemas/hotel/location.ts
new file mode 100644
index 000000000..d18f6d942
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/location.ts
@@ -0,0 +1,7 @@
+import { z } from "zod"
+
+export const locationSchema = z.object({
+ distanceToCentre: z.number(),
+ latitude: z.number(),
+ longitude: z.number(),
+})
diff --git a/server/routers/hotels/schemas/hotel/merchantInformation.ts b/server/routers/hotels/schemas/hotel/merchantInformation.ts
new file mode 100644
index 000000000..cf0daeb01
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/merchantInformation.ts
@@ -0,0 +1,21 @@
+import { z } from "zod"
+
+import type { PaymentMethodEnum } from "@/constants/booking"
+
+export const merchantInformationSchema = z.object({
+ alternatePaymentOptions: z
+ .record(z.string(), z.boolean())
+ .transform((val) => {
+ return Object.entries(val)
+ .filter(([_, enabled]) => enabled)
+ .map(([key]) => key)
+ .filter((key): key is PaymentMethodEnum => !!key)
+ }),
+ cards: z.record(z.string(), z.boolean()).transform((val) => {
+ return Object.entries(val)
+ .filter(([_, enabled]) => enabled)
+ .map(([key]) => key)
+ .filter((key): key is PaymentMethodEnum => !!key)
+ }),
+ webMerchantId: z.string(),
+})
diff --git a/server/routers/hotels/schemas/hotel/parking.ts b/server/routers/hotels/schemas/hotel/parking.ts
new file mode 100644
index 000000000..4e067bcc1
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/parking.ts
@@ -0,0 +1,41 @@
+import { z } from "zod"
+
+const periodSchema = z.object({
+ amount: z.number().optional(),
+ endTime: z.string().optional(),
+ period: z.string().optional(),
+ startTime: z.string().optional(),
+})
+
+const currencySchema = z
+ .object({
+ currency: z.string().optional(),
+ ordinary: z.array(periodSchema).optional(),
+ range: z
+ .object({
+ min: z.number().optional(),
+ max: z.number().optional(),
+ })
+ .optional(),
+ weekend: z.array(periodSchema).optional(),
+ })
+ .optional()
+
+const pricingSchema = z.object({
+ freeParking: z.boolean(),
+ localCurrency: currencySchema,
+ paymentType: z.string().optional(),
+ requestedCurrency: currencySchema,
+})
+
+export const parkingSchema = z.object({
+ address: z.string().optional(),
+ canMakeReservation: z.boolean(),
+ distanceToHotel: z.number().optional(),
+ externalParkingUrl: z.string().optional(),
+ name: z.string().optional(),
+ numberOfChargingSpaces: z.number().optional(),
+ numberOfParkingSpots: z.number().optional(),
+ pricing: pricingSchema,
+ type: z.string().optional(),
+})
diff --git a/server/routers/hotels/schemas/hotel/poi.ts b/server/routers/hotels/schemas/hotel/poi.ts
new file mode 100644
index 000000000..8ca47ad4f
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/poi.ts
@@ -0,0 +1,32 @@
+import { z } from "zod"
+
+import { getPoiGroupByCategoryName } from "../../utils"
+import { locationSchema } from "./location"
+
+export const pointOfInterestSchema = z
+ .object({
+ category: z.object({
+ name: z.string().optional(),
+ group: z.string().optional(),
+ }),
+ distance: z.number().optional(),
+ isHighlighted: z.boolean().optional(),
+ location: locationSchema.optional(),
+ name: z.string().optional(),
+ })
+ .transform((poi) => ({
+ categoryName: poi.category.name,
+ coordinates: {
+ lat: poi.location?.latitude ?? 0,
+ lng: poi.location?.longitude ?? 0,
+ },
+ distance: poi.distance,
+ group: getPoiGroupByCategoryName(poi.category.name),
+ name: poi.name,
+ }))
+
+export const pointOfInterestsSchema = z
+ .array(pointOfInterestSchema)
+ .transform((pois) =>
+ pois.sort((a, b) => (a.distance ?? 0) - (b.distance ?? 0))
+ )
diff --git a/server/routers/hotels/schemas/hotel/rating.ts b/server/routers/hotels/schemas/hotel/rating.ts
new file mode 100644
index 000000000..2f33fb850
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/rating.ts
@@ -0,0 +1,31 @@
+import { z } from "zod"
+
+const awardSchema = z.object({
+ displayName: z.string(),
+ images: z.object({
+ large: z.string(),
+ medium: z.string(),
+ small: z.string(),
+ }),
+})
+
+const reviewsSchema = z
+ .object({
+ widgetHtmlTagId: z.string(),
+ widgetScriptEmbedUrlIframe: z.string(),
+ widgetScriptEmbedUrlJavaScript: z.string(),
+ })
+ .optional()
+
+export const ratingsSchema = z
+ .object({
+ tripAdvisor: z.object({
+ awards: z.array(awardSchema),
+ numberOfReviews: z.number(),
+ rating: z.number(),
+ ratingImageUrl: z.string(),
+ reviews: reviewsSchema,
+ webUrl: z.string(),
+ }),
+ })
+ .optional()
diff --git a/server/routers/hotels/schemas/hotel/rewardNight.ts b/server/routers/hotels/schemas/hotel/rewardNight.ts
new file mode 100644
index 000000000..bd562062b
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/rewardNight.ts
@@ -0,0 +1,10 @@
+import { z } from "zod"
+
+export const rewardNightSchema = z.object({
+ campaign: z.object({
+ end: z.string(),
+ points: z.number(),
+ start: z.string(),
+ }),
+ points: z.number(),
+})
diff --git a/server/routers/hotels/schemas/hotel/socialMedia.ts b/server/routers/hotels/schemas/hotel/socialMedia.ts
new file mode 100644
index 000000000..66bd1e06e
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/socialMedia.ts
@@ -0,0 +1,6 @@
+import { z } from "zod"
+
+export const socialMediaSchema = z.object({
+ facebook: z.string().optional(),
+ instagram: z.string().optional(),
+})
diff --git a/server/routers/hotels/schemas/hotel/specialAlerts.ts b/server/routers/hotels/schemas/hotel/specialAlerts.ts
new file mode 100644
index 000000000..8687a81a6
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/specialAlerts.ts
@@ -0,0 +1,35 @@
+import { z } from "zod"
+
+import { dt } from "@/lib/dt"
+
+import { AlertTypeEnum } from "@/types/enums/alert"
+
+const specialAlertSchema = z.object({
+ description: z.string().optional(),
+ displayInBookingFlow: z.boolean(),
+ endDate: z.string().optional(),
+ startDate: z.string().optional(),
+ title: z.string().optional(),
+ type: z.string(),
+})
+
+export const specialAlertsSchema = z
+ .array(specialAlertSchema)
+ .transform((data) => {
+ const now = dt().utc().format("YYYY-MM-DD")
+ const filteredAlerts = data.filter((alert) => {
+ const shouldShowNow =
+ alert.startDate && alert.endDate
+ ? alert.startDate <= now && alert.endDate >= now
+ : true
+ const hasText = alert.description || alert.title
+ return shouldShowNow && hasText
+ })
+ return filteredAlerts.map((alert, idx) => ({
+ heading: alert.title || null,
+ id: `alert-${alert.type}-${idx}`,
+ text: alert.description || null,
+ type: AlertTypeEnum.Info,
+ }))
+ })
+ .default([])
diff --git a/server/routers/hotels/schemas/hotel/specialNeedGroups.ts b/server/routers/hotels/schemas/hotel/specialNeedGroups.ts
new file mode 100644
index 000000000..7f81aaaad
--- /dev/null
+++ b/server/routers/hotels/schemas/hotel/specialNeedGroups.ts
@@ -0,0 +1,11 @@
+import { z } from "zod"
+
+const specialNeedSchema = z.object({
+ details: z.string(),
+ name: z.string(),
+})
+
+export const specialNeedGroupSchema = z.object({
+ name: z.string(),
+ specialNeeds: z.array(specialNeedSchema),
+})
diff --git a/server/routers/hotels/schemas/hotelFilter.ts b/server/routers/hotels/schemas/hotelFilter.ts
new file mode 100644
index 000000000..5abd664df
--- /dev/null
+++ b/server/routers/hotels/schemas/hotelFilter.ts
@@ -0,0 +1,7 @@
+import { z } from "zod"
+
+export const hotelFilterSchema = z.object({
+ hotelFacilities: z.array(z.string()),
+ hotelSurroundings: z.array(z.string()),
+ roomFacilities: z.array(z.string()),
+})
diff --git a/server/routers/hotels/schemas/image.ts b/server/routers/hotels/schemas/image.ts
index 2674c1d89..de7cc4ded 100644
--- a/server/routers/hotels/schemas/image.ts
+++ b/server/routers/hotels/schemas/image.ts
@@ -1,17 +1,17 @@
import { z } from "zod"
-const imageSizesSchema = z.object({
- tiny: z.string(),
- small: z.string(),
- medium: z.string(),
+export const imageSizesSchema = z.object({
large: z.string(),
+ medium: z.string(),
+ small: z.string(),
+ tiny: z.string(),
})
-const imageMetaDataSchema = z.object({
- title: z.string(),
+export const imageMetaDataSchema = z.object({
altText: z.string(),
altText_En: z.string(),
copyRight: z.string(),
+ title: z.string(),
})
const DEFAULT_IMAGE_OBJ = {
diff --git a/server/routers/hotels/schemas/location/city.ts b/server/routers/hotels/schemas/location/city.ts
new file mode 100644
index 000000000..dc3404f31
--- /dev/null
+++ b/server/routers/hotels/schemas/location/city.ts
@@ -0,0 +1,12 @@
+import { z } from "zod"
+
+export const locationCitySchema = z.object({
+ attributes: z.object({
+ cityIdentifier: z.string().optional(),
+ keyWords: z.array(z.string()).optional(),
+ name: z.string().optional().default(""),
+ }),
+ country: z.string().optional().default(""),
+ id: z.string().optional().default(""),
+ type: z.literal("cities"),
+})
diff --git a/server/routers/hotels/schemas/location/hotel.ts b/server/routers/hotels/schemas/location/hotel.ts
new file mode 100644
index 000000000..01a407ab3
--- /dev/null
+++ b/server/routers/hotels/schemas/location/hotel.ts
@@ -0,0 +1,33 @@
+import { z } from "zod"
+
+export const locationHotelSchema = z.object({
+ attributes: z.object({
+ distanceToCentre: z.number().optional(),
+ images: z
+ .object({
+ large: z.string().optional(),
+ medium: z.string().optional(),
+ small: z.string().optional(),
+ tiny: z.string().optional(),
+ })
+ .optional(),
+ keyWords: z.array(z.string()).optional(),
+ name: z.string().optional().default(""),
+ operaId: z.string().optional(),
+ }),
+ id: z.string().optional().default(""),
+ relationships: z
+ .object({
+ city: z
+ .object({
+ links: z
+ .object({
+ related: z.string().optional(),
+ })
+ .optional(),
+ })
+ .optional(),
+ })
+ .optional(),
+ type: z.literal("hotels"),
+})
diff --git a/server/routers/hotels/schemas/meetingRoom.ts b/server/routers/hotels/schemas/meetingRoom.ts
new file mode 100644
index 000000000..01b30d316
--- /dev/null
+++ b/server/routers/hotels/schemas/meetingRoom.ts
@@ -0,0 +1,50 @@
+import { z } from "zod"
+
+import { imageSchema } from "./image"
+
+export const meetingRoomsSchema = z
+ .object({
+ data: z.array(
+ z.object({
+ attributes: z.object({
+ name: z.string(),
+ email: z.string().optional(),
+ phoneNumber: z.string(),
+ size: z.number(),
+ doorWidth: z.number(),
+ doorHeight: z.number(),
+ length: z.number(),
+ width: z.number(),
+ height: z.number(),
+ floorNumber: z.number(),
+ content: z.object({
+ images: z.array(imageSchema),
+ texts: z.object({
+ facilityInformation: z.string().optional(),
+ surroundingInformation: z.string().optional(),
+ descriptions: z.object({
+ short: z.string().optional(),
+ medium: z.string().optional(),
+ }),
+ meetingDescription: z
+ .object({
+ short: z.string().optional(),
+ medium: z.string().optional(),
+ })
+ .optional(),
+ }),
+ }),
+ seatings: z.array(
+ z.object({
+ type: z.string(),
+ capacity: z.number(),
+ })
+ ),
+ lighting: z.string(),
+ sortOrder: z.number().optional(),
+ }),
+ id: z.string(),
+ type: z.string(),
+ })
+ ),
+ })
\ No newline at end of file
diff --git a/server/routers/hotels/schemas/packages.ts b/server/routers/hotels/schemas/packages.ts
index 035882fc4..0f32f11a5 100644
--- a/server/routers/hotels/schemas/packages.ts
+++ b/server/routers/hotels/schemas/packages.ts
@@ -1,16 +1,9 @@
import { z } from "zod"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
+import { PackageTypeEnum } from "@/types/enums/packages"
-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([]),
-})
-
+// TODO: Remove optional and default when the API change has been deployed
export const packagePriceSchema = z
.object({
currency: z.string().default("N/A"),
@@ -22,40 +15,27 @@ export const packagePriceSchema = z
currency: "N/A",
price: "0",
totalPrice: "0",
- }) // TODO: Remove optional and default when the API change has been deployed
+ })
-export const packagesSchema = z.object({
+const inventorySchema = z.object({
+ date: z.string(),
+ total: z.number(),
+ available: z.number(),
+})
+
+export const packageSchema = z.object({
code: z.nativeEnum(RoomPackageCodeEnum),
description: z.string(),
+ inventories: z.array(inventorySchema),
+ itemCode: z.string().default(""),
+ localPrice: packagePriceSchema,
+ requestedPrice: packagePriceSchema,
+})
+
+export const breakfastPackageSchema = z.object({
+ code: z.string(),
+ description: z.string(),
localPrice: packagePriceSchema,
requestedPrice: packagePriceSchema,
- inventories: z.array(
- z.object({
- date: z.string(),
- total: z.number(),
- available: z.number(),
- })
- ),
+ packageType: z.literal(PackageTypeEnum.BreakfastAdult),
})
-
-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)
diff --git a/server/routers/hotels/schemas/productTypePrice.ts b/server/routers/hotels/schemas/productTypePrice.ts
new file mode 100644
index 000000000..2d8e206db
--- /dev/null
+++ b/server/routers/hotels/schemas/productTypePrice.ts
@@ -0,0 +1,16 @@
+import { z } from "zod"
+
+import { CurrencyEnum } from "@/types/enums/currency"
+
+export const priceSchema = z.object({
+ currency: z.nativeEnum(CurrencyEnum),
+ pricePerNight: z.coerce.number(),
+ pricePerStay: z.coerce.number(),
+})
+
+export const productTypePriceSchema = z.object({
+ localPrice: priceSchema,
+ rateCode: z.string(),
+ rateType: z.string().optional(),
+ requestedPrice: priceSchema.optional(),
+})
diff --git a/server/routers/hotels/schemas/rate.ts b/server/routers/hotels/schemas/rate.ts
new file mode 100644
index 000000000..f82345261
--- /dev/null
+++ b/server/routers/hotels/schemas/rate.ts
@@ -0,0 +1,21 @@
+import { z } from "zod"
+
+const flexibilityPrice = z.object({
+ member: z.number(),
+ standard: z.number(),
+})
+
+export const rateSchema = z.object({
+ breakfastIncluded: z.boolean(),
+ description: z.string(),
+ id: z.number(),
+ imageSrc: z.string(),
+ name: z.string(),
+ prices: z.object({
+ currency: z.string(),
+ freeCancellation: flexibilityPrice,
+ freeRebooking: flexibilityPrice,
+ nonRefundable: flexibilityPrice,
+ }),
+ size: z.string(),
+})
diff --git a/server/routers/hotels/schemas/relationships.ts b/server/routers/hotels/schemas/relationships.ts
new file mode 100644
index 000000000..9c2426c2c
--- /dev/null
+++ b/server/routers/hotels/schemas/relationships.ts
@@ -0,0 +1,10 @@
+import { z } from "zod"
+
+export const relationshipsSchema = z.object({
+ links: z.array(
+ z.object({
+ type: z.string(),
+ url: z.string().url(),
+ })
+ ),
+})
diff --git a/server/routers/hotels/schemas/roomAvailability/configuration.ts b/server/routers/hotels/schemas/roomAvailability/configuration.ts
new file mode 100644
index 000000000..4d433917b
--- /dev/null
+++ b/server/routers/hotels/schemas/roomAvailability/configuration.ts
@@ -0,0 +1,23 @@
+import { z } from "zod"
+
+import { productSchema } from "./product"
+
+import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
+
+export const roomConfigurationSchema = z.object({
+ features: z.array(
+ z.object({
+ inventory: z.number(),
+ code: z.enum([
+ RoomPackageCodeEnum.PET_ROOM,
+ RoomPackageCodeEnum.ALLERGY_ROOM,
+ RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
+ ]),
+ })
+ ),
+ products: z.array(productSchema),
+ roomsLeft: z.number(),
+ roomType: z.string(),
+ roomTypeCode: z.string(),
+ status: z.string(),
+})
diff --git a/server/routers/hotels/schemas/roomAvailability/product.ts b/server/routers/hotels/schemas/roomAvailability/product.ts
new file mode 100644
index 000000000..94ff02ab9
--- /dev/null
+++ b/server/routers/hotels/schemas/roomAvailability/product.ts
@@ -0,0 +1,21 @@
+import { z } from "zod"
+
+import { productTypePriceSchema } from "../productTypePrice"
+
+import { CurrencyEnum } from "@/types/enums/currency"
+
+export const productSchema = z.object({
+ productType: z.object({
+ member: productTypePriceSchema.optional(),
+ public: productTypePriceSchema.default({
+ localPrice: {
+ currency: CurrencyEnum.SEK,
+ pricePerNight: 0,
+ pricePerStay: 0,
+ },
+ rateCode: "",
+ rateType: "",
+ requestedPrice: undefined,
+ }),
+ }),
+})
diff --git a/server/routers/hotels/schemas/roomAvailability/rateDefinition.ts b/server/routers/hotels/schemas/roomAvailability/rateDefinition.ts
new file mode 100644
index 000000000..815c78447
--- /dev/null
+++ b/server/routers/hotels/schemas/roomAvailability/rateDefinition.ts
@@ -0,0 +1,12 @@
+import { z } from "zod"
+
+export const rateDefinitionSchema = z.object({
+ breakfastIncluded: z.boolean(),
+ cancellationRule: z.string(),
+ cancellationText: z.string(),
+ generalTerms: z.array(z.string()),
+ mustBeGuaranteed: z.boolean(),
+ rateCode: z.string(),
+ rateType: z.string().optional(),
+ title: z.string(),
+})
diff --git a/server/routers/hotels/utils.ts b/server/routers/hotels/utils.ts
index abfb53bf5..eae6946f6 100644
--- a/server/routers/hotels/utils.ts
+++ b/server/routers/hotels/utils.ts
@@ -4,25 +4,24 @@ import { unstable_cache } from "next/cache"
import * as api from "@/lib/api"
import {
- apiCitiesByCountrySchema,
- apiCitySchema,
- apiCountriesSchema,
- apiLocationsSchema,
- type CitiesGroupedByCountry,
+ citiesByCountrySchema,
+ citiesSchema,
+ countriesSchema,
getHotelIdsByCityIdSchema,
+ locationsSchema,
} from "./output"
-import {
- getHotelIdsCounter,
- getHotelIdsFailCounter,
- getHotelIdsSuccessCounter,
-} from "./telemetry"
-import type { Country } from "@/types/enums/country"
+import { PointOfInterestGroupEnum } from "@/types/enums/pointOfInterest"
import type { RequestOptionsWithOutBody } from "@/types/fetch"
-import { PointOfInterestGroupEnum } from "@/types/hotel"
-import type { HotelLocation } from "@/types/trpc/routers/hotel/locations"
+import type {
+ CitiesGroupedByCountry,
+ Countries,
+ HotelLocation,
+} from "@/types/trpc/routers/hotel/locations"
import type { Lang } from "@/constants/languages"
import type { Endpoint } from "@/lib/api/endpoints"
+import { Country } from "@/types/enums/country"
+import { metrics } from "./metrics"
export function getPoiGroupByCategoryName(category: string | undefined) {
if (!category) return PointOfInterestGroupEnum.LOCATION
@@ -75,7 +74,7 @@ export async function getCity(
}
const cityJson = await cityResponse.json()
- const city = apiCitySchema.safeParse(cityJson)
+ const city = citiesSchema.safeParse(cityJson)
if (!city.success) {
console.info(`Validation of city failed`)
console.info(`cityUrl: ${locationCityUrl}`)
@@ -108,7 +107,7 @@ export async function getCountries(
}
const countriesJson = await countryResponse.json()
- const countries = apiCountriesSchema.safeParse(countriesJson)
+ const countries = countriesSchema.safeParse(countriesJson)
if (!countries.success) {
console.info(`Validation for countries failed`)
console.error(countries.error)
@@ -149,8 +148,7 @@ export async function getCitiesByCountry(
}
const countryJson = await countryResponse.json()
- const citiesByCountry =
- apiCitiesByCountrySchema.safeParse(countryJson)
+ const citiesByCountry = citiesByCountrySchema.safeParse(countryJson)
if (!citiesByCountry.success) {
console.info(`Failed to validate Cities by Country payload`)
console.error(citiesByCountry.error)
@@ -200,7 +198,7 @@ export async function getLocations(
}
const apiJson = await apiResponse.json()
- const verifiedLocations = apiLocationsSchema.safeParse(apiJson)
+ const verifiedLocations = locationsSchema.safeParse(apiJson)
if (!verifiedLocations.success) {
console.info(`Locations Verification Failed`)
console.error(verifiedLocations.error)
@@ -273,7 +271,7 @@ export async function getHotelIdsByCityId(
) {
return unstable_cache(
async function (params: URLSearchParams) {
- getHotelIdsCounter.add(1, { params: params.toString() })
+ metrics.hotelIds.counter.add(1, { params: params.toString() })
console.info(
"api.hotel.hotel-ids start",
JSON.stringify({ params: params.toString() })
@@ -286,7 +284,7 @@ export async function getHotelIdsByCityId(
if (!apiResponse.ok) {
const responseMessage = await apiResponse.text()
- getHotelIdsFailCounter.add(1, {
+ metrics.hotelIds.fail.add(1, {
params: params.toString(),
error_type: "http_error",
error: responseMessage,
@@ -309,7 +307,7 @@ export async function getHotelIdsByCityId(
const apiJson = await apiResponse.json()
const validatedHotelIds = getHotelIdsByCityIdSchema.safeParse(apiJson)
if (!validatedHotelIds.success) {
- getHotelIdsFailCounter.add(1, {
+ metrics.hotelIds.fail.add(1, {
params: params.toString(),
error_type: "validation_error",
error: JSON.stringify(validatedHotelIds.error),
@@ -324,7 +322,7 @@ export async function getHotelIdsByCityId(
return null
}
- getHotelIdsSuccessCounter.add(1, { cityId })
+ metrics.hotelIds.success.add(1, { cityId })
console.info(
"api.hotel.hotel-ids success",
JSON.stringify({ params: params.toString() })
@@ -344,7 +342,7 @@ export async function getHotelIdsByCountry(
) {
return unstable_cache(
async function (params: URLSearchParams) {
- getHotelIdsCounter.add(1, { country })
+ metrics.hotelIds.counter.add(1, { country })
console.info(
"api.hotel.hotel-ids start",
JSON.stringify({ query: { country } })
@@ -357,7 +355,7 @@ export async function getHotelIdsByCountry(
if (!apiResponse.ok) {
const responseMessage = await apiResponse.text()
- getHotelIdsFailCounter.add(1, {
+ metrics.hotelIds.fail.add(1, {
country,
error_type: "http_error",
error: responseMessage,
@@ -380,7 +378,7 @@ export async function getHotelIdsByCountry(
const apiJson = await apiResponse.json()
const validatedHotelIds = getHotelIdsByCityIdSchema.safeParse(apiJson)
if (!validatedHotelIds.success) {
- getHotelIdsFailCounter.add(1, {
+ metrics.hotelIds.fail.add(1, {
country,
error_type: "validation_error",
error: JSON.stringify(validatedHotelIds.error),
@@ -395,7 +393,7 @@ export async function getHotelIdsByCountry(
return null
}
- getHotelIdsSuccessCounter.add(1, { country })
+ metrics.hotelIds.success.add(1, { country })
console.info(
"api.hotel.hotel-ids success",
JSON.stringify({ query: { country } })
diff --git a/types/components/hotelPage/meetingRooms.ts b/types/components/hotelPage/meetingRooms.ts
index c49c3b536..c73b68a79 100644
--- a/types/components/hotelPage/meetingRooms.ts
+++ b/types/components/hotelPage/meetingRooms.ts
@@ -1,6 +1,6 @@
+import { meetingRoomsSchema } from "@/server/routers/hotels/schemas/meetingRoom"
+
import type { z } from "zod"
-import type { getMeetingRoomsSchema } from "@/server/routers/hotels/output"
-
-export type MeetingRoomData = z.infer
+export type MeetingRoomData = z.output
export type MeetingRooms = MeetingRoomData["data"]
diff --git a/types/components/hotelPage/room.ts b/types/components/hotelPage/room.ts
index e66ff1b56..048958f43 100644
--- a/types/components/hotelPage/room.ts
+++ b/types/components/hotelPage/room.ts
@@ -1,10 +1,10 @@
-import type { RoomData } from "@/types/hotel"
+import type { Room } from "@/types/hotel"
export interface RoomCardProps {
- room: RoomData
+ room: Room
}
export type RoomsProps = {
- rooms: RoomData[]
preamble?: string
+ rooms: Room[]
}
diff --git a/types/components/hotelPage/sidepeek/amenities.ts b/types/components/hotelPage/sidepeek/amenities.ts
index 0ae272bef..e85b9ad19 100644
--- a/types/components/hotelPage/sidepeek/amenities.ts
+++ b/types/components/hotelPage/sidepeek/amenities.ts
@@ -1,6 +1,6 @@
import type {
Hotel,
- RestaurantData,
+ Restaurant,
RestaurantOpeningHours,
} from "@/types/hotel"
import type { ParkingAmenityProps } from "./parking"
@@ -10,7 +10,7 @@ export type AmenitiesSidePeekProps = {
parking: ParkingAmenityProps
checkInInformation: Hotel["hotelFacts"]["checkin"]
accessibility: Hotel["hotelFacts"]["hotelInformation"]["accessibility"]
- restaurants: RestaurantData[]
+ restaurants: Restaurant[]
}
export type FilteredAmenitiesProps = {
diff --git a/types/components/hotelPage/sidepeek/parking.ts b/types/components/hotelPage/sidepeek/parking.ts
index 08981c286..216a48463 100644
--- a/types/components/hotelPage/sidepeek/parking.ts
+++ b/types/components/hotelPage/sidepeek/parking.ts
@@ -1,27 +1,29 @@
-import type { Hotel } from "@/types/hotel"
+import type { Hotel, Parking } from "@/types/hotel"
export enum Periods {
+ allDay = "AllDay",
day = "Day",
night = "Night",
- allDay = "AllDay",
}
export type ParkingAmenityProps = {
+ hasExtraParkingPage: boolean
parking: Hotel["parking"]
parkingElevatorPitch: string
- hasExtraParkingPage: boolean
}
-export type ParkingListProps = {
- numberOfChargingSpaces: Hotel["parking"][number]["numberOfChargingSpaces"]
- canMakeReservation: Hotel["parking"][number]["canMakeReservation"]
- numberOfParkingSpots: Hotel["parking"][number]["numberOfParkingSpots"]
- distanceToHotel: Hotel["parking"][number]["distanceToHotel"]
- address: Hotel["parking"][number]["address"]
-}
+export interface ParkingListProps
+ extends Pick<
+ Parking,
+ | "address"
+ | "canMakeReservation"
+ | "distanceToHotel"
+ | "numberOfChargingSpaces"
+ | "numberOfParkingSpots"
+ > { }
-export type ParkingPricesProps = {
- pricing: Hotel["parking"][number]["pricing"]["localCurrency"]["ordinary"]
- currency: Hotel["parking"][number]["pricing"]["localCurrency"]["currency"]
- freeParking: Hotel["parking"][number]["pricing"]["freeParking"]
+export interface ParkingPricesProps
+ extends Pick,
+ Pick, "currency"> {
+ pricing: NonNullable["ordinary"]
}
diff --git a/types/components/hotelPage/sidepeek/restaurantBar.ts b/types/components/hotelPage/sidepeek/restaurantBar.ts
index bbd0ba91a..31ab6b4b7 100644
--- a/types/components/hotelPage/sidepeek/restaurantBar.ts
+++ b/types/components/hotelPage/sidepeek/restaurantBar.ts
@@ -1,9 +1,9 @@
-import type { RestaurantData } from "@/types/hotel"
+import type { Restaurant } from "@/types/hotel"
export interface RestaurantBarSidePeekProps {
- restaurants: RestaurantData[]
+ restaurants: Restaurant[]
}
export interface RestaurantBarItemProps {
- restaurant: RestaurantData
+ restaurant: Restaurant
}
diff --git a/types/components/hotelPage/sidepeek/room.ts b/types/components/hotelPage/sidepeek/room.ts
index 7ff699273..dcc442b08 100644
--- a/types/components/hotelPage/sidepeek/room.ts
+++ b/types/components/hotelPage/sidepeek/room.ts
@@ -1,5 +1,5 @@
-import type { RoomData } from "@/types/hotel"
+import type { Room } from "@/types/hotel"
export interface RoomSidePeekProps {
- room: RoomData
+ room: Room
}
diff --git a/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts b/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts
index 73007a82d..88fb770f3 100644
--- a/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts
+++ b/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts
@@ -4,4 +4,4 @@ export interface BookingConfirmationProps {
confirmationNumber: string
}
-export interface ConfirmationProps extends BookingConfirmation {}
+export interface ConfirmationProps extends BookingConfirmation { }
diff --git a/types/components/hotelReservation/enterDetails/breakfast.ts b/types/components/hotelReservation/enterDetails/breakfast.ts
index acb936abd..e6e2a4e0f 100644
--- a/types/components/hotelReservation/enterDetails/breakfast.ts
+++ b/types/components/hotelReservation/enterDetails/breakfast.ts
@@ -1,19 +1,17 @@
-import { type z } from "zod"
+import type { z } from "zod"
import type { breakfastFormSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema"
-import type {
- breakfastPackageSchema,
- breakfastPackagesSchema,
-} from "@/server/routers/hotels/output"
+import type { breakfastPackagesSchema } from "@/server/routers/hotels/output"
+import type { breakfastPackageSchema } from "@/server/routers/hotels/schemas/packages"
export interface BreakfastFormSchema
- extends z.output {}
+ extends z.output { }
export interface BreakfastPackages
- extends z.output {}
+ extends z.output { }
export interface BreakfastPackage
- extends z.output {}
+ extends z.output { }
export interface BreakfastProps {
packages: BreakfastPackages
diff --git a/types/components/hotelReservation/enterDetails/hotelHeader.ts b/types/components/hotelReservation/enterDetails/hotelHeader.ts
index afb69676d..f8a15e87c 100644
--- a/types/components/hotelReservation/enterDetails/hotelHeader.ts
+++ b/types/components/hotelReservation/enterDetails/hotelHeader.ts
@@ -1,7 +1,5 @@
-import type { RouterOutput } from "@/lib/trpc/client"
-
-type HotelDataGet = RouterOutput["hotel"]["hotelData"]["get"]
+import type { HotelData } from "@/types/hotel"
export interface HotelHeaderProps {
- hotelData: NonNullable
+ hotelData: HotelData
}
diff --git a/types/components/hotelReservation/enterDetails/room.ts b/types/components/hotelReservation/enterDetails/room.ts
index 60e5e2646..f96146620 100644
--- a/types/components/hotelReservation/enterDetails/room.ts
+++ b/types/components/hotelReservation/enterDetails/room.ts
@@ -1,7 +1,7 @@
-import { RoomConfiguration } from "@/server/routers/hotels/output"
+import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
export interface SelectedRoomProps {
hotelId: string
- room: RoomConfiguration
rateDescription: string
+ room: RoomConfiguration
}
diff --git a/types/components/hotelReservation/enterDetails/summary.ts b/types/components/hotelReservation/enterDetails/summary.ts
index 54d896d92..62372f226 100644
--- a/types/components/hotelReservation/enterDetails/summary.ts
+++ b/types/components/hotelReservation/enterDetails/summary.ts
@@ -1,5 +1,5 @@
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
-import type { RoomAvailability } from "@/types/trpc/routers/hotel/availability"
+import type { RoomAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
export interface ClientSummaryProps
extends Pick<
diff --git a/types/components/hotelReservation/selectHotel/hotePriceListProps.ts b/types/components/hotelReservation/selectHotel/hotePriceListProps.ts
index 4144abf45..769edf97f 100644
--- a/types/components/hotelReservation/selectHotel/hotePriceListProps.ts
+++ b/types/components/hotelReservation/selectHotel/hotePriceListProps.ts
@@ -1,6 +1,6 @@
-import type { ProductType } from "@/server/routers/hotels/output"
+import type { ProductType } from "@/types/trpc/routers/hotel/availability"
export type HotelPriceListProps = {
- price: ProductType
hotelId: string
+ price: ProductType
}
diff --git a/types/components/hotelReservation/selectHotel/hotelCardListingProps.ts b/types/components/hotelReservation/selectHotel/hotelCardListingProps.ts
index d8404024b..24336f390 100644
--- a/types/components/hotelReservation/selectHotel/hotelCardListingProps.ts
+++ b/types/components/hotelReservation/selectHotel/hotelCardListingProps.ts
@@ -1,21 +1,21 @@
import type { Hotel } from "@/types/hotel"
-import type { ProductType } from "@/server/routers/hotels/output"
+import type { ProductType } from "@/types/trpc/routers/hotel/availability"
export enum HotelCardListingTypeEnum {
MapListing = "mapListing",
PageListing = "pageListing",
}
-export type HotelCardListingProps = {
- hotelData: HotelData[]
- type?: HotelCardListingTypeEnum
-}
-
export type HotelData = {
hotelData: Hotel
price: ProductType
}
+export type HotelCardListingProps = {
+ hotelData: HotelData[]
+ type?: HotelCardListingTypeEnum
+}
+
export interface NullableHotelData extends Omit {
hotelData: HotelData["hotelData"] | null
}
diff --git a/types/components/hotelReservation/selectHotel/priceCardProps.ts b/types/components/hotelReservation/selectHotel/priceCardProps.ts
index a56a67d0f..d82b5d31f 100644
--- a/types/components/hotelReservation/selectHotel/priceCardProps.ts
+++ b/types/components/hotelReservation/selectHotel/priceCardProps.ts
@@ -1,4 +1,4 @@
-import { ProductTypePrices } from "@/server/routers/hotels/output"
+import type { ProductTypePrices } from "@/types/trpc/routers/hotel/availability"
export type PriceCardProps = {
productTypePrices: ProductTypePrices
diff --git a/types/components/hotelReservation/selectHotel/selectHotel.ts b/types/components/hotelReservation/selectHotel/selectHotel.ts
index 8beba9035..253f64408 100644
--- a/types/components/hotelReservation/selectHotel/selectHotel.ts
+++ b/types/components/hotelReservation/selectHotel/selectHotel.ts
@@ -1,10 +1,9 @@
-import type { CheckInData, Hotel, ParkingData } from "@/types/hotel"
-import type { HotelLocation } from "@/types/trpc/routers/hotel/locations"
-import type { Lang } from "@/constants/languages"
import type {
AlternativeHotelsSearchParams,
SelectHotelSearchParams,
} from "./selectHotelSearchParams"
+import type { CheckInData, Hotel, Parking } from "@/types/hotel"
+import type { Lang } from "@/constants/languages"
export enum AvailabilityEnum {
Available = "Available",
@@ -23,7 +22,7 @@ export interface ContactProps {
}
export interface ParkingProps {
- parking: ParkingData[]
+ parking: Parking[]
}
export interface AccessibilityProps {
diff --git a/types/components/hotelReservation/selectRate/flexibilityOption.ts b/types/components/hotelReservation/selectRate/flexibilityOption.ts
index fdeef83f1..e5e06de47 100644
--- a/types/components/hotelReservation/selectRate/flexibilityOption.ts
+++ b/types/components/hotelReservation/selectRate/flexibilityOption.ts
@@ -1,11 +1,13 @@
import type { z } from "zod"
import type {
- priceSchema,
Product,
- productTypePriceSchema,
RoomConfiguration,
-} from "@/server/routers/hotels/output"
+} from "@/types/trpc/routers/hotel/roomAvailability"
+import type {
+ priceSchema,
+ productTypePriceSchema,
+} from "@/server/routers/hotels/schemas/productTypePrice"
import type { RoomPackage } from "./roomFilter"
type ProductPrice = z.output
diff --git a/types/components/hotelReservation/selectRate/hotelInfoCard.ts b/types/components/hotelReservation/selectRate/hotelInfoCard.ts
new file mode 100644
index 000000000..8fc9a8c2d
--- /dev/null
+++ b/types/components/hotelReservation/selectRate/hotelInfoCard.ts
@@ -0,0 +1,11 @@
+import type { Lang } from "@/constants/languages"
+import type { Child } from "./selectRate"
+
+export interface HotelInfoCardProps {
+ adultCount: number
+ childArray?: Child[]
+ fromDate: Date
+ hotelId: number
+ lang: Lang
+ toDate: Date
+}
diff --git a/types/components/hotelReservation/selectRate/rateSummary.ts b/types/components/hotelReservation/selectRate/rateSummary.ts
index 28fed15d7..470f2bf3d 100644
--- a/types/components/hotelReservation/selectRate/rateSummary.ts
+++ b/types/components/hotelReservation/selectRate/rateSummary.ts
@@ -1,9 +1,9 @@
-import type { RoomsAvailability } from "@/server/routers/hotels/output"
-import type { RoomPackageData } from "./roomFilter"
+import type { RoomsAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
+import type { RoomPackages } from "./roomFilter"
import type { Rate } from "./selectRate"
export interface RateSummaryProps {
isUserLoggedIn: boolean
- packages: RoomPackageData | undefined
+ packages: RoomPackages | undefined
roomsAvailability: RoomsAvailability
}
diff --git a/types/components/hotelReservation/selectRate/roomCard.ts b/types/components/hotelReservation/selectRate/roomCard.ts
index 5e5d58eae..1b3366d52 100644
--- a/types/components/hotelReservation/selectRate/roomCard.ts
+++ b/types/components/hotelReservation/selectRate/roomCard.ts
@@ -1,23 +1,25 @@
import type { z } from "zod"
-import type { RoomData } from "@/types/hotel"
+import type { Room } from "@/types/hotel"
import type {
- packagePriceSchema,
RateDefinition,
RoomConfiguration,
-} from "@/server/routers/hotels/output"
+} from "@/types/trpc/routers/hotel/roomAvailability"
+import type { packagePriceSchema } from "@/server/routers/hotels/schemas/packages"
import type { RoomPriceSchema } from "./flexibilityOption"
-import type { RoomPackageCodes, RoomPackageData } from "./roomFilter"
+import type { RoomPackageCodes, RoomPackages } from "./roomFilter"
+import type { RateCode } from "./selectRate"
export type RoomCardProps = {
hotelId: string
hotelType: string | undefined
roomConfiguration: RoomConfiguration
rateDefinitions: RateDefinition[]
- roomCategories: RoomData[]
+ roomCategories: Room[]
selectedPackages: RoomPackageCodes[]
- packages: RoomPackageData | undefined
roomListIndex: number
+ packages: RoomPackages | undefined
+ handleSelectRate: React.Dispatch>
}
type RoomPackagePriceSchema = z.output
diff --git a/types/components/hotelReservation/selectRate/roomFilter.ts b/types/components/hotelReservation/selectRate/roomFilter.ts
index c6698df9b..2ac6d743d 100644
--- a/types/components/hotelReservation/selectRate/roomFilter.ts
+++ b/types/components/hotelReservation/selectRate/roomFilter.ts
@@ -1,6 +1,6 @@
import type { z } from "zod"
-import type { packagesSchema } from "@/server/routers/hotels/output"
+import type { packageSchema } from "@/server/routers/hotels/schemas/packages"
export enum RoomPackageCodeEnum {
PET_ROOM = "PETR",
@@ -24,7 +24,6 @@ export interface RoomFilterProps {
roomListIndex: number
}
-export type RoomPackage = z.output
-export interface RoomPackageData extends Array {}
-
+export type RoomPackage = z.output
export type RoomPackageCodes = RoomPackage["code"]
+export type RoomPackages = RoomPackage[]
diff --git a/types/components/hotelReservation/selectRate/roomSelection.ts b/types/components/hotelReservation/selectRate/roomSelection.ts
index 014f3a731..cdae49d50 100644
--- a/types/components/hotelReservation/selectRate/roomSelection.ts
+++ b/types/components/hotelReservation/selectRate/roomSelection.ts
@@ -1,31 +1,32 @@
-import type { RoomData } from "@/types/hotel"
-import type { RoomsAvailability } from "@/server/routers/hotels/output"
import type {
DefaultFilterOptions,
RoomPackage,
RoomPackageCodes,
- RoomPackageData,
+ RoomPackages,
} from "./roomFilter"
+import type { Room } from "@/types/hotel"
+import type { RoomsAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
+
export interface RoomTypeListProps {
roomsAvailability: RoomsAvailability
- roomCategories: RoomData[]
- availablePackages: RoomPackageData | undefined
+ roomCategories: Room[]
+ availablePackages: RoomPackage | undefined
selectedPackages: RoomPackageCodes[]
hotelType: string | undefined
roomListIndex: number
}
export interface SelectRateProps {
- roomsAvailability: RoomsAvailability
- roomCategories: RoomData[]
- availablePackages: RoomPackageData
+ availablePackages: RoomPackages
hotelType: string | undefined
isUserLoggedIn: boolean
+ roomsAvailability: RoomsAvailability
+ roomCategories: Room[]
}
export interface RoomSelectionPanelProps {
- roomCategories: RoomData[]
+ roomCategories: Room[]
availablePackages: RoomPackage[]
selectedPackages: RoomPackageCodes[]
hotelType: string | undefined
diff --git a/types/components/hotelReservation/selectRate/roomsContainer.ts b/types/components/hotelReservation/selectRate/roomsContainer.ts
new file mode 100644
index 000000000..74f52aa2d
--- /dev/null
+++ b/types/components/hotelReservation/selectRate/roomsContainer.ts
@@ -0,0 +1,11 @@
+import type { Lang } from "@/constants/languages"
+import type { Child } from "./selectRate"
+
+export interface RoomsContainerProps {
+ adultCount: number
+ childArray?: Child[]
+ fromDate: Date
+ hotelId: number
+ lang: Lang
+ toDate: Date
+}
diff --git a/types/components/hotelReservation/selectRate/selectRate.ts b/types/components/hotelReservation/selectRate/selectRate.ts
index a993bed94..8ca10f6b1 100644
--- a/types/components/hotelReservation/selectRate/selectRate.ts
+++ b/types/components/hotelReservation/selectRate/selectRate.ts
@@ -1,4 +1,7 @@
-import type { Product, RoomConfiguration } from "@/server/routers/hotels/output"
+import type {
+ Product,
+ RoomConfiguration,
+} from "@/types/trpc/routers/hotel/roomAvailability"
import type { ChildBedMapEnum } from "../../bookingWidget/enums"
import type { RoomPackageCodeEnum } from "./roomFilter"
diff --git a/types/components/hotelReservation/sidePeek.ts b/types/components/hotelReservation/sidePeek.ts
index adeb99ec5..8502d6b23 100644
--- a/types/components/hotelReservation/sidePeek.ts
+++ b/types/components/hotelReservation/sidePeek.ts
@@ -1,10 +1,10 @@
-import { Hotel } from "@/types/hotel"
+import { HotelData } from "@/types/hotel"
export enum SidePeekEnum {
hotelDetails = "hotel-detail-side-peek",
roomDetails = "room-detail-side-peek",
}
-export type SidePeekProps = {
- hotel: Hotel
+export type HotelReservationSidePeekProps = {
+ hotel: HotelData | null
}
diff --git a/types/components/hotelReservation/summary.ts b/types/components/hotelReservation/summary.ts
index c60194d71..0edd00d72 100644
--- a/types/components/hotelReservation/summary.ts
+++ b/types/components/hotelReservation/summary.ts
@@ -5,11 +5,11 @@ import type {
Price,
RoomPrice,
} from "@/types/stores/enter-details"
-import type { RoomAvailability } from "@/types/trpc/routers/hotel/availability"
import type { BedTypeSchema } from "./enterDetails/bedType"
import type { BreakfastPackage } from "./enterDetails/breakfast"
import type { DetailsSchema } from "./enterDetails/details"
import type { Child, SelectRateSearchParams } from "./selectRate/selectRate"
+import type { RoomAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
export type RoomsData = Pick &
Pick &
@@ -21,7 +21,7 @@ export type RoomsData = Pick &
export interface SummaryProps
extends Pick,
- Pick {
+ Pick {
isMember: boolean
breakfastIncluded: boolean
}
diff --git a/types/components/maps/poiMarker.ts b/types/components/maps/poiMarker.ts
index abab6fc9d..3d33caeb4 100644
--- a/types/components/maps/poiMarker.ts
+++ b/types/components/maps/poiMarker.ts
@@ -1,8 +1,7 @@
-import { poiVariants } from "@/components/Maps/Markers/Poi/variants"
-
import type { VariantProps } from "class-variance-authority"
-import type { PointOfInterestGroupEnum } from "@/types/hotel"
+import type { PointOfInterestGroupEnum } from "@/types/enums/pointOfInterest"
+import type { poiVariants } from "@/components/Maps/Markers/Poi/variants"
export interface PoiMarkerProps extends VariantProps {
group: PointOfInterestGroupEnum
diff --git a/types/components/sidePeeks/roomSidePeek.ts b/types/components/sidePeeks/roomSidePeek.ts
index 0298cb83c..cd978578f 100644
--- a/types/components/sidePeeks/roomSidePeek.ts
+++ b/types/components/sidePeeks/roomSidePeek.ts
@@ -1,9 +1,8 @@
-import { SidePeekEnum } from "../hotelReservation/sidePeek"
-
-import type { RoomData } from "@/types/hotel"
+import type { Room } from "@/types/hotel"
+import type { SidePeekEnum } from "../hotelReservation/sidePeek"
export type RoomSidePeekProps = {
- room: RoomData
+ room: Room
activeSidePeek: SidePeekEnum | null
close: () => void
}
diff --git a/types/enums/pointOfInterest.ts b/types/enums/pointOfInterest.ts
new file mode 100644
index 000000000..8fc582389
--- /dev/null
+++ b/types/enums/pointOfInterest.ts
@@ -0,0 +1,8 @@
+export enum PointOfInterestGroupEnum {
+ PUBLIC_TRANSPORT = "Public transport",
+ ATTRACTIONS = "Attractions",
+ BUSINESS = "Business",
+ LOCATION = "Location",
+ PARKING = "Parking",
+ SHOPPING_DINING = "Shopping & Dining",
+}
diff --git a/types/hotel.ts b/types/hotel.ts
index 54448dd66..3e46ec5b4 100644
--- a/types/hotel.ts
+++ b/types/hotel.ts
@@ -1,66 +1,66 @@
import type { z } from "zod"
-import type {
- checkinSchema,
- getHotelDataSchema,
- parkingSchema,
- pointOfInterestSchema,
-} from "@/server/routers/hotels/output"
+import type { hotelSchema } from "@/server/routers/hotels/output"
+import type { citySchema } from "@/server/routers/hotels/schemas/city"
+import type { attributesSchema } from "@/server/routers/hotels/schemas/hotel"
+import type { addressSchema } from "@/server/routers/hotels/schemas/hotel/address"
+import type { hotelContentSchema } from "@/server/routers/hotels/schemas/hotel/content"
+import type { detailedFacilitiesSchema } from "@/server/routers/hotels/schemas/hotel/detailedFacility"
+import type { checkinSchema } from "@/server/routers/hotels/schemas/hotel/facts"
+import type { nearbyHotelsSchema } from "@/server/routers/hotels/schemas/hotel/include/nearbyHotels"
+import type { restaurantsSchema } from "@/server/routers/hotels/schemas/hotel/include/restaurants"
+import type { transformRoomCategories } from "@/server/routers/hotels/schemas/hotel/include/roomCategories"
+import type { locationSchema } from "@/server/routers/hotels/schemas/hotel/location"
+import type { parkingSchema } from "@/server/routers/hotels/schemas/hotel/parking"
+import type { pointOfInterestSchema } from "@/server/routers/hotels/schemas/hotel/poi"
+import type { ratingsSchema } from "@/server/routers/hotels/schemas/hotel/rating"
+import type { imageSchema } from "@/server/routers/hotels/schemas/image"
+import { restaurantOpeningHoursSchema } from "@/server/routers/hotels/schemas/restaurants"
+import { healthFacilitySchema } from "@/server/routers/hotels/schemas/hotel/healthFacilities"
import type {
additionalDataSchema,
extraPageSchema,
facilitySchema,
+ transformAdditionalData,
} from "@/server/routers/hotels/schemas/additionalData"
-import type { imageSchema } from "@/server/routers/hotels/schemas/image"
-import type {
- restaurantOpeningHoursSchema,
- restaurantSchema,
-} from "@/server/routers/hotels/schemas/restaurants"
-import type { roomSchema } from "@/server/routers/hotels/schemas/room"
-export type HotelData = z.output
+export type HotelData = z.output
-export type Hotel = HotelData["data"]["attributes"]
-export type HotelAddress = HotelData["data"]["attributes"]["address"]
-export type HotelLocation = HotelData["data"]["attributes"]["location"]
-export type Amenities = HotelData["data"]["attributes"]["detailedFacilities"]
-export type HealthFacilities =
- HotelData["data"]["attributes"]["healthFacilities"]
+export type Amenities = z.output
+export type CheckInData = z.output
+type CitySchema = z.output
+export type City = Pick & CitySchema["attributes"]
+export type Facility = z.output & { id: string }
+export type GalleryImage = z.output
+export type HealthFacility = z.output
+export type HealthFacilities = HealthFacility[]
+export type Hotel = z.output
+export type HotelAddress = z.output
+export type HotelContent = z.output
+export type HotelLocation = z.output
+export type HotelRatings = z.output
+type NearbyHotelsSchema = z.output
+export type NearbyHotel = Pick &
+ NearbyHotelsSchema["attributes"]
+export type Parking = z.output
+export type PointOfInterest = z.output
+type RestaurantSchema = z.output
+export type RestaurantOpeningHours = z.output
+export type Restaurant = Pick &
+ RestaurantSchema["attributes"]
+export type Room = ReturnType
-type HotelRatings = HotelData["data"]["attributes"]["ratings"]
+export type HotelMapContentProps = {
+ activePoi?: string | null
+ coordinates: { lat: number; lng: number }
+ onActivePoiChange?: (poiName: string | null) => void
+ pointsOfInterest: PointOfInterest[]
+}
export type HotelTripAdvisor =
| NonNullable["tripAdvisor"]
| undefined
-export type RoomData = z.infer
-export type RestaurantData = z.output
-export type RestaurantOpeningHours = z.output<
- typeof restaurantOpeningHoursSchema
->
-export type GalleryImage = z.infer
-export type CheckInData = z.infer
+export type AdditionalData = ReturnType
-export type AdditionalData = z.infer
+export type ExtraPageSchema = z.output
-export type ExtraPageSchema = z.infer
-
-export type PointOfInterest = z.output
-
-export enum PointOfInterestGroupEnum {
- PUBLIC_TRANSPORT = "Public transport",
- ATTRACTIONS = "Attractions",
- BUSINESS = "Business",
- LOCATION = "Location",
- PARKING = "Parking",
- SHOPPING_DINING = "Shopping & Dining",
-}
-
-export type ParkingData = z.infer
-export type Facility = z.infer & { id: string }
-
-export type HotelMapContentProps = {
- coordinates: { lat: number; lng: number }
- pointsOfInterest: PointOfInterest[]
- onActivePoiChange?: (poiName: string | null) => void
- activePoi?: string | null
-}
diff --git a/types/providers/enter-details.ts b/types/providers/enter-details.ts
index 05db0ee74..12a03251e 100644
--- a/types/providers/enter-details.ts
+++ b/types/providers/enter-details.ts
@@ -1,6 +1,6 @@
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
import type { StepEnum } from "@/types/enums/step"
-import type { RoomAvailability } from "@/types/trpc/routers/hotel/availability"
+import type { RoomAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
import type { SafeUser } from "@/types/user"
import type { SelectRateSearchParams } from "../components/hotelReservation/selectRate/selectRate"
import type { Packages } from "../requests/packages"
diff --git a/types/requests/packages.ts b/types/requests/packages.ts
index fb242917f..f0d501332 100644
--- a/types/requests/packages.ts
+++ b/types/requests/packages.ts
@@ -1,15 +1,15 @@
-import { z } from "zod"
+import type { z } from "zod"
-import {
- getBreakfastPackageInputSchema,
- getRoomPackagesInputSchema,
+import type {
+ breakfastPackageInputSchema,
+ roomPackagesInputSchema,
} from "@/server/routers/hotels/input"
-import { getRoomPackagesSchema } from "@/server/routers/hotels/output"
+import type { packagesSchema } from "@/server/routers/hotels/output"
export interface BreackfastPackagesInput
- extends z.input {}
+ extends z.input {}
export interface PackagesInput
- extends z.input {}
+ extends z.input {}
-export interface Packages extends z.output {}
+export type Packages = z.output
diff --git a/types/trpc/routers/booking/confirmation.ts b/types/trpc/routers/booking/confirmation.ts
index d5440f0c7..713842cfe 100644
--- a/types/trpc/routers/booking/confirmation.ts
+++ b/types/trpc/routers/booking/confirmation.ts
@@ -1,20 +1,17 @@
import type { z } from "zod"
-import type { Hotel, RoomData } from "@/types/hotel"
+import type { Hotel, Room } from "@/types/hotel"
import type { bookingConfirmationSchema } from "@/server/routers/booking/output"
export interface BookingConfirmationSchema
- extends z.output {}
+ extends z.output { }
+
export interface BookingConfirmation {
booking: BookingConfirmationSchema
- hotel: Hotel & {
- included?: {
- rooms: RoomData[] | undefined
- }
- }
+ hotel: Hotel
room:
- | (RoomData & {
- bedType: RoomData["roomTypes"][number]
- })
- | null
+ | (Room & {
+ bedType: Room["roomTypes"][number]
+ })
+ | null
}
diff --git a/types/trpc/routers/hotel/availability.ts b/types/trpc/routers/hotel/availability.ts
index 760766837..90f1ccb07 100644
--- a/types/trpc/routers/hotel/availability.ts
+++ b/types/trpc/routers/hotel/availability.ts
@@ -1,5 +1,9 @@
-import type { RouterOutput } from "@/lib/trpc/client"
+import type { z } from "zod"
-export type RoomAvailability = NonNullable<
- RouterOutput["hotel"]["availability"]["room"]
->
+import type { hotelsAvailabilitySchema } from "@/server/routers/hotels/output"
+import type { productTypeSchema } from "@/server/routers/hotels/schemas/availability/productType"
+import type { productTypePriceSchema } from "@/server/routers/hotels/schemas/productTypePrice"
+
+export type HotelsAvailability = z.output
+export type ProductType = z.output
+export type ProductTypePrices = z.output
diff --git a/types/trpc/routers/hotel/filter.ts b/types/trpc/routers/hotel/filter.ts
new file mode 100644
index 000000000..5f4108b68
--- /dev/null
+++ b/types/trpc/routers/hotel/filter.ts
@@ -0,0 +1,5 @@
+import type { z } from "zod"
+
+import type { hotelFilterSchema } from "@/server/routers/hotels/schemas/hotelFilter"
+
+export type HotelFilter = z.output
diff --git a/types/trpc/routers/hotel/hotel.ts b/types/trpc/routers/hotel/hotel.ts
new file mode 100644
index 000000000..d086488ef
--- /dev/null
+++ b/types/trpc/routers/hotel/hotel.ts
@@ -0,0 +1,12 @@
+import type { z } from "zod"
+
+import type { Room } from "@/types/hotel"
+import type {
+ cityCoordinatesInputSchema,
+ hotelInputSchema,
+} from "@/server/routers/hotels/input"
+
+export type CityCoordinatesInput = z.input
+export type HotelInput = z.input
+
+export type RoomType = Pick
diff --git a/types/trpc/routers/hotel/locations.ts b/types/trpc/routers/hotel/locations.ts
index 40d56dc7f..30e668777 100644
--- a/types/trpc/routers/hotel/locations.ts
+++ b/types/trpc/routers/hotel/locations.ts
@@ -1,8 +1,12 @@
import type { z } from "zod"
-import type { apiLocationsSchema } from "@/server/routers/hotels/output"
+import type {
+ citiesByCountrySchema,
+ countriesSchema,
+ locationsSchema,
+} from "@/server/routers/hotels/output"
-export interface LocationSchema extends z.output {}
+export interface LocationSchema extends z.output { }
export type Locations = LocationSchema["data"]
export type Location = Locations[number]
@@ -15,3 +19,8 @@ export function isHotelLocation(
): location is HotelLocation {
return location?.type === "hotels"
}
+export interface CitiesByCountry
+ extends z.output { }
+export type CitiesGroupedByCountry = Record
+
+export interface Countries extends z.output { }
diff --git a/types/trpc/routers/hotel/rate.ts b/types/trpc/routers/hotel/rate.ts
new file mode 100644
index 000000000..2ebcb02af
--- /dev/null
+++ b/types/trpc/routers/hotel/rate.ts
@@ -0,0 +1,5 @@
+import type { z } from "zod"
+
+import type { rateSchema } from "@/server/routers/hotels/schemas/rate"
+
+export type Rate = z.output
diff --git a/types/trpc/routers/hotel/roomAvailability.ts b/types/trpc/routers/hotel/roomAvailability.ts
new file mode 100644
index 000000000..5f7afd495
--- /dev/null
+++ b/types/trpc/routers/hotel/roomAvailability.ts
@@ -0,0 +1,16 @@
+import type { z } from "zod"
+
+import type { RouterOutput } from "@/lib/trpc/client"
+import type { roomsAvailabilitySchema } from "@/server/routers/hotels/output"
+import type { roomConfigurationSchema } from "@/server/routers/hotels/schemas/roomAvailability/configuration"
+import type { productSchema } from "@/server/routers/hotels/schemas/roomAvailability/product"
+import type { rateDefinitionSchema } from "@/server/routers/hotels/schemas/roomAvailability/rateDefinition"
+
+export type RoomAvailability = NonNullable<
+ RouterOutput["hotel"]["availability"]["room"]
+>
+
+export type Product = z.output
+export type RateDefinition = z.output
+export type RoomConfiguration = z.output
+export type RoomsAvailability = z.output