fix: clean up hotel and its typings

This commit is contained in:
Simon Emanuelsson
2024-12-17 16:17:25 +01:00
parent ec74af8814
commit 13a164242f
110 changed files with 1931 additions and 1559 deletions

View File

@@ -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<typeof additionalDataSchema>
) {
return {
...data.attributes,
id: data.attributes.id,
type: data.type
}
}

View File

@@ -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),
})

View File

@@ -0,0 +1,10 @@
import { z } from "zod"
import { productTypePriceSchema } from "../productTypePrice"
export const productTypeSchema = z
.object({
public: productTypePriceSchema.optional(),
member: productTypePriceSchema.optional(),
})
.optional()

View File

@@ -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"),
})

View File

@@ -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,
})

View File

@@ -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(),
})

View File

@@ -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(),
})

View File

@@ -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(),
}),
})

View File

@@ -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)
)

View File

@@ -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(),
})

View File

@@ -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),
})

View File

@@ -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(),
})

View File

@@ -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
}
})

View File

@@ -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"),
})

View File

@@ -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"),
})

View File

@@ -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<typeof roomCategoriesSchema>
) {
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}`,
},
}
}

View File

@@ -0,0 +1,7 @@
import { z } from "zod"
export const locationSchema = z.object({
distanceToCentre: z.number(),
latitude: z.number(),
longitude: z.number(),
})

View File

@@ -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(),
})

View File

@@ -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(),
})

View File

@@ -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))
)

View File

@@ -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()

View File

@@ -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(),
})

View File

@@ -0,0 +1,6 @@
import { z } from "zod"
export const socialMediaSchema = z.object({
facebook: z.string().optional(),
instagram: z.string().optional(),
})

View File

@@ -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([])

View File

@@ -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),
})

View File

@@ -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()),
})

View File

@@ -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 = {

View File

@@ -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"),
})

View File

@@ -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"),
})

View File

@@ -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(),
})
),
})

View File

@@ -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)

View File

@@ -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(),
})

View File

@@ -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(),
})

View File

@@ -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(),
})
),
})

View File

@@ -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(),
})

View File

@@ -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,
}),
}),
})

View File

@@ -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(),
})