fix: clean up hotel and its typings
This commit is contained in:
@@ -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<typeof hotelsAvailabilitySchema>
|
||||
export type ProductType =
|
||||
HotelsAvailability["data"][number]["attributes"]["productType"]
|
||||
export type ProductTypePrices = z.infer<typeof productTypePriceSchema>
|
||||
export type HotelsAvailabilityItem =
|
||||
HotelsAvailability["data"][number]["attributes"]
|
||||
|
||||
const roomConfigurationSchema = z.object({
|
||||
status: z.string(),
|
||||
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<typeof roomsAvailabilitySchema>
|
||||
export type RoomConfiguration = z.infer<typeof roomConfigurationSchema>
|
||||
export type Product = z.infer<typeof productSchema>
|
||||
export type RateDefinition = z.infer<typeof rateDefinitionSchema>
|
||||
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<typeof rate>
|
||||
|
||||
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<typeof hotelFilter>
|
||||
|
||||
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<typeof apiCitiesByCountrySchema> {}
|
||||
export type CitiesGroupedByCountry = Record<string, CitiesByCountry["data"]>
|
||||
|
||||
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<typeof apiCountriesSchema> {}
|
||||
|
||||
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))
|
||||
Reference in New Issue
Block a user