Files
web/packages/trpc/lib/routers/contentstack/campaignPage/output.ts
Erik Tiekstra c918c1aa66 fix(BOOK-661): Updated themes and correct variables for campaign hero
Approved-by: Matilda Landström
2026-01-26 06:44:43 +00:00

304 lines
8.3 KiB
TypeScript

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