feat(SW-3061): Added block for all campaigns

Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-08-14 07:07:54 +00:00
parent 985d47d7b3
commit dc483fe599
10 changed files with 276 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
.allCampaignsSection {
display: grid;
gap: var(--Space-x3);
}
.header {
display: grid;
gap: var(--Space-x15);
}
.heading {
color: var(--Text-Heading);
}
.cardsList {
list-style: none;
display: none;
}
@media screen and (min-width: 768px) {
.carousel {
display: none;
}
.cardsList {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--Space-x4) var(--Space-x1);
}
}
@media screen and (min-width: 1024px) {
.cardsList {
grid-template-columns: repeat(3, 1fr);
}
}

View File

@@ -0,0 +1,80 @@
import { Typography } from "@scandic-hotels/design-system/Typography"
import { Carousel } from "@/components/Carousel"
import { CarouselContent } from "@/components/Carousel/CarouselContent"
import { CarouselDots } from "@/components/Carousel/CarouselDots"
import { CarouselItem } from "@/components/Carousel/CarouselItem"
import {
CarouselNext,
CarouselPrevious,
} from "@/components/Carousel/CarouselNavigation"
import ContentCard from "@/components/ContentCard"
import styles from "./allCampaigns.module.css"
import type { ImageVaultAsset } from "@scandic-hotels/trpc/types/imageVault"
interface AllCampaignsProps {
heading: string
preamble?: string | null
cards: {
url: string
heading: string
text: string | null
image: ImageVaultAsset
}[]
}
export default function AllCampaigns({
heading,
preamble,
cards,
}: AllCampaignsProps) {
if (!cards.length) {
return null
}
return (
<section className={styles.allCampaignsSection}>
<header className={styles.header}>
<Typography variant="Title/md">
<h3 className={styles.heading}>{heading}</h3>
</Typography>
{preamble ? (
<Typography variant="Body/Paragraph/mdRegular">
<p className={styles.preamble}>{preamble}</p>
</Typography>
) : null}
</header>
<ul className={styles.cardsList}>
{cards.map(({ url, image, heading, text }) => (
<li key={url} className={styles.listItem}>
<ContentCard
heading={heading}
image={image}
bodyText={text || ""}
link={{ href: url }}
/>
</li>
))}
</ul>
<Carousel className={styles.carousel}>
<CarouselContent>
{cards.map(({ url, heading, text, image }) => (
<CarouselItem key={url}>
<ContentCard
heading={heading}
image={image}
bodyText={text || ""}
link={{ href: url }}
/>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious className={styles.navigationButton} />
<CarouselNext className={styles.navigationButton} />
<CarouselDots />
</Carousel>
</section>
)
}

View File

@@ -1,5 +1,6 @@
import { BlocksEnums } from "@scandic-hotels/trpc/types/blocksEnum"
import AllCampaigns from "@/components/Blocks/AllCampaigns"
import CampaignHotelListing from "@/components/Blocks/CampaignHotelListing"
import CarouselCards from "@/components/Blocks/CarouselCards"
@@ -26,6 +27,15 @@ export default function Blocks({ blocks }: BlocksProps) {
visibleCountDesktop={3}
/>
)
case BlocksEnums.block.CampaignOverviewPageAllCampaigns:
return (
<AllCampaigns
key={block.all_campaigns.heading}
heading={block.all_campaigns.heading}
preamble={block.all_campaigns.preamble}
cards={block.all_campaigns.cards}
/>
)
default:
return null
}

View File

@@ -0,0 +1,38 @@
#import "../CampaignPage/Ref.graphql"
fragment AllCampaigns on CampaignOverviewPageBlocksAllCampaigns {
all_campaigns {
heading
preamble
campaignsConnection {
edges {
node {
... on CampaignPage {
url
card_content {
heading
image
text
}
system {
locale
}
}
}
}
}
}
}
fragment AllCampaignsRefs on CampaignOverviewPageBlocksAllCampaigns {
all_campaigns {
campaignsConnection {
edges {
node {
__typename
...CampaignPageRef
}
}
}
}
}

View File

@@ -3,6 +3,7 @@
#import "../../Fragments/CampaignOverviewPage/NavigationLinks.graphql"
#import "../../Fragments/CampaignOverviewPage/TopCampaign.graphql"
#import "../../Fragments/Blocks/AllCampaigns.graphql"
#import "../../Fragments/Blocks/CarouselCards.graphql"
#import "../../Fragments/Blocks/HotelListing.graphql"
@@ -23,6 +24,7 @@ query GetCampaignOverviewPage($locale: String!, $uid: String!) {
}
blocks {
__typename
...AllCampaigns
...CarouselCards_CampaignOverviewPage
...HotelListing_CampaignOverviewPage
}
@@ -52,6 +54,7 @@ query GetCampaignOverviewPageRefs($locale: String!, $uid: String!) {
blocks {
__typename
...CarouselCards_CampaignOverviewPageRefs
...AllCampaignsRefs
}
system {
...System

View File

@@ -8,6 +8,10 @@ import {
heroSchema,
includedHotelsSchema,
} from "../campaignPage/output"
import {
allCampaignsRefsSchema,
allCampaignsSchema,
} from "../schemas/blocks/allCampaigns"
import {
carouselCardsRefsSchema,
carouselCardsSchema,
@@ -71,6 +75,14 @@ const campaignOverviewPageCarouselCards = z
})
.merge(carouselCardsSchema)
const campaignOverviewPageAllCampaigns = z
.object({
__typename: z.literal(
CampaignOverviewPageEnum.ContentStack.blocks.AllCampaigns
),
})
.merge(allCampaignsSchema)
export const campaignOverviewPageHotelListing = z
.object({
__typename: z.literal(
@@ -80,6 +92,7 @@ export const campaignOverviewPageHotelListing = z
.merge(campaignOverviewPageHotelListingSchema)
export const blocksSchema = z.discriminatedUnion("__typename", [
campaignOverviewPageAllCampaigns,
campaignOverviewPageCarouselCards,
campaignOverviewPageHotelListing,
])
@@ -134,7 +147,16 @@ const campaignOverviewPageCarouselCardsRef = z
})
.merge(carouselCardsRefsSchema)
const campaignOverviewPageAllCampaignsRef = z
.object({
__typename: z.literal(
CampaignOverviewPageEnum.ContentStack.blocks.AllCampaigns
),
})
.merge(allCampaignsRefsSchema)
const blockRefsSchema = z.discriminatedUnion("__typename", [
campaignOverviewPageAllCampaignsRef,
campaignOverviewPageCarouselCardsRef,
])

View File

@@ -45,6 +45,12 @@ export function getConnections({
})
break
}
case CampaignOverviewPageEnum.ContentStack.blocks.AllCampaigns: {
block.all_campaigns.campaignsConnection.edges.forEach(({ node }) => {
connections.push(node.system)
})
break
}
}
})
}

