diff --git a/components/Content/Blocks/DynamicContent/OverviewTable/BenefitCard/index.tsx b/components/Content/Blocks/DynamicContent/OverviewTable/BenefitCard/index.tsx index ee02753a7..fba17ba79 100644 --- a/components/Content/Blocks/DynamicContent/OverviewTable/BenefitCard/index.tsx +++ b/components/Content/Blocks/DynamicContent/OverviewTable/BenefitCard/index.tsx @@ -6,7 +6,7 @@ import BenefitValue from "../BenefitValue" import styles from "./benefitCard.module.css" -import type { BenefitCardProps } from "@/types/components/loyalty/blocks" +import type { BenefitCardProps } from "@/types/components/content/blocks" export default function BenefitCard({ comparedValues, diff --git a/components/Content/Blocks/DynamicContent/OverviewTable/BenefitList/index.tsx b/components/Content/Blocks/DynamicContent/OverviewTable/BenefitList/index.tsx index dc947bfe0..b3aaf8784 100644 --- a/components/Content/Blocks/DynamicContent/OverviewTable/BenefitList/index.tsx +++ b/components/Content/Blocks/DynamicContent/OverviewTable/BenefitList/index.tsx @@ -4,7 +4,7 @@ import BenefitCard from "../BenefitCard" import styles from "./benefitList.module.css" -import type { BenefitListProps } from "@/types/components/loyalty/blocks" +import type { BenefitListProps } from "@/types/components/content/blocks" export default function BenefitList({ levels }: BenefitListProps) { return getUnlockedBenefits(levels).map((benefit) => { diff --git a/components/Content/Blocks/DynamicContent/OverviewTable/BenefitValue/index.tsx b/components/Content/Blocks/DynamicContent/OverviewTable/BenefitValue/index.tsx index e22b8f70e..546eba5b8 100644 --- a/components/Content/Blocks/DynamicContent/OverviewTable/BenefitValue/index.tsx +++ b/components/Content/Blocks/DynamicContent/OverviewTable/BenefitValue/index.tsx @@ -4,7 +4,7 @@ import CheckCircle from "@/components/Icons/CheckCircle" import styles from "./benefitValue.module.css" -import type { BenefitValueProps } from "@/types/components/loyalty/blocks" +import type { BenefitValueProps } from "@/types/components/content/blocks" export default function BenefitValue({ benefit }: BenefitValueProps) { if (!benefit.unlocked) { diff --git a/components/Content/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/index.tsx b/components/Content/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/index.tsx index edb176eba..e519b2529 100644 --- a/components/Content/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/index.tsx +++ b/components/Content/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/index.tsx @@ -8,7 +8,7 @@ import styles from "./desktopHeader.module.css" import type { DesktopSelectColumns, LargeTableProps, -} from "@/types/components/loyalty/blocks" +} from "@/types/components/content/blocks" export default function DesktopHeader({ levels, diff --git a/components/Content/Blocks/DynamicContent/OverviewTable/LargeTable/index.tsx b/components/Content/Blocks/DynamicContent/OverviewTable/LargeTable/index.tsx index 5c3566cd8..4a534fd5d 100644 --- a/components/Content/Blocks/DynamicContent/OverviewTable/LargeTable/index.tsx +++ b/components/Content/Blocks/DynamicContent/OverviewTable/LargeTable/index.tsx @@ -11,7 +11,7 @@ import styles from "./largeTable.module.css" import type { BenefitTableHeaderProps, LargeTableProps, -} from "@/types/components/loyalty/blocks" +} from "@/types/components/content/blocks" export default function LargeTable({ levels, diff --git a/components/Content/Blocks/DynamicContent/OverviewTable/LevelSummary/index.tsx b/components/Content/Blocks/DynamicContent/OverviewTable/LevelSummary/index.tsx index 153ba1f11..d4ee59647 100644 --- a/components/Content/Blocks/DynamicContent/OverviewTable/LevelSummary/index.tsx +++ b/components/Content/Blocks/DynamicContent/OverviewTable/LevelSummary/index.tsx @@ -1,6 +1,6 @@ import styles from "./levelSummary.module.css" -import type { LevelSummaryProps } from "@/types/components/loyalty/blocks" +import type { LevelSummaryProps } from "@/types/components/content/blocks" export default function LevelSummary({ level, diff --git a/components/ContentType/ContentPage/contentPage.module.css b/components/ContentType/ContentPage/contentPage.module.css index 979fad6e5..013b1863a 100644 --- a/components/ContentType/ContentPage/contentPage.module.css +++ b/components/ContentType/ContentPage/contentPage.module.css @@ -1,3 +1,7 @@ +/*nav:has(+ .contentPage) {*/ +/* background-color: var(--Base-Surface-Subtle-Normal);*/ +/*}*/ + .contentPage { padding-bottom: var(--Spacing-x9); } diff --git a/lib/graphql/Query/ContentPage.graphql b/lib/graphql/Query/ContentPage.graphql index 6dedd3057..2b054be5f 100644 --- a/lib/graphql/Query/ContentPage.graphql +++ b/lib/graphql/Query/ContentPage.graphql @@ -1,5 +1,6 @@ #import "../Fragments/Blocks/Card.graphql" #import "../Fragments/Blocks/LoyaltyCard.graphql" + #import "../Fragments/Blocks/Refs/Card.graphql" #import "../Fragments/Blocks/Refs/LoyaltyCard.graphql" @@ -83,6 +84,27 @@ query GetContentPage($locale: String!, $uid: String!) { } } } + ... on ContentPageBlocksDynamicContent { + __typename + dynamic_content { + title + subtitle + component + link { + text + pageConnection { + edges { + node { + # TODO: Link HotelPage + ...ContentPageLink + ...LoyaltyPageLink + } + } + totalCount + } + } + } + } } title header { @@ -164,6 +186,22 @@ query GetContentPageRefs($locale: String!, $uid: String!) { } } } + ... on ContentPageBlocksDynamicContent { + __typename + dynamic_content { + link { + pageConnection { + edges { + node { + __typename + ...ContentPageRef + ...LoyaltyPageRef + } + } + } + } + } + } } system { ...System diff --git a/server/routers/contentstack/contentPage/output.ts b/server/routers/contentstack/contentPage/output.ts index c15d18555..7f7eee45d 100644 --- a/server/routers/contentstack/contentPage/output.ts +++ b/server/routers/contentstack/contentPage/output.ts @@ -7,6 +7,7 @@ import { imageVaultAssetSchema } from "../schemas/imageVault" import { CardsGridEnum, ContentBlocksTypenameEnum, + DynamicContentComponentEnum, } from "@/types/components/content/enums" import { ImageVaultAsset } from "@/types/components/imageVault" import { Embeds } from "@/types/requests/embeds" @@ -45,7 +46,23 @@ const contentPageShortcuts = z.object({ }), }) -// TODO: this is a separate entity, should be in a separate file. +const contentPageDynamicContent = z.object({ + __typename: z.literal( + ContentBlocksTypenameEnum.ContentPageBlocksDynamicContent + ), + dynamic_content: z.object({ + title: z.string().nullable(), + subtitle: z.string().nullable(), + component: z.nativeEnum(DynamicContentComponentEnum), + link: z + .object({ + text: z.string(), + href: z.string(), + }) + .optional(), + }), +}) + const cardBlock = z.object({ __typename: z.literal(CardsGridEnum.Card), heading: z.string().nullable(), @@ -111,10 +128,13 @@ const contentPageCards = z.object({ const contentPageBlockItem = z.discriminatedUnion("__typename", [ contentPageBlockTextContent, - contentPageShortcuts, contentPageCards, + contentPageDynamicContent, + contentPageShortcuts, ]) +export type DynamicContent = z.infer + type BlockContentRaw = z.infer export interface RteBlockContent extends BlockContentRaw { content: { @@ -141,7 +161,7 @@ export type CardsGrid = Omit & { } export type CardsRaw = CardsGrid["cards_grid"]["cards"][number] -export type Block = RteBlockContent | Shortcuts | CardsGrid +export type Block = RteBlockContent | Shortcuts | CardsGrid | DynamicContent // Content Page Schema and types export const validateContentPageSchema = z.object({ @@ -246,7 +266,13 @@ const contentPageBlockTextContentRefs = z.object({ const contentPageCardsRefs = z.object({ __typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksCardsGrid), cards_grid: z.object({ - cardConnection: cardGridCardsRef, + cardConnection: z.object({ + edges: z.array( + z.object({ + node: cardGridCardsRef, + }) + ), + }), }), }) @@ -261,10 +287,22 @@ const contentPageShortcutsRefs = z.object({ }), }) +const contentPageDynamicContentRefs = z.object({ + __typename: z.literal( + ContentBlocksTypenameEnum.ContentPageBlocksDynamicContent + ), + dynamic_content: z.object({ + link: z.object({ + pageConnection: pageConnectionRefs, + }), + }), +}) + const contentPageBlockRefsItem = z.discriminatedUnion("__typename", [ contentPageBlockTextContentRefs, contentPageShortcutsRefs, contentPageCardsRefs, + contentPageDynamicContentRefs, ]) export const validateContentPageRefsSchema = z.object({ diff --git a/server/routers/contentstack/contentPage/query.ts b/server/routers/contentstack/contentPage/query.ts index 7611ea410..c64cef51e 100644 --- a/server/routers/contentstack/contentPage/query.ts +++ b/server/routers/contentstack/contentPage/query.ts @@ -121,6 +121,24 @@ export const contentPageQueryRouter = router({ ), }, } + case ContentBlocksTypenameEnum.ContentPageBlocksDynamicContent: + return { + ...block, + dynamic_content: { + ...block.dynamic_content, + link: block.dynamic_content.link.pageConnection.edges.length + ? { + text: block.dynamic_content.link.text, + href: removeMultipleSlashes( + `/${block.dynamic_content.link.pageConnection.edges[0].node.system.locale}/${block.dynamic_content.link.pageConnection.edges[0].node.url}` + ), + title: + block.dynamic_content.link.pageConnection.edges[0] + .node.title, + } + : undefined, + }, + } default: return block } diff --git a/types/components/content/blocks.ts b/types/components/content/blocks.ts index 702a086d4..720490a2a 100644 --- a/types/components/content/blocks.ts +++ b/types/components/content/blocks.ts @@ -1,8 +1,13 @@ +import { Lang } from "@/constants/languages" +import { membershipLevels } from "@/constants/membershipLevels" import { Block, CardsGrid, + DynamicContent, } from "@/server/routers/contentstack/contentPage/output" +import type { IntlFormatters } from "@formatjs/intl" + export type BlocksProps = { blocks: Block[] } @@ -10,3 +15,89 @@ export type BlocksProps = { export type CardsGridProps = Pick & { firstItem?: boolean } + +export type DynamicContentProps = { + dynamicContent: DynamicContent["dynamic_content"] + firstItem: boolean +} + +export type DynamicComponentProps = { + component: DynamicContent["dynamic_content"]["component"] +} + +type BenefitTitle = { title: string } + +export type Level = { + level: membershipLevels + name: string + requiredPoints: number + requiredNights?: number + benefits: BenefitTitle[] +} + +export type LevelCardProps = { + formatMessage: IntlFormatters["formatMessage"] + lang: Lang + level: Level +} + +export type BenefitCardProps = { + comparedValues: BenefitValueInformation[] + title: string + description: string +} + +type BenefitValueInformation = { + unlocked: boolean + value?: string + valueDetails?: string +} + +export type Benefit = { + name: string + description: string + unlocked: boolean + value?: string + valueDetails?: string +} + +export type ComparisonLevel = { + level: membershipLevels + name: string + description: string + requirement: string + icon: string + benefits: Benefit[] +} + +export type BenefitListProps = { + levels: ComparisonLevel[] +} + +export type BenefitValueProps = { + benefit: BenefitValueInformation +} + +export type MobileColumnHeaderProps = { + column: "A" | "B" +} + +export type DesktopSelectColumns = { + column: MobileColumnHeaderProps["column"] | "C" +} + +export type LargeTableProps = { + levels: ComparisonLevel[] + activeLevel: membershipLevels | null + Select?: (column: DesktopSelectColumns) => JSX.Element | null +} + +export type BenefitTableHeaderProps = { + name: string + description: string +} + +export type LevelSummaryProps = { + level: ComparisonLevel + showDescription?: boolean +} diff --git a/types/components/content/enums.ts b/types/components/content/enums.ts index a629a32c6..98515a381 100644 --- a/types/components/content/enums.ts +++ b/types/components/content/enums.ts @@ -2,9 +2,16 @@ export enum ContentBlocksTypenameEnum { ContentPageBlocksContent = "ContentPageBlocksContent", ContentPageBlocksShortcuts = "ContentPageBlocksShortcuts", ContentPageBlocksCardsGrid = "ContentPageBlocksCardsGrid", + ContentPageBlocksDynamicContent = "ContentPageBlocksDynamicContent", } export enum CardsGridEnum { LoyaltyCard = "LoyaltyCard", Card = "Card", } + +export enum DynamicContentComponentEnum { + loyalty_levels = "loyalty_levels", + how_it_works = "how_it_works", + overview_table = "overview_table", +}