import { z } from "zod" import { transformedImageVaultAssetSchema } from "@scandic-hotels/common/utils/imageVault" import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url" import { CampaignPageEnum } from "../../../types/campaignPage" import { discriminatedUnionArray } from "../../../utils/discriminatedUnion" import { accordionRefsSchema, accordionSchema, } from "../schemas/blocks/accordion" import { carouselCardsRefsSchema, carouselCardsSchema, } from "../schemas/blocks/carouselCards" import { essentialsBlockSchema } from "../schemas/blocks/essentials" import { campaignPageHotelListingSchema } from "../schemas/blocks/hotelListing" import { linkConnectionRefs, linkConnectionSchema, } from "../schemas/linkConnection" import { systemSchema } from "../schemas/system" import { getCarouselCardsBlockWithBookingCodeLinks } from "./utils" import type { ImageVaultAsset } from "@scandic-hotels/common/utils/imageVault" const campaignPageEssentials = z .object({ __typename: z.literal(CampaignPageEnum.ContentStack.blocks.Essentials), }) .merge(essentialsBlockSchema) const campaignPageCarouselCards = z .object({ __typename: z.literal(CampaignPageEnum.ContentStack.blocks.CarouselCards), }) .merge(carouselCardsSchema) export const campaignPageAccordion = z .object({ __typename: z.literal(CampaignPageEnum.ContentStack.blocks.Accordion), }) .merge(accordionSchema) export const campaignPageHotelListing = z .object({ __typename: z.literal(CampaignPageEnum.ContentStack.blocks.HotelListing), }) .merge(campaignPageHotelListingSchema) export const blocksSchema = z.discriminatedUnion("__typename", [ campaignPageEssentials, campaignPageCarouselCards, campaignPageAccordion, campaignPageHotelListing, ]) export const heroSchema = z.object({ image: transformedImageVaultAssetSchema, heading: z.string(), theme: z .enum(["Peach", "Burgundy"]) .nullish() .transform((theme) => { switch (theme) { case "Burgundy": return "Primary 3" case "Peach": default: return "Accent" } }), benefits: z .array(z.string()) .nullish() .transform((data) => data || []), rate_text: z .object({ bold_text: z.string().nullish(), text: z.string().nullish(), }) .nullish() .transform((data) => { if (data?.bold_text || data?.text) { return data } return null }), campaign_text: z .object({ text: z.string().nullish(), bold_text: z.string().nullish(), }) .nullish() .transform((data) => { if (data?.bold_text || data?.text) { return data } return null }), button: z .intersection(z.object({ cta: z.string() }), linkConnectionSchema) .transform((data) => { if (!data.link) { return null } return { cta: data.cta, url: data.link?.url || "", } }), }) export const includedHotelsSchema = z .object({ list_1Connection: z.object({ edges: z.array( z.object({ node: z.object({ hotel_page_id: z.string().transform((id) => id.trim()), }), }) ), }), list_2Connection: z.object({ edges: z.array( z.object({ node: z.object({ hotel_page_id: z.string().transform((id) => id.trim()), }), }) ), }), }) .transform((data) => { const list1HotelIds = data.list_1Connection.edges .map((edge) => edge.node.hotel_page_id) .filter(Boolean) const list2HotelIds = data.list_2Connection.edges .map((edge) => edge.node.hotel_page_id) .filter(Boolean) return [...new Set([...list1HotelIds, ...list2HotelIds])] }) export const campaignPageSchema = z .object({ campaign_page: z.object({ title: z.string(), hero: heroSchema, heading: z.string(), subheading: z.string().nullish(), included_hotels: includedHotelsSchema, preamble: z.object({ is_two_columns: z.boolean().default(false), first_column: z.string(), second_column: z.string(), }), blocks: discriminatedUnionArray(blocksSchema.options), page_settings: z .object({ booking_code: z.string().nullish(), }) .nullish(), system: systemSchema.merge( z.object({ created_at: z.string(), updated_at: z.string(), }) ), }), trackingProps: z.object({ url: z.string(), }), }) .transform(({ campaign_page, ...data }) => { const { blocks, page_settings, included_hotels, ...campaignPageData } = campaign_page const bookingCode = page_settings?.booking_code || null const mappedBlocks = blocks.map((block) => { switch (block.__typename) { case CampaignPageEnum.ContentStack.blocks.HotelListing: return { ...block, hotel_listing: { ...block.hotel_listing, hotelIds: included_hotels, bookingCode, }, } case CampaignPageEnum.ContentStack.blocks.CarouselCards: return getCarouselCardsBlockWithBookingCodeLinks(block, bookingCode) case CampaignPageEnum.ContentStack.blocks.Essentials: case CampaignPageEnum.ContentStack.blocks.Accordion: default: return block } }) return { ...data, campaign_page: { ...campaignPageData, blocks: [...mappedBlocks], }, } }) export const campaignPagesByHotelUidSchema = z .object({ all_campaign_page: z.object({ items: z .array( z.object({ heading: z.string(), url: z.string(), sort_order: z.number().nullish(), card_content: z .object({ image: transformedImageVaultAssetSchema, heading: z.string().nullish(), text: z.string().nullish(), }) .nullish(), hero: heroSchema, system: systemSchema, }) ) .transform((data) => { const mappedCampaigns = data.map((campaign) => { const { card_content, hero, system, heading, sort_order, url } = campaign const hasImage = !!(card_content?.image || hero.image) const cardContentImage = card_content?.image || hero.image const heroImage = hero.image || card_content?.image if (hasImage) { return { id: system.uid, url: removeMultipleSlashes(`/${system.locale}/${url}`), sort_order, card_content: { ...card_content, heading: card_content?.heading || heading, image: (cardContentImage || heroImage) as ImageVaultAsset, }, hero: { ...hero, image: (heroImage || cardContentImage) as ImageVaultAsset, heading: hero.heading || card_content?.heading || heading, }, } } return null }) return mappedCampaigns.filter((item) => !!item) }), }), }) .transform((data) => data.all_campaign_page.items) /** REFS */ const campaignPageCarouselCardsRef = z .object({ __typename: z.literal(CampaignPageEnum.ContentStack.blocks.CarouselCards), }) .merge(carouselCardsRefsSchema) const campaignPageAccordionRefs = z .object({ __typename: z.literal(CampaignPageEnum.ContentStack.blocks.Accordion), }) .merge(accordionRefsSchema) const campaignPageBlockRefsItem = z.discriminatedUnion("__typename", [ campaignPageCarouselCardsRef, campaignPageAccordionRefs, ]) const heroRefsSchema = z.object({ button: linkConnectionRefs, }) export const campaignPageRefsSchema = z.object({ campaign_page: z.object({ hero: heroRefsSchema, blocks: discriminatedUnionArray( campaignPageBlockRefsItem.options ).nullable(), system: systemSchema, }), }) export const campaignPagesByHotelUidRefsSchema = z .object({ all_campaign_page: z.object({ items: z.array( z.object({ system: systemSchema, }) ), }), }) .transform((data) => data.all_campaign_page.items.map((item) => item.system))