View File

@@ -0,0 +1,80 @@
import { z } from "zod"
import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import { BlocksEnums } from "../../../../types/blocksEnum"
import { tempImageVaultAssetSchema } from "../imageVault"
import { campaignPageRefSchema } from "../pageLinks"
export const campaignPageCardSchema = z.object({
image: tempImageVaultAssetSchema,
heading: z.string(),
text: z.string(),
})
export const allCampaignsSchema = z.object({
typename: z
.literal(BlocksEnums.block.CampaignOverviewPageAllCampaigns)
.optional()
.default(BlocksEnums.block.CampaignOverviewPageAllCampaigns),
all_campaigns: z
.object({
heading: z.string(),
preamble: z.string().nullish(),
campaignsConnection: z.object({
edges: z
.array(
z.object({
node: z.object({
url: z.string(),
card_content: z
.object({
image: tempImageVaultAssetSchema,
heading: z.string().nullish(),
text: z.string().nullish(),
})
.nullish(),
system: z.object({
locale: z.string(),
}),
}),
})
)
.transform((data) => {
const mappedCards = data.map(({ node }) => {
const { url, card_content } = node
if (url && card_content?.image && card_content?.heading) {
return {
url: removeMultipleSlashes(`/${node.system.locale}/${url}`),
heading: card_content.heading,
image: card_content.image,
text: card_content.text ?? null,
}
}
return null
})
return mappedCards.filter((card) => !!card)
}),
}),
})
.transform((data) => {
return {
...data,
cards: data.campaignsConnection.edges,
}
}),
})
export const allCampaignsRefsSchema = z.object({
all_campaigns: z.object({
campaignsConnection: z.object({
edges: z.array(
z.object({
node: campaignPageRefSchema,
})
),
}),
}),
})

View File

@@ -8,6 +8,7 @@ export namespace BlocksEnums {
DynamicContent = "DynamicContent",
FullWidthCampaign = "FullWidthCampaign",
CampaignOverviewPageHotelListing = "CampaignOverviewPageHotelListing",
CampaignOverviewPageAllCampaigns = "CampaignOverviewPageAllCampaigns",
CampaignPageHotelListing = "CampaignPageHotelListing",
ContentPageHotelListing = "ContentPageHotelListing",
JoinScandicFriends = "JoinScandicFriends",

View File

@@ -3,6 +3,7 @@ export namespace CampaignOverviewPageEnum {
export const enum blocks {
CarouselCards = "CampaignOverviewPageBlocksCarouselCards",
HotelListing = "CampaignOverviewPageBlocksHotelListing",
AllCampaigns = "CampaignOverviewPageBlocksAllCampaigns",
}
}
}