feat(SW-219): add support for content card in cards grid in content pages
This commit is contained in:
@@ -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,17 +23,32 @@ 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: {
|
||||||
return (
|
if (card.isContentCard) {
|
||||||
<Card
|
return (
|
||||||
theme={cards_grid.theme || "one"}
|
<ContentCard
|
||||||
key={card.system.uid}
|
key={card.system.uid}
|
||||||
scriptedTopTitle={card.scripted_top_title}
|
title={card.heading || ""}
|
||||||
heading={card.heading}
|
description={card.body_text || ""}
|
||||||
bodyText={card.body_text}
|
primaryButton={card.primaryButton}
|
||||||
secondaryButton={card.secondaryButton}
|
secondaryButton={card.secondaryButton}
|
||||||
primaryButton={card.primaryButton}
|
sidePeekButton={card.sidePeekButton}
|
||||||
/>
|
backgroundImage={card.background_image}
|
||||||
)
|
style="default"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
theme={cards_grid.theme || "one"}
|
||||||
|
key={card.system.uid}
|
||||||
|
scriptedTopTitle={card.scripted_top_title}
|
||||||
|
heading={card.heading}
|
||||||
|
bodyText={card.body_text}
|
||||||
|
secondaryButton={card.secondaryButton}
|
||||||
|
primaryButton={card.primaryButton}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case CardsGridEnum.LoyaltyCard:
|
case CardsGridEnum.LoyaltyCard:
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -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 ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user