feat(SW-2270): Added hotel listing block to campaign overview page

Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-06-30 09:29:20 +00:00
parent 4229e9b11b
commit 0393f7b7b9
10 changed files with 89 additions and 1 deletions

View File

@@ -20,6 +20,7 @@ import type { HotelDataWithUrl } from "@scandic-hotels/trpc/types/hotel"
interface CampaignHotelListingClientProps {
heading: string
preamble?: string | null
hotels: HotelDataWithUrl[]
visibleCountMobile?: 3 | 6
visibleCountDesktop?: 3 | 6
@@ -27,6 +28,7 @@ interface CampaignHotelListingClientProps {
export default function CampaignHotelListingClient({
heading,
preamble,
hotels,
visibleCountMobile = 3,
visibleCountDesktop = 6,
@@ -46,7 +48,7 @@ export default function CampaignHotelListingClient({
)
// Only show the show more/less button if the length of hotels exceeds the threshold count
const showButton = hotels.length >= thresholdCount
const showButton = hotels.length > thresholdCount
// Determine if we are at the stage where the user can click to show all hotels
const canShowAll =
@@ -93,6 +95,11 @@ export default function CampaignHotelListingClient({
<Typography variant="Title/Subtitle/lg">
<h3>{heading}</h3>
</Typography>
{preamble ? (
<Typography variant="Body/Paragraph/mdRegular">
<p>{preamble}</p>
</Typography>
) : null}
</header>
<ul className={styles.list}>
{hotels.map(({ hotel, url }, index) => (

View File

@@ -8,6 +8,11 @@
scroll-margin-top: var(--scroll-margin-top);
}
.header {
display: grid;
gap: var(--Space-x15);
}
.list {
list-style: none;
display: grid;

View File

@@ -4,6 +4,7 @@ import CampaignHotelListingClient from "./Client"
interface CampaignHotelListingProps {
heading: string
preamble?: string | null
hotelIds: string[]
visibleCountMobile?: 3 | 6
visibleCountDesktop?: 3 | 6
@@ -11,6 +12,7 @@ interface CampaignHotelListingProps {
export default async function CampaignHotelListing({
heading,
preamble,
hotelIds,
visibleCountMobile,
visibleCountDesktop,
@@ -24,6 +26,7 @@ export default async function CampaignHotelListing({
return (
<CampaignHotelListingClient
heading={heading}
preamble={preamble}
hotels={hotels}
visibleCountMobile={visibleCountMobile}
visibleCountDesktop={visibleCountDesktop}

View File

@@ -1,5 +1,6 @@
import { BlocksEnums } from "@scandic-hotels/trpc/types/blocks"
import CampaignHotelListing from "@/components/Blocks/CampaignHotelListing"
import CarouselCards from "@/components/Blocks/CarouselCards"
import type { BlocksProps } from "@/types/components/blocks"
@@ -14,6 +15,17 @@ export default function Blocks({ blocks }: BlocksProps) {
key={block.carousel_cards.heading}
/>
)
case BlocksEnums.block.CampaignOverviewPageHotelListing:
return (
<CampaignHotelListing
key={block.hotel_listing.heading}
heading={block.hotel_listing.heading}
preamble={block.hotel_listing.preamble}
hotelIds={block.hotel_listing.hotelIds}
visibleCountMobile={3}
visibleCountDesktop={3}
/>
)
default:
return null
}

View File

@@ -29,3 +29,19 @@ fragment HotelListing_CampaignPage on CampaignPageBlocksHotelListing {
heading
}
}
fragment HotelListing_CampaignOverviewPage on CampaignOverviewPageBlocksHotelListing {
hotel_listing {
heading
preamble
included_hotelsConnection {
edges {
node {
... on HotelPage {
hotel_page_id
}
}
}
}
}
}

View File

@@ -4,6 +4,7 @@
#import "../../Fragments/CampaignOverviewPage/TopCampaign.graphql"
#import "../../Fragments/Blocks/CarouselCards.graphql"
#import "../../Fragments/Blocks/HotelListing.graphql"
query GetCampaignOverviewPage($locale: String!, $uid: String!) {
campaign_overview_page(uid: $uid, locale: $locale) {
@@ -23,6 +24,7 @@ query GetCampaignOverviewPage($locale: String!, $uid: String!) {
blocks {
__typename
...CarouselCards_CampaignOverviewPage
...HotelListing_CampaignOverviewPage
}
system {
...System

View File

@@ -12,6 +12,7 @@ import {
carouselCardsRefsSchema,
carouselCardsSchema,
} from "../schemas/blocks/carouselCards"
import { campaignOverviewPageHotelListingSchema } from "../schemas/blocks/hotelListing"
import {
linkAndTitleSchema,
linkConnectionRefs,
@@ -70,8 +71,17 @@ const campaignOverviewPageCarouselCards = z
})
.merge(carouselCardsSchema)
export const campaignOverviewPageHotelListing = z
.object({
__typename: z.literal(
CampaignOverviewPageEnum.ContentStack.blocks.HotelListing
),
})
.merge(campaignOverviewPageHotelListingSchema)
export const blocksSchema = z.discriminatedUnion("__typename", [
campaignOverviewPageCarouselCards,
campaignOverviewPageHotelListing,
])
export const campaignOverviewPageSchema = z.object({

View File

@@ -69,3 +69,34 @@ export const campaignPageHotelListingSchema = z.object({
heading: z.string(),
}),
})
export const campaignOverviewPageHotelListingSchema = z.object({
typename: z
.literal(BlocksEnums.block.CampaignOverviewPageHotelListing)
.default(BlocksEnums.block.CampaignOverviewPageHotelListing),
hotel_listing: z
.object({
heading: z.string(),
preamble: z.string().nullish(),
included_hotelsConnection: z.object({
edges: z.array(
z.object({
node: z.object({
hotel_page_id: z.string(),
}),
})
),
}),
})
.transform((data) => {
const { included_hotelsConnection, ...rest } = data
const hotelIds = data.included_hotelsConnection.edges
.map(({ node }) => node.hotel_page_id)
.filter(Boolean)
return {
...rest,
hotelIds,
}
}),
})

View File

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

View File

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