import { z } from "zod" import { type FindMyBookingRoute, findMyBookingRoutes, } from "@scandic-hotels/common/constants/routes/findMyBookingRoutes" import { myStay } from "@scandic-hotels/common/constants/routes/myStay" import { transformedImageVaultAssetSchema } from "@scandic-hotels/common/utils/imageVault" import { Country } from "../../../types/country" import { RTETypeEnum } from "../../../types/RTEenums" import { destinationFilterSchema } from "../schemas/destinationFilters" import { systemSchema } from "../schemas/system" import type { Lang, LanguageSwitcherData, } from "@scandic-hotels/common/constants/language" import type { ImageVaultAsset } from "@scandic-hotels/common/utils/imageVault" const metaDataJsonSchema = z.object({ children: z.array( z.object({ type: z.nativeEnum(RTETypeEnum), children: z.array( z.object({ text: z.string().optional(), }) ), }) ), }) const metaDataBlocksSchema = z .array( z.object({ content: z .object({ content: z .object({ json: metaDataJsonSchema, }) .nullish(), }) .nullish(), }) ) .nullish() const metaDataImageSchema = z.object({ src: z.string().nullish(), altText: z.string().nullish(), altText_En: z.string().nullish(), }) const metaDataHotelDataSchema = z .object({ name: z.string(), detailedFacilities: z .array( z.object({ name: z.string(), }) ) .nullish(), healthFacilities: z .array( z .object({ content: z .object({ images: z.array(metaDataImageSchema), }) .nullish(), }) .nullish() ) .nullish(), translatedCityName: z.string(), }) .nullish() const metaDataAdditionalHotelDataSchema = z .object({ gallery: z .object({ heroImages: z.array(metaDataImageSchema).nullish(), smallerImages: z.array(metaDataImageSchema).nullish(), }) .nullish(), hotelParking: z .object({ nameInUrl: z.string().nullish(), elevatorPitch: z.string().nullish(), }) .nullish(), healthAndFitness: z .object({ nameInUrl: z.string().nullish(), elevatorPitch: z.string().nullish(), }) .nullish(), hotelSpecialNeeds: z .object({ nameInUrl: z.string().nullish(), elevatorPitch: z.string().nullish(), }) .nullish(), meetingRooms: z .object({ nameInUrl: z.string().nullish(), elevatorPitch: z.string().nullish(), }) .nullish(), parkingImages: z .object({ heroImages: z.array(metaDataImageSchema).nullish(), }) .nullish(), accessibility: z .object({ heroImages: z.array(metaDataImageSchema).nullish(), }) .nullish(), conferencesAndMeetings: z .object({ heroImages: z.array(metaDataImageSchema).nullish(), }) .nullish(), }) .nullish() const metaDataHotelRestaurantsSchema = z .array( z.object({ nameInUrl: z.string().nullish(), elevatorPitch: z.string().nullish(), name: z.string().nullish(), content: z .object({ images: z.array(metaDataImageSchema).nullish(), }) .nullish(), }) ) .nullish() export const seoMetadataSchema = z .object({ title: z.string().nullish(), description: z.string().nullish(), noindex: z.boolean().nullish(), seo_image: transformedImageVaultAssetSchema, }) .nullish() export const rawMetadataSchema = z.object({ web: z .object({ seo_metadata: seoMetadataSchema, breadcrumbs: z .object({ title: z.string().nullish(), }) .nullish(), }) .nullish(), destination_settings: z .object({ city_denmark: z.string().nullish(), city_finland: z.string().nullish(), city_germany: z.string().nullish(), city_poland: z.string().nullish(), city_norway: z.string().nullish(), city_sweden: z.string().nullish(), country: z.nativeEnum(Country).nullish(), }) .nullish(), heading: z.string().nullish(), preamble: z .union([ z.string(), z.object({ first_column: z.string(), }), ]) .transform((preamble) => { if (typeof preamble === "string") { return preamble } return preamble?.first_column || null }) .nullish(), header: z .object({ heading: z.string().nullish(), preamble: z.string().nullish(), hero_image: transformedImageVaultAssetSchema, }) .nullish(), hero_image: transformedImageVaultAssetSchema, images: z .array(z.object({ image: transformedImageVaultAssetSchema }).nullish()) .transform((images) => images .map((image) => image?.image) .filter((image): image is ImageVaultAsset => !!image) ) .nullish(), blocks: metaDataBlocksSchema, hotel_page_id: z .string() .nullish() .transform((id) => id?.trim() || null), hotelData: metaDataHotelDataSchema, additionalHotelData: metaDataAdditionalHotelDataSchema, hotelRestaurants: metaDataHotelRestaurantsSchema, subpageUrl: z.string().nullish(), destinationData: z .object({ location: z.string().nullish(), filter: z.string().nullish(), filterType: z.enum(["facility", "surroundings"]).nullish(), hotelCount: z.number().nullish(), cities: z.array(z.string()).nullish(), }) .nullish(), seo_filters: z .array( destinationFilterSchema.merge( z.object({ seo_metadata: seoMetadataSchema }) ) ) .nullish(), system: systemSchema, }) export interface RawMetadataSchema extends z.output {} // Several pages are not currently routed within contentstack context. // This function is used to generate the urls for these pages. export function getNonContentstackUrls(lang: Lang, pathName: string) { if (isFindMyBookingUrl(pathName)) { const urls: LanguageSwitcherData = {} return Object.entries(findMyBookingRoutes).reduce((acc, [lang, url]) => { acc[lang as Lang] = { url } return urls }, urls) } if (Object.values(myStay).includes(pathName)) { const urls: LanguageSwitcherData = {} return Object.entries(myStay).reduce((acc, [lang, url]) => { acc[lang as Lang] = { url } return urls }, urls) } if (pathName.startsWith(hotelreservation(lang))) { return baseUrls } return { [lang]: { url: pathName } } } const baseUrls: LanguageSwitcherData = { da: { url: "/da/" }, de: { url: "/de/" }, en: { url: "/en/" }, fi: { url: "/fi/" }, no: { url: "/no/" }, sv: { url: "/sv/" }, } function hotelreservation(lang: Lang) { return `/${lang}/hotelreservation` } function isFindMyBookingUrl(value: string): value is FindMyBookingRoute { return Object.values( findMyBookingRoutes as unknown as readonly string[] ).includes(value) }