feat(SW-219): add support for content card in cards grid in content pages

This commit is contained in:
Chuma McPhoy
2024-09-12 18:01:32 +02:00
parent 3eb7e5f653
commit 24dc404370
7 changed files with 93 additions and 92 deletions

View File

@@ -1,6 +1,7 @@
import SectionContainer from "@/components/Section/Container" import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header" import SectionHeader from "@/components/Section/Header"
import Card from "@/components/TempDesignSystem/Card" import Card from "@/components/TempDesignSystem/Card"
import ContentCard from "@/components/TempDesignSystem/ContentCard"
import Grids from "@/components/TempDesignSystem/Grids" import Grids from "@/components/TempDesignSystem/Grids"
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard" import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
@@ -22,6 +23,20 @@ export default function CardsGrid({
{cards_grid.cards.map((card) => { {cards_grid.cards.map((card) => {
switch (card.__typename) { switch (card.__typename) {
case CardsGridEnum.Card: { case CardsGridEnum.Card: {
if (card.isContentCard) {
return (
<ContentCard
key={card.system.uid}
title={card.heading || ""}
description={card.body_text || ""}
primaryButton={card.primaryButton}
secondaryButton={card.secondaryButton}
sidePeekButton={card.sidePeekButton}
backgroundImage={card.background_image}
style="default"
/>
)
} else {
return ( return (
<Card <Card
theme={cards_grid.theme || "one"} theme={cards_grid.theme || "one"}
@@ -34,6 +49,7 @@ export default function CardsGrid({
/> />
) )
} }
}
case CardsGridEnum.LoyaltyCard: case CardsGridEnum.LoyaltyCard:
return ( return (
<LoyaltyCard <LoyaltyCard

View File

@@ -1,8 +1,6 @@
import { env } from "@/env/server" import { env } from "@/env/server"
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import ContentCard from "@/components/TempDesignSystem/ContentCard"
import { MOCK_FACILITIES } from "./Facilities/mockData" import { MOCK_FACILITIES } from "./Facilities/mockData"
import { setActivityCard } from "./Facilities/utils" import { setActivityCard } from "./Facilities/utils"
import DynamicMap from "./Map/DynamicMap" import DynamicMap from "./Map/DynamicMap"
@@ -67,35 +65,6 @@ export default async function HotelPage() {
</div> </div>
<Rooms rooms={roomCategories} /> <Rooms rooms={roomCategories} />
<Facilities facilities={facilities} /> <Facilities facilities={facilities} />
{/* NOTE: These are added here for testing. Remove before PR */}
{/* Example of ContentCard with Button CTA's */}
<ContentCard
title="Special Offer"
description="Mattis sit duis pulvinar ultricies auctor euismod. Augue mattis mauris at est iaculis pulvinar pulvinar."
alwaysStack={true}
primaryCTA={{
label: "Book Now",
href: "/booking",
}}
secondaryCTA={{
label: "Learn More",
href: "/offers",
openInNewTab: true,
}}
backgroundImage="https://www.scandichotels.com/imageVault/publishedmedia/sixadhu91jy67aal2pla/Scandic_Downtown_Camper_cocktail_lounge_bar_the_ne.jpg"
/>
{/* Example of ContentCard with SidePeek */}
<ContentCard
title="Explore Facilities"
description="Mattis sit duis pulvinar ultricies auctor euismod. Augue mattis mauris at est iaculis pulvinar pulvinar."
sidePeekCTA={{
label: "View Facilities",
onClick: true,
}}
style="default"
backgroundImage="https://www.scandichotels.com/imageVault/publishedmedia/sixadhu91jy67aal2pla/Scandic_Downtown_Camper_cocktail_lounge_bar_the_ne.jpg"
/>
</main> </main>
{googleMapsApiKey ? ( {googleMapsApiKey ? (
<> <>

View File

@@ -16,9 +16,9 @@ import type { ContentCardProps } from "@/types/components/contentCard"
export default function ContentCard({ export default function ContentCard({
title, title,
description, description,
primaryCTA, primaryButton,
secondaryCTA, secondaryButton,
sidePeekCTA, sidePeekButton,
backgroundImage, backgroundImage,
style = "default", style = "default",
alwaysStack = false, alwaysStack = false,
@@ -31,8 +31,8 @@ export default function ContentCard({
{backgroundImage && ( {backgroundImage && (
<div className={styles.imageContainer}> <div className={styles.imageContainer}>
<Image <Image
src={backgroundImage} src={backgroundImage.url}
alt="" alt={backgroundImage.meta?.alt || ""}
className={styles.backgroundImage} className={styles.backgroundImage}
width={399} width={399}
height={201} height={201}
@@ -44,21 +44,23 @@ export default function ContentCard({
{title} {title}
</Subtitle> </Subtitle>
<Body color="black">{description}</Body> <Body color="black">{description}</Body>
{sidePeekCTA ? ( {!!sidePeekButton ? (
<Button <Button
// onClick={sidePeekCTA.onClick} // onClick={() => {
// // TODO: Implement sidePeek functionality once SW-341 is merged.
// }}
theme="base" theme="base"
variant="icon" variant="icon"
intent="text" intent="text"
size="small" size="small"
className={styles.sidePeekCTA} className={styles.sidePeekCTA}
> >
{sidePeekCTA.label} {sidePeekButton.title}
<ChevronRightIcon /> <ChevronRightIcon />
</Button> </Button>
) : ( ) : (
<div className={styles.ctaContainer}> <div className={styles.ctaContainer}>
{primaryCTA && ( {primaryButton && (
<Button <Button
asChild asChild
intent="primary" intent="primary"
@@ -66,14 +68,14 @@ export default function ContentCard({
className={styles.ctaButton} className={styles.ctaButton}
> >
<Link <Link
href={primaryCTA.href} href={primaryButton.href}
target={primaryCTA.openInNewTab ? "_blank" : undefined} target={primaryButton.openInNewTab ? "_blank" : undefined}
> >
{primaryCTA.label} {primaryButton.title}
</Link> </Link>
</Button> </Button>
)} )}
{secondaryCTA && ( {secondaryButton && (
<Button <Button
asChild asChild
intent="secondary" intent="secondary"
@@ -81,10 +83,10 @@ export default function ContentCard({
className={styles.ctaButton} className={styles.ctaButton}
> >
<Link <Link
href={secondaryCTA.href} href={secondaryButton.href}
target={secondaryCTA.openInNewTab ? "_blank" : undefined} target={secondaryButton.openInNewTab ? "_blank" : undefined}
> >
{secondaryCTA.label} {secondaryButton.title}
</Link> </Link>
</Button> </Button>
)} )}

View File

@@ -1,30 +1,13 @@
fragment CardBlock on Card { fragment CardBlock on Card {
is_content_card
heading heading
body_text body_text
background_image background_image
scripted_top_title scripted_top_title
title title
has_secondary_button
secondary_button {
is_contentstack_link
cta_text
open_in_new_tab
external_link {
title
href
}
linkConnection {
edges {
node {
__typename
...LoyaltyPageLink
...ContentPageLink
...AccountPageLink
}
}
}
}
has_primary_button has_primary_button
has_secondary_button
has_sidepeek_button
primary_button { primary_button {
is_contentstack_link is_contentstack_link
cta_text cta_text
@@ -44,6 +27,28 @@ fragment CardBlock on Card {
} }
} }
} }
secondary_button {
is_contentstack_link
cta_text
open_in_new_tab
external_link {
title
href
}
linkConnection {
edges {
node {
__typename
...LoyaltyPageLink
...ContentPageLink
...AccountPageLink
}
}
}
}
sidepeek_button {
call_to_action_text
}
system { system {
locale locale
uid uid

View File

@@ -68,6 +68,7 @@ export const contentPageDynamicContent = z.object({
export const cardBlock = z.object({ export const cardBlock = z.object({
__typename: z.literal(CardsGridEnum.Card), __typename: z.literal(CardsGridEnum.Card),
isContentCard: z.boolean(),
heading: z.string().nullable(), heading: z.string().nullable(),
body_text: z.string().nullable(), body_text: z.string().nullable(),
background_image: z.any(), background_image: z.any(),
@@ -88,6 +89,11 @@ export const cardBlock = z.object({
isExternal: z.boolean(), isExternal: z.boolean(),
}) })
.optional(), .optional(),
sidePeekButton: z
.object({
title: z.string(),
})
.optional(),
system: z.object({ system: z.object({
locale: z.nativeEnum(Lang), locale: z.nativeEnum(Lang),
uid: z.string(), uid: z.string(),

View File

@@ -102,6 +102,7 @@ export const contentPageQueryRouter = router({
case CardsGridEnum.Card: case CardsGridEnum.Card:
return { return {
...card, ...card,
isContentCard: !!card.is_content_card,
backgroundImage: makeImageVaultImage( backgroundImage: makeImageVaultImage(
card.background_image card.background_image
), ),
@@ -111,6 +112,14 @@ export const contentPageQueryRouter = router({
secondaryButton: card.has_secondary_button secondaryButton: card.has_secondary_button
? makeButtonObject(card.secondary_button) ? makeButtonObject(card.secondary_button)
: undefined, : undefined,
sidePeekButton:
card.has_sidepeek_button ||
!!card.sidepeek_button?.call_to_action_text
? {
title:
card.sidepeek_button.call_to_action_text,
}
: undefined,
} }
case CardsGridEnum.LoyaltyCard: case CardsGridEnum.LoyaltyCard:
return { return {

View File

@@ -1,27 +1,21 @@
import { VariantProps } from "class-variance-authority" import { VariantProps } from "class-variance-authority"
import { CardProps } from "@/components/TempDesignSystem/Card/card"
import { contentCardVariants } from "@/components/TempDesignSystem/ContentCard/variants" import { contentCardVariants } from "@/components/TempDesignSystem/ContentCard/variants"
export interface CTA { import { ImageVaultAsset } from "@/types/components/imageVault"
label: string
href: string
openInNewTab?: boolean
}
export interface SidePeekCTA { export interface SidePeekButton {
label: string title: string
// onClick: () => void
// TODO: change back to function.
onClick: boolean
} }
export interface ContentCardProps export interface ContentCardProps
extends VariantProps<typeof contentCardVariants> { extends VariantProps<typeof contentCardVariants> {
title: string title: string
description: string description: string
primaryCTA?: CTA primaryButton?: CardProps["primaryButton"]
secondaryCTA?: CTA secondaryButton?: CardProps["secondaryButton"]
sidePeekCTA?: SidePeekCTA sidePeekButton?: SidePeekButton
backgroundImage?: string backgroundImage?: ImageVaultAsset
className?: string className?: string
} }