Feat/SW-3028 hotel page campaigns

* feat(SW-3028): Added query and typings to fetch campaigns by hotelUid
* feat(SW-3028): Added components for campaigns to the hotel page
* feat(SW-3028): Implemented prioritized campaigns list
* chore(SW-3028): Refactor how campaigns are fetched on hotel pages
* feat(SW-3028): Added offers/campaigns to tab navigation

Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-08-21 13:00:34 +00:00
parent 456e10c674
commit 2064732e56
22 changed files with 566 additions and 45 deletions

View File

@@ -1,5 +1,7 @@
import { z } from "zod"
import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import { CampaignPageEnum } from "../../../types/campaignPage"
import { discriminatedUnionArray } from "../../../utils/discriminatedUnion"
import {
@@ -20,6 +22,8 @@ import {
import { systemSchema } from "../schemas/system"
import { getCarouselCardsBlockWithBookingCodeLinks } from "./utils"
import type { ImageVaultAsset } from "../../../types/imageVault"
const campaignPageEssentials = z
.object({
__typename: z.literal(CampaignPageEnum.ContentStack.blocks.Essentials),
@@ -173,6 +177,56 @@ export const campaignPageSchema = z
}
})
export const campaignPagesByHotelUidSchema = z
.object({
all_campaign_page: z.object({
items: z
.array(
z.object({
heading: z.string(),
url: z.string(),
card_content: z
.object({
image: tempImageVaultAssetSchema,
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, 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}`),
card_content: {
...card_content,
heading: card_content?.heading || heading,
image: (cardContentImage || heroImage) as ImageVaultAsset,
},
hero: {
...hero,
image: (heroImage || cardContentImage) as ImageVaultAsset,
heading,
},
}
}
return null
})
return mappedCampaigns.filter((item) => !!item)
}),
}),
})
.transform((data) => data.all_campaign_page.items)
/** REFS */
const campaignPageCarouselCardsRef = z
.object({
@@ -203,3 +257,15 @@ export const campaignPageRefsSchema = z.object({
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))