From 0597b09c0858175008d65cd0fb505c24e290ccb9 Mon Sep 17 00:00:00 2001 From: Erik Tiekstra Date: Fri, 12 Dec 2025 06:34:32 +0000 Subject: [PATCH] Feat/BOOK-257 videoplayer with card * feat(BOOK-257): Added VideoPlayer with card component * feat(BOOK-257): Added queries and component for VideoCard block to Content and Collection pages * fix(BOOK-257): Only setting object-fit: cover on the video if it is not fullscreen * feat(BOOK-257): Added queries and component for VideoCard block to Startpage * feat(BOOK-257): Added queries and component for Video block to content/collection/start page Approved-by: Chuma Mcphoy (We Ahead) --- .../components/Blocks/Video/index.tsx | 13 ++ .../components/Blocks/VideoCard/index.tsx | 13 ++ apps/scandic-web/components/Blocks/index.tsx | 13 ++ .../ContentType/StartPage/Blocks/index.tsx | 17 ++ .../VideoPlayer/VideoPlayer.stories.tsx | 2 +- .../VideoPlayer/VideoWithCard/VideoCard.tsx | 82 +++++++ .../VideoWithCard/VideoWithCard.stories.tsx | 207 ++++++++++++++++++ .../VideoPlayer/VideoWithCard/index.tsx | 81 +++++++ .../VideoPlayer/VideoWithCard/variants.ts | 22 ++ .../VideoWithCard/videoWithCard.module.css | 84 +++++++ .../lib/components/VideoPlayer/index.tsx | 2 +- .../VideoPlayer/videoPlayer.module.css | 5 +- packages/design-system/package.json | 1 + .../graphql/Fragments/Blocks/Video.graphql.ts | 63 ++++++ .../Fragments/Blocks/VideoCard.graphql.ts | 166 ++++++++++++++ .../CollectionPage/CollectionPage.graphql.ts | 17 ++ .../Query/ContentPage/ContentPage.graphql.ts | 16 ++ .../Query/StartPage/StartPage.graphql.ts | 16 ++ .../contentstack/collectionPage/output.ts | 37 +++- .../contentstack/collectionPage/utils.ts | 80 +++++-- .../contentstack/contentPage/output.ts | 33 +++ .../routers/contentstack/contentPage/utils.ts | 75 +++++-- .../contentstack/schemas/blocks/video.ts | 14 ++ .../contentstack/schemas/blocks/videoCard.ts | 117 ++++++++++ .../routers/contentstack/startPage/output.ts | 33 +++ .../routers/contentstack/startPage/query.ts | 5 +- .../routers/contentstack/startPage/utils.ts | 53 ++++- packages/trpc/lib/types/blocks.ts | 5 + packages/trpc/lib/types/blocksEnum.ts | 2 + packages/trpc/lib/types/collectionPage.ts | 2 + packages/trpc/lib/types/contentPage.ts | 2 + 31 files changed, 1226 insertions(+), 52 deletions(-) create mode 100644 apps/scandic-web/components/Blocks/Video/index.tsx create mode 100644 apps/scandic-web/components/Blocks/VideoCard/index.tsx create mode 100644 packages/design-system/lib/components/VideoPlayer/VideoWithCard/VideoCard.tsx create mode 100644 packages/design-system/lib/components/VideoPlayer/VideoWithCard/VideoWithCard.stories.tsx create mode 100644 packages/design-system/lib/components/VideoPlayer/VideoWithCard/index.tsx create mode 100644 packages/design-system/lib/components/VideoPlayer/VideoWithCard/variants.ts create mode 100644 packages/design-system/lib/components/VideoPlayer/VideoWithCard/videoWithCard.module.css create mode 100644 packages/trpc/lib/graphql/Fragments/Blocks/Video.graphql.ts create mode 100644 packages/trpc/lib/graphql/Fragments/Blocks/VideoCard.graphql.ts create mode 100644 packages/trpc/lib/routers/contentstack/schemas/blocks/video.ts create mode 100644 packages/trpc/lib/routers/contentstack/schemas/blocks/videoCard.ts diff --git a/apps/scandic-web/components/Blocks/Video/index.tsx b/apps/scandic-web/components/Blocks/Video/index.tsx new file mode 100644 index 000000000..1d6f4781b --- /dev/null +++ b/apps/scandic-web/components/Blocks/Video/index.tsx @@ -0,0 +1,13 @@ +import { VideoPlayer } from "@scandic-hotels/design-system/VideoPlayer" + +import type { VideoBlock } from "@scandic-hotels/trpc/types/blocks" + +interface VideoBlockProps extends Pick {} + +export function VideoBlock({ video }: VideoBlockProps) { + if (!video) { + return null + } + + return +} diff --git a/apps/scandic-web/components/Blocks/VideoCard/index.tsx b/apps/scandic-web/components/Blocks/VideoCard/index.tsx new file mode 100644 index 000000000..f84b7909a --- /dev/null +++ b/apps/scandic-web/components/Blocks/VideoCard/index.tsx @@ -0,0 +1,13 @@ +import { VideoWithCard } from "@scandic-hotels/design-system/VideoWithCard" + +import type { VideoCard } from "@scandic-hotels/trpc/types/blocks" + +interface VideoCardBlockProps extends Pick {} + +export function VideoCardBlock({ video_card }: VideoCardBlockProps) { + if (!video_card) { + return null + } + + return +} diff --git a/apps/scandic-web/components/Blocks/index.tsx b/apps/scandic-web/components/Blocks/index.tsx index 0f7b97d58..44bbaf90f 100644 --- a/apps/scandic-web/components/Blocks/index.tsx +++ b/apps/scandic-web/components/Blocks/index.tsx @@ -7,6 +7,8 @@ import DynamicContent from "@/components/Blocks/DynamicContent" import ShortcutsList from "@/components/Blocks/ShortcutsList" import TextCols from "@/components/Blocks/TextCols" import UspGrid from "@/components/Blocks/UspGrid" +import { VideoBlock } from "@/components/Blocks/Video" +import { VideoCardBlock } from "@/components/Blocks/VideoCard" import AccordionSection from "./Accordion" import CardGallery from "./CardGallery" @@ -105,6 +107,17 @@ export default function Blocks({ blocks }: BlocksProps) { return case BlocksEnums.block.Essentials: return + case BlocksEnums.block.VideoCard: + return ( + + ) + case BlocksEnums.block.Video: + return ( + + ) default: return null } diff --git a/apps/scandic-web/components/ContentType/StartPage/Blocks/index.tsx b/apps/scandic-web/components/ContentType/StartPage/Blocks/index.tsx index 54d5f4dab..6af3f3a08 100644 --- a/apps/scandic-web/components/ContentType/StartPage/Blocks/index.tsx +++ b/apps/scandic-web/components/ContentType/StartPage/Blocks/index.tsx @@ -6,6 +6,8 @@ import CardsGrid from "@/components/Blocks/CardsGrid" import CarouselCards from "@/components/Blocks/CarouselCards" import FullWidthCampaign from "@/components/Blocks/FullWidthCampaign" import JoinScandicFriends from "@/components/Blocks/JoinScandicFriends" +import { VideoBlock } from "@/components/Blocks/Video" +import { VideoCardBlock } from "@/components/Blocks/VideoCard" import styles from "./blocks.module.css" @@ -55,6 +57,21 @@ export function Blocks({ blocks }: BlocksProps) { ) + case BlocksEnums.block.VideoCard: + return ( +
+ +
+ ) + case BlocksEnums.block.Video: + return ( +
+ +
+ ) default: return null } diff --git a/packages/design-system/lib/components/VideoPlayer/VideoPlayer.stories.tsx b/packages/design-system/lib/components/VideoPlayer/VideoPlayer.stories.tsx index 53ec27129..ff363dbb1 100644 --- a/packages/design-system/lib/components/VideoPlayer/VideoPlayer.stories.tsx +++ b/packages/design-system/lib/components/VideoPlayer/VideoPlayer.stories.tsx @@ -5,7 +5,7 @@ import { VideoPlayer } from '.' import { config as videoPlayerConfig } from './variants' const meta: Meta = { - title: 'Core Components/🚧 VideoPlayer 🚧', + title: 'Core Components//🚧 Video 🚧/VideoPlayer', component: VideoPlayer, parameters: { diff --git a/packages/design-system/lib/components/VideoPlayer/VideoWithCard/VideoCard.tsx b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/VideoCard.tsx new file mode 100644 index 000000000..64a1d86c6 --- /dev/null +++ b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/VideoCard.tsx @@ -0,0 +1,82 @@ +import { VariantProps } from 'class-variance-authority' +import { Typography } from '../../Typography' +import { variants } from './variants' + +import { VideoPlayer, VideoPlayerProps } from '..' +import styles from './videoWithCard.module.css' + +interface TextCardProps { + variant: 'text' + heading: string + text?: string +} +interface QuoteCardProps { + variant: 'quote' + quote: string + author: string + authorDescription?: string +} + +type VideoWithCardProps = VariantProps & + (TextCardProps | QuoteCardProps) & { + video: Pick + } + +export function VideoWithCard(props: VideoWithCardProps) { + const { variant, style, video } = props + const classNames = variants({ + variant, + style, + }) + + return ( +
+
+ +
+ +
+
+
+ ) +} + +function CardContent(props: VideoWithCardProps) { + if (props.variant === 'quote') { + const { quote, author, authorDescription } = props + + return ( + <> + +
{quote}
+
+ + + + {author} + + {authorDescription ? ( + + {authorDescription} + + ) : null} + + + ) + } + + const { heading, text } = props + + return ( + <> + +

{heading}

+
+ {text ? ( + +

{text}

+
+ ) : null} + + ) +} diff --git a/packages/design-system/lib/components/VideoPlayer/VideoWithCard/VideoWithCard.stories.tsx b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/VideoWithCard.stories.tsx new file mode 100644 index 000000000..9c4f050f0 --- /dev/null +++ b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/VideoWithCard.stories.tsx @@ -0,0 +1,207 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite' + +import { VideoWithCard } from '.' +import { config } from './variants' + +const meta: Meta = { + title: 'Core Components//🚧 Video 🚧//VideoWithCard', + component: VideoWithCard, + parameters: { + docs: { + description: { + component: + 'A component to display a VideoPlayer and content inside a card connected to the video. The size and gaps are determined by the parent container.', + }, + }, + }, + argTypes: { + variant: { + control: 'select', + options: Object.keys(config.variants.variant), + table: { + defaultValue: { + summary: config.defaultVariants.variant, + }, + type: { + summary: Object.keys(config.variants.variant).join(' | '), + }, + }, + description: + 'The variant of the card, which determines its style of the text and what content is shown.', + }, + style: { + control: 'select', + options: Object.keys(config.variants.style), + table: { + defaultValue: { + summary: config.defaultVariants.style, + }, + type: { + summary: Object.keys(config.variants.style).join(' | '), + }, + }, + description: + 'This decides the background color and text color of the card.', + }, + heading: { + table: { + type: { summary: 'string' }, + }, + description: 'The heading text. Only applicable for the text variant.', + }, + text: { + table: { + type: { summary: 'string' }, + }, + description: 'The body text. Only applicable for the text variant.', + }, + quote: { + table: { + type: { summary: 'string' }, + }, + description: 'The quote text. Only applicable for the quote variant.', + }, + author: { + table: { + type: { summary: 'string' }, + }, + description: + 'The author of the quote. Only applicable for the quote variant.', + }, + authorDescription: { + table: { + type: { summary: 'string' }, + }, + description: + 'The description of the author. Only applicable for the quote variant.', + }, + video: { + control: false, + table: { + type: { + summary: + '{ src: string; captions?: Caption[]; focalPoint?: FocalPoint}', + }, + }, + description: + 'The video props including source URL, captions and focal point. Please note that not all props from the VideoPlayer component are supported in this wrapper component.', + }, + }, +} + +export default meta + +type Story = StoryObj + +const videoProps = { + src: 'https://eu-assets.contentstack.com/v3/assets/bltfd73aa2de3a5c4e3/bltad0fe3c2ce340947/68eced6c14e5a8150ebba18c/Scandic_EB_Master.mp4', +} + +const quoteCardProps = { + variant: 'quote' as const, + quote: 'Download our membership App for smoother & richer experience', + author: 'Hans Christian Andersen', + authorDescription: 'The famed Danish storyteller.', + video: videoProps, +} + +const textCardProps = { + variant: 'text' as const, + heading: 'Download our membership App now', + text: 'Hans Christian Andersen, the famed Danish storyteller, spent many years of his life in Nyhavn, drawing inspiration from its lively atmosphere and picturesque setting.', + video: videoProps, +} + +const smallDecorator = (Story: React.FC) => ( +
+ +
+) + +const largeDecorator = (Story: React.FC) => ( +
+ +
+) + +export const QuotePrimary1Small: Story = { + args: { + ...quoteCardProps, + style: 'primary-1', + }, + decorators: [smallDecorator], + render: (args) => , +} + +export const QuotePrimary2Small: Story = { + args: { + ...quoteCardProps, + style: 'primary-2', + }, + decorators: [smallDecorator], + render: (args) => , +} + +export const TextPrimary1Small: Story = { + args: { + ...textCardProps, + style: 'primary-1', + }, + decorators: [smallDecorator], + render: (args) => , +} + +export const TextPrimary2Small: Story = { + args: { + ...textCardProps, + style: 'primary-2', + }, + decorators: [smallDecorator], + render: (args) => , +} + +export const QuotePrimary1Large: Story = { + args: { + ...quoteCardProps, + style: 'primary-1', + }, + decorators: [largeDecorator], + render: (args) => , +} + +export const QuotePrimary2Large: Story = { + args: { + ...quoteCardProps, + style: 'primary-2', + }, + decorators: [largeDecorator], + render: (args) => , +} + +export const TextPrimary1Large: Story = { + args: { + ...textCardProps, + style: 'primary-1', + }, + decorators: [largeDecorator], + render: (args) => , +} + +export const TextPrimary2Large: Story = { + args: { + ...textCardProps, + style: 'primary-2', + }, + decorators: [largeDecorator], + render: (args) => , +} diff --git a/packages/design-system/lib/components/VideoPlayer/VideoWithCard/index.tsx b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/index.tsx new file mode 100644 index 000000000..c2a9d58a2 --- /dev/null +++ b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/index.tsx @@ -0,0 +1,81 @@ +import { VariantProps } from 'class-variance-authority' +import { Typography } from '../../Typography' +import { variants } from './variants' + +import { VideoPlayer, VideoPlayerProps } from '..' +import styles from './videoWithCard.module.css' +interface TextCardProps { + variant: 'text' + heading: string + text?: string +} +interface QuoteCardProps { + variant: 'quote' + quote: string + author: string + authorDescription?: string +} + +type VideoWithCardProps = VariantProps & + (TextCardProps | QuoteCardProps) & { + video: Pick + } + +export function VideoWithCard(props: VideoWithCardProps) { + const { variant, style, video } = props + const classNames = variants({ + variant, + style, + }) + + return ( +
+
+ +
+ +
+
+
+ ) +} + +function CardContent(props: VideoWithCardProps) { + if (props.variant === 'quote') { + const { quote, author, authorDescription } = props + + return ( + <> + +
{quote}
+
+ + + + {author} + + {authorDescription ? ( + + {authorDescription} + + ) : null} + + + ) + } + + const { heading, text } = props + + return ( + <> + +

{heading}

+
+ {text ? ( + +

{text}

+
+ ) : null} + + ) +} diff --git a/packages/design-system/lib/components/VideoPlayer/VideoWithCard/variants.ts b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/variants.ts new file mode 100644 index 000000000..153be9705 --- /dev/null +++ b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/variants.ts @@ -0,0 +1,22 @@ +import { cva } from 'class-variance-authority' + +import styles from './videoWithCard.module.css' + +export const config = { + variants: { + variant: { + text: styles['variant-text'], + quote: styles['variant-quote'], + }, + style: { + 'primary-1': styles['style-primary-1'], + 'primary-2': styles['style-primary-2'], + }, + }, + defaultVariants: { + style: 'primary-1', + variant: 'text', + }, +} as const + +export const variants = cva(styles.card, config) diff --git a/packages/design-system/lib/components/VideoPlayer/VideoWithCard/videoWithCard.module.css b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/videoWithCard.module.css new file mode 100644 index 000000000..66bce30df --- /dev/null +++ b/packages/design-system/lib/components/VideoPlayer/VideoWithCard/videoWithCard.module.css @@ -0,0 +1,84 @@ +.videoWithCardWrapper { + width: 100%; + container-type: inline-size; + container-name: videoWithCardWrapper; + + @media screen and (min-width: 768px) { + .videoWithCard { + grid-template-columns: 1fr auto; + min-height: 261px; + gap: var(--Space-x1); + + .card { + width: 320px; + } + } + + @container videoWithCardWrapper (min-width: 793px) { + .videoWithCard { + min-height: 445px; + gap: var(--Space-x2); + + .card { + width: 391px; + } + } + } + } +} + +.videoWithCard { + display: grid; + width: 100%; + gap: var(--Space-x025); +} + +.card { + display: grid; + min-height: 200px; + height: 100%; + width: 100%; + padding: var(--Space-x3) var(--Space-x4); + align-content: center; + border-radius: var(--Corner-radius-lg); + + &.style-primary-1 { + background: var(--Surface-Brand-Primary-1-Default); + color: var(--Text-Brand-OnPrimary-1-Heading); + } + + &.style-primary-2 { + background: var(--Surface-Brand-Primary-2-Default); + color: var(--Text-Brand-OnPrimary-2-Default); + + .heading { + color: var(--Text-Brand-OnPrimary-2-Heading); + } + } + + &.variant-quote { + gap: var(--Space-x3); + + @media screen and (min-width: 768px) { + gap: var(--Space-x4); + } + } + + &.variant-text { + gap: var(--Space-x2); + } +} + +.blockquote { + &::before { + content: '“'; + } + &::after { + content: '”'; + } +} + +.cite { + font-style: normal; + display: grid; +} diff --git a/packages/design-system/lib/components/VideoPlayer/index.tsx b/packages/design-system/lib/components/VideoPlayer/index.tsx index 30616d06b..ee321472f 100644 --- a/packages/design-system/lib/components/VideoPlayer/index.tsx +++ b/packages/design-system/lib/components/VideoPlayer/index.tsx @@ -16,7 +16,7 @@ interface Caption { isDefault: boolean } -interface VideoPlayerProps extends VariantProps { +export interface VideoPlayerProps extends VariantProps { src: string className?: string captions?: Caption[] diff --git a/packages/design-system/lib/components/VideoPlayer/videoPlayer.module.css b/packages/design-system/lib/components/VideoPlayer/videoPlayer.module.css index 80553969d..933a08e1c 100644 --- a/packages/design-system/lib/components/VideoPlayer/videoPlayer.module.css +++ b/packages/design-system/lib/components/VideoPlayer/videoPlayer.module.css @@ -40,7 +40,10 @@ .video { width: 100%; height: 100%; - object-fit: cover; + + &:not(:fullscreen) { + object-fit: cover; + } } .playButton { diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 04507e35a..56f11d5e5 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -189,6 +189,7 @@ "./TripAdvisorChip": "./lib/components/TripAdvisorChip/index.tsx", "./Typography": "./lib/components/Typography/index.tsx", "./VideoPlayer": "./lib/components/VideoPlayer/index.tsx", + "./VideoWithCard": "./lib/components/VideoPlayer/VideoWithCard/index.tsx", "./base.css": "./lib/base.css", "./design-system-new-deprecated.css": "./lib/design-system-new-deprecated.css", "./downtown-camper.css": "./lib/styles/downtown-camper.css", diff --git a/packages/trpc/lib/graphql/Fragments/Blocks/Video.graphql.ts b/packages/trpc/lib/graphql/Fragments/Blocks/Video.graphql.ts new file mode 100644 index 000000000..5b18483d9 --- /dev/null +++ b/packages/trpc/lib/graphql/Fragments/Blocks/Video.graphql.ts @@ -0,0 +1,63 @@ +import { gql } from "graphql-tag" + +import { Video, VideoRef } from "../Video.graphql" + +export const Video_ContentPage = gql` + fragment Video_ContentPage on ContentPageBlocksVideo { + __typename + video { + ...Video + } + } + ${Video} +` + +export const Video_CollectionPage = gql` + fragment Video_CollectionPage on CollectionPageBlocksVideo { + __typename + video { + ...Video + } + } + ${Video} +` + +export const Video_StartPage = gql` + fragment Video_StartPage on StartPageBlocksVideo { + __typename + video { + ...Video + } + } + ${Video} +` + +export const Video_ContentPageRefs = gql` + fragment Video_ContentPageRefs on ContentPageBlocksVideo { + __typename + video { + ...VideoRef + } + } + ${VideoRef} +` + +export const Video_CollectionPageRefs = gql` + fragment Video_CollectionPageRefs on CollectionPageBlocksVideo { + __typename + video { + ...VideoRef + } + } + ${VideoRef} +` + +export const Video_StartPageRefs = gql` + fragment Video_StartPageRefs on StartPageBlocksVideo { + __typename + video { + ...VideoRef + } + } + ${VideoRef} +` diff --git a/packages/trpc/lib/graphql/Fragments/Blocks/VideoCard.graphql.ts b/packages/trpc/lib/graphql/Fragments/Blocks/VideoCard.graphql.ts new file mode 100644 index 000000000..0987e8d5b --- /dev/null +++ b/packages/trpc/lib/graphql/Fragments/Blocks/VideoCard.graphql.ts @@ -0,0 +1,166 @@ +import { gql } from "graphql-tag" + +import { System } from "../System.graphql" +import { Video, VideoRef } from "../Video.graphql" + +export const VideoQuoteCard = gql` + fragment VideoQuoteCard on VideoQuoteCard { + video { + ...Video + } + style + quote + author + author_description + } + ${Video} +` + +export const VideoTextCard = gql` + fragment VideoTextCard on VideoTextCard { + video { + ...Video + } + style + heading + text + } + ${Video} +` + +export const VideoCard_ContentPage = gql` + fragment VideoCard_ContentPage on ContentPageBlocksVideoCard { + __typename + video_card { + video_cardConnection { + edges { + node { + __typename + ...VideoQuoteCard + ...VideoTextCard + } + } + } + } + } + ${VideoQuoteCard} + ${VideoTextCard} +` + +export const VideoCard_CollectionPage = gql` + fragment VideoCard_CollectionPage on CollectionPageBlocksVideoCard { + __typename + video_card { + video_cardConnection { + edges { + node { + __typename + ...VideoQuoteCard + ...VideoTextCard + } + } + } + } + } + ${VideoQuoteCard} + ${VideoTextCard} +` + +export const VideoCard_StartPage = gql` + fragment VideoCard_StartPage on StartPageBlocksVideoCard { + __typename + video_card { + video_cardConnection { + edges { + node { + __typename + ...VideoQuoteCard + ...VideoTextCard + } + } + } + } + } + ${VideoQuoteCard} + ${VideoTextCard} +` + +export const VideoQuoteCardRef = gql` + fragment VideoQuoteCardRef on VideoQuoteCard { + video { + ...VideoRef + } + system { + ...System + } + } + ${VideoRef} + ${System} +` + +export const VideoTextCardRef = gql` + fragment VideoTextCardRef on VideoTextCard { + video { + ...VideoRef + } + system { + ...System + } + } + ${VideoRef} + ${System} +` + +export const VideoCard_ContentPageRefs = gql` + fragment VideoCard_ContentPageRefs on ContentPageBlocksVideoCard { + video_card { + video_cardConnection { + edges { + node { + __typename + ...VideoQuoteCardRef + ...VideoTextCardRef + } + } + } + } + } + ${VideoQuoteCardRef} + ${VideoTextCardRef} +` + +export const VideoCard_CollectionPageRefs = gql` + fragment VideoCard_CollectionPageRefs on CollectionPageBlocksVideoCard { + video_card { + video_cardConnection { + edges { + node { + __typename + ...VideoQuoteCardRef + ...VideoTextCardRef + } + } + } + } + } + ${VideoQuoteCardRef} + ${VideoTextCardRef} +` + +export const VideoCard_StartPageRefs = gql` + fragment VideoCard_StartPageRefs on StartPageBlocksVideoCard { + video_card { + video_cardConnection { + edges { + node { + __typename + ...VideoQuoteCardRef + ...VideoTextCardRef + } + } + } + } + } + ${VideoQuoteCardRef} + ${VideoTextCardRef} +` diff --git a/packages/trpc/lib/graphql/Query/CollectionPage/CollectionPage.graphql.ts b/packages/trpc/lib/graphql/Query/CollectionPage/CollectionPage.graphql.ts index c29984126..6b3093ce1 100644 --- a/packages/trpc/lib/graphql/Query/CollectionPage/CollectionPage.graphql.ts +++ b/packages/trpc/lib/graphql/Query/CollectionPage/CollectionPage.graphql.ts @@ -16,6 +16,14 @@ import { UspGrid_CollectionPage, UspGrid_CollectionPageRefs, } from "../../Fragments/Blocks/UspGrid.graphql" +import { + Video_CollectionPage, + Video_CollectionPageRefs, +} from "../../Fragments/Blocks/Video.graphql" +import { + VideoCard_CollectionPage, + VideoCard_CollectionPageRefs, +} from "../../Fragments/Blocks/VideoCard.graphql" import { NavigationLinks_CollectionPage, NavigationLinksRef_CollectionPage, @@ -51,6 +59,8 @@ export const GetCollectionPage = gql` ...Shortcuts_CollectionPage ...UspGrid_CollectionPage ...DynamicContent_CollectionPage + ...VideoCard_CollectionPage + ...Video_CollectionPage } system { ...System @@ -62,13 +72,16 @@ export const GetCollectionPage = gql` url } } + ${System} ${TopPrimaryButton_CollectionPage} ${NavigationLinks_CollectionPage} ${CardsGrid_CollectionPage} ${Shortcuts_CollectionPage} ${UspGrid_CollectionPage} ${DynamicContent_CollectionPage} + ${VideoCard_CollectionPage} ${Video} + ${Video_CollectionPage} ` export const GetCollectionPageRefs = gql` @@ -87,6 +100,8 @@ export const GetCollectionPageRefs = gql` ...Shortcuts_CollectionPageRefs ...UspGrid_CollectionPageRefs ...DynamicContent_CollectionPageRefs + ...VideoCard_CollectionPageRefs + ...Video_CollectionPageRefs } system { ...System @@ -101,6 +116,8 @@ export const GetCollectionPageRefs = gql` ${UspGrid_CollectionPageRefs} ${DynamicContent_CollectionPageRefs} ${VideoRef} + ${VideoCard_CollectionPageRefs} + ${Video_CollectionPageRefs} ` export const GetDaDeEnUrlsCollectionPage = gql` diff --git a/packages/trpc/lib/graphql/Query/ContentPage/ContentPage.graphql.ts b/packages/trpc/lib/graphql/Query/ContentPage/ContentPage.graphql.ts index 6b75e92d5..9f2a8a6c2 100644 --- a/packages/trpc/lib/graphql/Query/ContentPage/ContentPage.graphql.ts +++ b/packages/trpc/lib/graphql/Query/ContentPage/ContentPage.graphql.ts @@ -30,6 +30,14 @@ import { UspGrid_ContentPage, UspGrid_ContentPageRefs, } from "../../Fragments/Blocks/UspGrid.graphql" +import { + Video_ContentPage, + Video_ContentPageRefs, +} from "../../Fragments/Blocks/Video.graphql" +import { + VideoCard_ContentPage, + VideoCard_ContentPageRefs, +} from "../../Fragments/Blocks/VideoCard.graphql" import { NavigationLinks_ContentPage, NavigationLinksRef_ContentPage, @@ -126,6 +134,8 @@ export const GetContentPageBlocksBatch1 = gql` ...CardsGrid_ContentPage ...Content_ContentPage ...DynamicContent_ContentPage + ...VideoCard_ContentPage + ...Video_ContentPage } } } @@ -133,6 +143,8 @@ export const GetContentPageBlocksBatch1 = gql` ${CardsGrid_ContentPage} ${Content_ContentPage} ${DynamicContent_ContentPage} + ${VideoCard_ContentPage} + ${Video_ContentPage} ` export const GetContentPageBlocksBatch2 = gql` @@ -204,6 +216,8 @@ export const GetContentPageBlocksRefs = gql` ...Shortcuts_ContentPageRefs ...TextCols_ContentPageRef ...UspGrid_ContentPageRefs + ...VideoCard_ContentPageRefs + ...Video_ContentPageRefs } } } @@ -214,6 +228,8 @@ export const GetContentPageBlocksRefs = gql` ${Shortcuts_ContentPageRefs} ${TextCols_ContentPageRef} ${UspGrid_ContentPageRefs} + ${VideoCard_ContentPageRefs} + ${Video_ContentPageRefs} ` export const GetDaDeEnUrlsContentPage = gql` diff --git a/packages/trpc/lib/graphql/Query/StartPage/StartPage.graphql.ts b/packages/trpc/lib/graphql/Query/StartPage/StartPage.graphql.ts index e7070cdad..3c9ffa143 100644 --- a/packages/trpc/lib/graphql/Query/StartPage/StartPage.graphql.ts +++ b/packages/trpc/lib/graphql/Query/StartPage/StartPage.graphql.ts @@ -16,6 +16,14 @@ import { JoinScandicFriends_StartPage, JoinScandicFriends_StartPageRefs, } from "../../Fragments/Blocks/JoinScandicFriends.graphql" +import { + Video_StartPage, + Video_StartPageRefs, +} from "../../Fragments/Blocks/Video.graphql" +import { + VideoCard_StartPage, + VideoCard_StartPageRefs, +} from "../../Fragments/Blocks/VideoCard.graphql" import { System } from "../../Fragments/System.graphql" export const GetStartPage = gql` @@ -44,6 +52,8 @@ export const GetStartPage = gql` } } ...JoinScandicFriends_StartPage + ...VideoCard_StartPage + ...Video_StartPage } system { ...System @@ -60,6 +70,8 @@ export const GetStartPage = gql` ${FullWidthCampaign} ${CarouselCards_StartPage} ${JoinScandicFriends_StartPage} + ${VideoCard_StartPage} + ${Video_StartPage} ` export const GetStartPageRefs = gql` @@ -81,6 +93,8 @@ export const GetStartPageRefs = gql` } } ...JoinScandicFriends_StartPageRefs + ...VideoCard_StartPageRefs + ...Video_StartPageRefs } system { ...System @@ -92,6 +106,8 @@ export const GetStartPageRefs = gql` ${FullWidthCampaignRefs} ${CarouselCards_StartPageRefs} ${JoinScandicFriends_StartPageRefs} + ${VideoCard_StartPageRefs} + ${Video_StartPageRefs} ` export const GetDaDeEnUrlsStartPage = gql` diff --git a/packages/trpc/lib/routers/contentstack/collectionPage/output.ts b/packages/trpc/lib/routers/contentstack/collectionPage/output.ts index d2c76eb6a..8c66a1b90 100644 --- a/packages/trpc/lib/routers/contentstack/collectionPage/output.ts +++ b/packages/trpc/lib/routers/contentstack/collectionPage/output.ts @@ -17,6 +17,11 @@ import { shortcutsSchema, } from "../schemas/blocks/shortcuts" import { uspGridRefsSchema, uspGridSchema } from "../schemas/blocks/uspGrid" +import { videoBlockRefsSchema, videoBlockSchema } from "../schemas/blocks/video" +import { + videoCardRefsSchema, + videoCardSchema, +} from "../schemas/blocks/videoCard" import { linkAndTitleSchema, linkConnectionRefs, @@ -52,11 +57,25 @@ export const collectionPageDynamicContent = z }) .merge(blockDynamicContentSchema) +export const collectionPageVideoCard = z + .object({ + __typename: z.literal(CollectionPageEnum.ContentStack.blocks.VideoCard), + }) + .merge(videoCardSchema) + +export const collectionPageVideo = z + .object({ + __typename: z.literal(CollectionPageEnum.ContentStack.blocks.Video), + }) + .merge(videoBlockSchema) + export const blocksSchema = z.discriminatedUnion("__typename", [ collectionPageCards, collectionPageDynamicContent, collectionPageShortcuts, collectionPageUspGrid, + collectionPageVideoCard, + collectionPageVideo, ]) const navigationLinksSchema = z @@ -125,7 +144,7 @@ const collectionPageUspGridRefs = z }) .merge(uspGridRefsSchema) -const contentPageDynamicContentRefs = z +const collectionPageDynamicContentRefs = z .object({ __typename: z.literal( CollectionPageEnum.ContentStack.blocks.DynamicContent @@ -133,11 +152,25 @@ const contentPageDynamicContentRefs = z }) .merge(dynamicContentRefsSchema) +const collectionPageVideoCardRefs = z + .object({ + __typename: z.literal(CollectionPageEnum.ContentStack.blocks.VideoCard), + }) + .merge(videoCardRefsSchema) + +const collectionPageVideoRefs = z + .object({ + __typename: z.literal(CollectionPageEnum.ContentStack.blocks.Video), + }) + .merge(videoBlockRefsSchema) + const collectionPageBlockRefsItem = z.discriminatedUnion("__typename", [ collectionPageShortcutsRefs, - contentPageDynamicContentRefs, + collectionPageDynamicContentRefs, collectionPageCardsRefs, collectionPageUspGridRefs, + collectionPageVideoCardRefs, + collectionPageVideoRefs, ]) const collectionPageHeaderRefs = z.object({ diff --git a/packages/trpc/lib/routers/contentstack/collectionPage/utils.ts b/packages/trpc/lib/routers/contentstack/collectionPage/utils.ts index dc15e7344..d5a987f66 100644 --- a/packages/trpc/lib/routers/contentstack/collectionPage/utils.ts +++ b/packages/trpc/lib/routers/contentstack/collectionPage/utils.ts @@ -18,7 +18,7 @@ import { collectionPageRefsSchema } from "./output" import type { Lang } from "@scandic-hotels/common/constants/language" -import type { System } from "../schemas/system" +import type { AssetSystem, System } from "../schemas/system" export async function fetchCollectionPageRefs(lang: Lang, uid: string) { const getCollectionPageRefsCounter = createCounter( @@ -83,35 +83,81 @@ export function generatePageTags( lang: Lang ): string[] { const connections = getConnections(validatedData) + const assetConnections = getConnectionsFromAssets(validatedData) return [ generateTagsFromSystem(lang, connections), - generateTagsFromAssetSystem(connections), + generateTagsFromAssetSystem(assetConnections), generateTag(lang, validatedData.collection_page.system.uid), ].flat() } -export function getConnections({ collection_page }: CollectionPageRefs) { - const connections: System["system"][] = [collection_page.system] +export function getConnectionsFromAssets({ + collection_page, +}: CollectionPageRefs) { + const connections: AssetSystem["system"][] = [] + + if (collection_page.hero_video?.sourceConnection.edges[0]) { + connections.push( + collection_page.hero_video.sourceConnection.edges[0].node.system + ) + } + if (collection_page.blocks) { collection_page.blocks.forEach((block) => { switch (block.__typename) { - case CollectionPageEnum.ContentStack.blocks.Shortcuts: { - if (block.shortcuts.shortcuts.length) { - connections.push(...block.shortcuts.shortcuts.filter((c) => !!c)) + case CollectionPageEnum.ContentStack.blocks.VideoCard: + if (block.video_card?.video.sourceConnection.edges[0]) { + connections.push( + block.video_card.video.sourceConnection.edges[0].node.system + ) } break - } - case CollectionPageEnum.ContentStack.blocks.CardsGrid: { - if (block.cards_grid.length) { - connections.push(...block.cards_grid) + case CollectionPageEnum.ContentStack.blocks.Video: + if (block.video?.sourceConnection.edges[0]) { + connections.push(block.video.sourceConnection.edges[0].node.system) } break - } - case CollectionPageEnum.ContentStack.blocks.UspGrid: { - if (block.usp_grid.length) { - connections.push(...block.usp_grid.filter((c) => !!c)) - } - } + default: + break + } + }) + } + + return connections +} + +export function getConnections({ collection_page }: CollectionPageRefs) { + const connections: System["system"][] = [collection_page.system] + if (collection_page.blocks) { + collection_page.blocks.forEach((block) => { + const typeName = block.__typename + switch (typeName) { + case CollectionPageEnum.ContentStack.blocks.Shortcuts: + if (block.shortcuts.shortcuts.length) { + connections.push(...block.shortcuts.shortcuts.filter((c) => !!c)) + } + break + case CollectionPageEnum.ContentStack.blocks.CardsGrid: + if (block.cards_grid.length) { + connections.push(...block.cards_grid) + } + break + case CollectionPageEnum.ContentStack.blocks.UspGrid: + if (block.usp_grid.length) { + connections.push(...block.usp_grid.filter((c) => !!c)) + } + break + case CollectionPageEnum.ContentStack.blocks.VideoCard: + if (block.video_card?.system) { + connections.push(block.video_card.system) + } + break + case CollectionPageEnum.ContentStack.blocks.DynamicContent: + case CollectionPageEnum.ContentStack.blocks.Video: + break + default: + const _exhaustiveCheck: never = typeName + break } }) } diff --git a/packages/trpc/lib/routers/contentstack/contentPage/output.ts b/packages/trpc/lib/routers/contentstack/contentPage/output.ts index 6445421af..4d5b1bb0c 100644 --- a/packages/trpc/lib/routers/contentstack/contentPage/output.ts +++ b/packages/trpc/lib/routers/contentstack/contentPage/output.ts @@ -28,6 +28,11 @@ import { import { tableSchema } from "../schemas/blocks/table" import { textColsRefsSchema, textColsSchema } from "../schemas/blocks/textCols" import { uspGridRefsSchema, uspGridSchema } from "../schemas/blocks/uspGrid" +import { videoBlockRefsSchema, videoBlockSchema } from "../schemas/blocks/video" +import { + videoCardRefsSchema, + videoCardSchema, +} from "../schemas/blocks/videoCard" import { dynamicContentRefsSchema as headerDynamicContentRefsSchema, dynamicContentSchema as headerDynamicContentSchema, @@ -116,6 +121,18 @@ export const contentPageHotelListing = z }) .merge(contentPageHotelListingSchema) +export const contentPageVideoCard = z + .object({ + __typename: z.literal(ContentPageEnum.ContentStack.blocks.VideoCard), + }) + .merge(videoCardSchema) + +export const contentPageVideo = z + .object({ + __typename: z.literal(ContentPageEnum.ContentStack.blocks.Video), + }) + .merge(videoBlockSchema) + export const blocksSchema = z.discriminatedUnion("__typename", [ contentPageAccordion, contentPageCards, @@ -126,6 +143,8 @@ export const blocksSchema = z.discriminatedUnion("__typename", [ contentPageTextCols, contentPageUspGrid, contentPageHotelListing, + contentPageVideoCard, + contentPageVideo, ]) export const contentPageSidebarContent = z @@ -267,6 +286,18 @@ const contentPageAccordionRefs = z }) .merge(accordionRefsSchema) +const contentPageVideoCardRefs = z + .object({ + __typename: z.literal(ContentPageEnum.ContentStack.blocks.VideoCard), + }) + .merge(videoCardRefsSchema) + +const contentPageVideoRefs = z + .object({ + __typename: z.literal(ContentPageEnum.ContentStack.blocks.Video), + }) + .merge(videoBlockRefsSchema) + const contentPageBlockRefsItem = z.discriminatedUnion("__typename", [ contentPageAccordionRefs, contentPageBlockContentRefs, @@ -275,6 +306,8 @@ const contentPageBlockRefsItem = z.discriminatedUnion("__typename", [ contentPageDynamicContentRefs, contentPageTextColsRefs, contentPageUspGridRefs, + contentPageVideoCardRefs, + contentPageVideoRefs, ]) const contentPageSidebarContentRef = z diff --git a/packages/trpc/lib/routers/contentstack/contentPage/utils.ts b/packages/trpc/lib/routers/contentstack/contentPage/utils.ts index 0168d810a..521240d28 100644 --- a/packages/trpc/lib/routers/contentstack/contentPage/utils.ts +++ b/packages/trpc/lib/routers/contentstack/contentPage/utils.ts @@ -21,7 +21,7 @@ import type { ContentPageRefs, GetContentPageRefsSchema, } from "../../../types/contentPage" -import type { System } from "../schemas/system" +import type { AssetSystem, System } from "../schemas/system" export async function fetchContentPageRefs(lang: Lang, uid: string) { const getContentPageRefsCounter = createCounter( @@ -74,25 +74,58 @@ export function generatePageTags( lang: Lang ): string[] { const connections = getConnections(validatedData) + const assetConnections = getConnectionsFromAssets(validatedData) return [ generateTagsFromSystem(lang, connections), - generateTagsFromAssetSystem(connections), + generateTagsFromAssetSystem(assetConnections), generateTag(lang, validatedData.content_page.system.uid), ].flat() } +export function getConnectionsFromAssets({ content_page }: ContentPageRefs) { + const connections: AssetSystem["system"][] = [] + + if (content_page.hero_video?.sourceConnection.edges[0]) { + connections.push( + content_page.hero_video.sourceConnection.edges[0].node.system + ) + } + + if (content_page.blocks) { + content_page.blocks.forEach((block) => { + switch (block.__typename) { + case ContentPageEnum.ContentStack.blocks.VideoCard: + if (block.video_card?.video.sourceConnection.edges[0]) { + connections.push( + block.video_card.video.sourceConnection.edges[0].node.system + ) + } + break + case ContentPageEnum.ContentStack.blocks.Video: + if (block.video?.sourceConnection.edges[0]) { + connections.push(block.video.sourceConnection.edges[0].node.system) + } + break + default: + break + } + }) + } + return connections +} + export function getConnections({ content_page }: ContentPageRefs) { const connections: System["system"][] = [content_page.system] if (content_page.blocks) { content_page.blocks.forEach((block) => { - switch (block.__typename) { - case ContentPageEnum.ContentStack.blocks.Accordion: { + const typeName = block.__typename + switch (typeName) { + case ContentPageEnum.ContentStack.blocks.Accordion: if (block.accordion.length) { connections.push(...block.accordion.filter((c) => !!c)) } break - } case ContentPageEnum.ContentStack.blocks.Content: { if (block?.content?.length) { @@ -100,51 +133,56 @@ export function getConnections({ content_page }: ContentPageRefs) { } } break - case ContentPageEnum.ContentStack.blocks.CardsGrid: { + case ContentPageEnum.ContentStack.blocks.CardsGrid: if (block.cards_grid.length) { connections.push(...block.cards_grid) } break - } - case ContentPageEnum.ContentStack.blocks.DynamicContent: { + case ContentPageEnum.ContentStack.blocks.DynamicContent: if (block.dynamic_content.link) { connections.push(block.dynamic_content.link) } break - } - case ContentPageEnum.ContentStack.blocks.Shortcuts: { + case ContentPageEnum.ContentStack.blocks.Shortcuts: if (block.shortcuts.shortcuts.length) { connections.push(...block.shortcuts.shortcuts.filter((c) => !!c)) } break - } - case ContentPageEnum.ContentStack.blocks.TextCols: { + case ContentPageEnum.ContentStack.blocks.TextCols: if (block.text_cols.length) { connections.push(...block.text_cols) } break - } - case ContentPageEnum.ContentStack.blocks.UspGrid: { + case ContentPageEnum.ContentStack.blocks.UspGrid: if (block.usp_grid.length) { connections.push(...block.usp_grid.filter((c) => !!c)) } break - } - case ContentPageEnum.ContentStack.blocks.CardsGrid: { + case ContentPageEnum.ContentStack.blocks.CardsGrid: if (block.cards_grid.length) { block.cards_grid.forEach((card) => { connections.push(card) }) } break - } + case ContentPageEnum.ContentStack.blocks.VideoCard: + if (block.video_card) { + connections.push(block.video_card.system) + } + break + case ContentPageEnum.ContentStack.blocks.Video: + break + default: + const _exhaustiveCheck: never = typeName + break } }) } if (content_page.sidebar) { content_page.sidebar.forEach((block) => { - switch (block.__typename) { + const typeName = block.__typename + switch (typeName) { case ContentPageEnum.ContentStack.sidebar.Content: if (block.content.length) { connections.push(...block.content.filter((c) => !!c)) @@ -171,6 +209,7 @@ export function getConnections({ content_page }: ContentPageRefs) { } break default: + const _exhaustiveCheck: never = typeName break } }) diff --git a/packages/trpc/lib/routers/contentstack/schemas/blocks/video.ts b/packages/trpc/lib/routers/contentstack/schemas/blocks/video.ts new file mode 100644 index 000000000..3326a58e7 --- /dev/null +++ b/packages/trpc/lib/routers/contentstack/schemas/blocks/video.ts @@ -0,0 +1,14 @@ +import { z } from "zod" + +import { BlocksEnums } from "../../../../types/blocksEnum" +import { transformedVideoSchema, videoRefSchema } from "../video" + +export const videoBlockSchema = z.object({ + typename: z.literal(BlocksEnums.block.Video).default(BlocksEnums.block.Video), + video: transformedVideoSchema, +}) + +export const videoBlockRefsSchema = z.object({ + typename: z.literal(BlocksEnums.block.Video).default(BlocksEnums.block.Video), + video: videoRefSchema, +}) diff --git a/packages/trpc/lib/routers/contentstack/schemas/blocks/videoCard.ts b/packages/trpc/lib/routers/contentstack/schemas/blocks/videoCard.ts new file mode 100644 index 000000000..ca881c1b3 --- /dev/null +++ b/packages/trpc/lib/routers/contentstack/schemas/blocks/videoCard.ts @@ -0,0 +1,117 @@ +import { z } from "zod" + +import { logger } from "@scandic-hotels/common/logger" + +import { BlocksEnums } from "../../../../types/blocksEnum" +import { systemSchema } from "../system" +import { transformedVideoSchema, videoRefSchema } from "../video" + +const cardStyleSchema = z + .string() + .transform((val): "primary-1" | "primary-2" => { + if (val === "primary-1" || val === "primary-2") { + return val + } + return "primary-1" + }) + +export const videoQuoteCardSchema = z.object({ + __typename: z.literal("VideoQuoteCard"), + video: transformedVideoSchema, + style: cardStyleSchema, + quote: z.string(), + author: z.string(), + author_description: z.string().nullish(), +}) + +export const videoTextCardSchema = z.object({ + __typename: z.literal("VideoTextCard"), + video: transformedVideoSchema, + style: cardStyleSchema, + heading: z.string(), + text: z.string().nullish(), +}) + +export const videoCardSchema = z.object({ + typename: z + .literal(BlocksEnums.block.VideoCard) + .default(BlocksEnums.block.VideoCard), + video_card: z + .object({ + video_cardConnection: z.object({ + edges: z.array( + z.object({ + node: z.discriminatedUnion("__typename", [ + videoQuoteCardSchema, + videoTextCardSchema, + ]), + }) + ), + }), + }) + .transform((data) => { + const videoCard = data.video_cardConnection.edges[0]?.node + if (!videoCard?.video) { + return null + } + + const { __typename, style, video } = videoCard + + switch (__typename) { + case "VideoTextCard": { + const { heading, text } = videoCard + return { + variant: "text" as const, + style, + video, + heading, + text: text || undefined, + } + } + case "VideoQuoteCard": { + const { quote, author, author_description } = videoCard + return { + variant: "quote" as const, + style, + video, + quote, + author, + authorDescription: author_description || undefined, + } + } + default: + const type: never = __typename + logger.error(`Unsupported content type given: ${type}`) + return null + } + }), +}) + +const videoCardRefSchema = z.object({ + video: videoRefSchema, + system: systemSchema, +}) + +export const videoCardRefsSchema = z.object({ + typename: z + .literal(BlocksEnums.block.VideoCard) + .default(BlocksEnums.block.VideoCard), + video_card: z + .object({ + video_cardConnection: z.object({ + edges: z.array( + z.object({ + node: videoCardRefSchema, + }) + ), + }), + }) + .transform((data) => { + const videoCard = data.video_cardConnection.edges[0]?.node + if (!videoCard?.video) { + return null + } + + return videoCard + }), +}) diff --git a/packages/trpc/lib/routers/contentstack/startPage/output.ts b/packages/trpc/lib/routers/contentstack/startPage/output.ts index 10980da55..4c2393049 100644 --- a/packages/trpc/lib/routers/contentstack/startPage/output.ts +++ b/packages/trpc/lib/routers/contentstack/startPage/output.ts @@ -19,6 +19,11 @@ import { joinScandicFriendsBlockRefsSchema, joinScandicFriendsBlockSchema, } from "../schemas/blocks/joinScandicFriends" +import { videoBlockRefsSchema, videoBlockSchema } from "../schemas/blocks/video" +import { + videoCardRefsSchema, + videoCardSchema, +} from "../schemas/blocks/videoCard" import { systemSchema } from "../schemas/system" import { StartPageEnum } from "./utils" @@ -46,11 +51,25 @@ const startPageJoinScandicFriends = z }) .merge(joinScandicFriendsBlockSchema) +const startPageVideoCard = z + .object({ + __typename: z.literal(StartPageEnum.ContentStack.blocks.VideoCard), + }) + .merge(videoCardSchema) + +const startPageVideo = z + .object({ + __typename: z.literal(StartPageEnum.ContentStack.blocks.Video), + }) + .merge(videoBlockSchema) + export const blocksSchema = z.discriminatedUnion("__typename", [ startPageCards, startPageFullWidthCampaign, startPageCarouselCards, startPageJoinScandicFriends, + startPageVideoCard, + startPageVideo, ]) export const startPageSchema = z.object({ @@ -100,11 +119,25 @@ const startPageJoinScandicFriendsRef = z }) .merge(joinScandicFriendsBlockRefsSchema) +const startPageVideoCardRef = z + .object({ + __typename: z.literal(StartPageEnum.ContentStack.blocks.VideoCard), + }) + .merge(videoCardRefsSchema) + +const startPageVideoRef = z + .object({ + __typename: z.literal(StartPageEnum.ContentStack.blocks.Video), + }) + .merge(videoBlockRefsSchema) + const startPageBlockRefsItem = z.discriminatedUnion("__typename", [ startPageCardsRefs, startPageFullWidthCampaignRef, startPageCarouselCardsRef, startPageJoinScandicFriendsRef, + startPageVideoCardRef, + startPageVideoRef, ]) export const startPageRefsSchema = z.object({ diff --git a/packages/trpc/lib/routers/contentstack/startPage/query.ts b/packages/trpc/lib/routers/contentstack/startPage/query.ts index b79d0eb68..b275a81e3 100644 --- a/packages/trpc/lib/routers/contentstack/startPage/query.ts +++ b/packages/trpc/lib/routers/contentstack/startPage/query.ts @@ -11,10 +11,11 @@ import { contentstackExtendedProcedureUID } from "../../../procedures" import { generateRefsResponseTag, generateTag, + generateTagsFromAssetSystem, generateTagsFromSystem, } from "../../../utils/generateTag" import { startPageRefsSchema, startPageSchema } from "./output" -import { getConnections } from "./utils" +import { getConnections, getConnectionsFromAssets } from "./utils" import type { z } from "zod" @@ -73,9 +74,11 @@ export const startPageQueryRouter = router({ metricsGetStartPage.start() const connections = getConnections(validatedRefsData.data) + const assetConnections = getConnectionsFromAssets(validatedRefsData.data) const tags = [ generateTagsFromSystem(lang, connections), + generateTagsFromAssetSystem(assetConnections), generateTag(lang, validatedRefsData.data.start_page.system.uid), ].flat() diff --git a/packages/trpc/lib/routers/contentstack/startPage/utils.ts b/packages/trpc/lib/routers/contentstack/startPage/utils.ts index bd511026f..b5992ba39 100644 --- a/packages/trpc/lib/routers/contentstack/startPage/utils.ts +++ b/packages/trpc/lib/routers/contentstack/startPage/utils.ts @@ -1,6 +1,6 @@ import type { z } from "zod" -import type { System } from "../schemas/system" +import type { AssetSystem, System } from "../schemas/system" import type { startPageRefsSchema } from "./output" export namespace StartPageEnum { @@ -10,12 +10,40 @@ export namespace StartPageEnum { CarouselCards = "StartPageBlocksCarouselCards", FullWidthCampaign = "StartPageBlocksFullWidthCampaign", JoinScandicFriends = "StartPageBlocksJoinScandicFriends", + VideoCard = "StartPageBlocksVideoCard", + Video = "StartPageBlocksVideo", } } } export interface StartPageRefs extends z.output {} +export function getConnectionsFromAssets({ start_page }: StartPageRefs) { + const connections: AssetSystem["system"][] = [] + + if (start_page.blocks) { + start_page.blocks.forEach((block) => { + switch (block.__typename) { + case StartPageEnum.ContentStack.blocks.VideoCard: + if (block.video_card?.video.sourceConnection.edges[0]) { + connections.push( + block.video_card.video.sourceConnection.edges[0].node.system + ) + } + break + case StartPageEnum.ContentStack.blocks.Video: + if (block.video?.sourceConnection.edges[0]) { + connections.push(block.video.sourceConnection.edges[0].node.system) + } + break + default: + break + } + }) + } + return connections +} + export function getConnections({ start_page }: StartPageRefs) { const connections: System["system"][] = [start_page.system] @@ -23,7 +51,7 @@ export function getConnections({ start_page }: StartPageRefs) { start_page.blocks.forEach((block) => { const typeName = block.__typename switch (typeName) { - case StartPageEnum.ContentStack.blocks.FullWidthCampaign: { + case StartPageEnum.ContentStack.blocks.FullWidthCampaign: block.full_width_campaign.full_width_campaignConnection.edges.forEach( ({ node }) => { if (node.system) { @@ -32,31 +60,34 @@ export function getConnections({ start_page }: StartPageRefs) { } ) break - } - case StartPageEnum.ContentStack.blocks.CardsGrid: { + case StartPageEnum.ContentStack.blocks.CardsGrid: block.cards_grid.forEach((card) => { connections.push(card) }) break - } - case StartPageEnum.ContentStack.blocks.CarouselCards: { + case StartPageEnum.ContentStack.blocks.CarouselCards: block.carousel_cards.card_groups.forEach((group) => { group.cardConnection.edges.forEach((node) => { connections.push(node.node.system) }) }) break - } - case StartPageEnum.ContentStack.blocks.JoinScandicFriends: { + case StartPageEnum.ContentStack.blocks.JoinScandicFriends: if (block.join_scandic_friends.primary_button) { connections.push(block.join_scandic_friends.primary_button) } break - } - default: { - const _exhaustiveCheck: never = typeName + case StartPageEnum.ContentStack.blocks.VideoCard: { + if (block.video_card?.system) { + connections.push(block.video_card.system) + } break } + case StartPageEnum.ContentStack.blocks.Video: + break + default: + const _exhaustiveCheck: never = typeName + break } }) } diff --git a/packages/trpc/lib/types/blocks.ts b/packages/trpc/lib/types/blocks.ts index 93eadb3db..e987f8c51 100644 --- a/packages/trpc/lib/types/blocks.ts +++ b/packages/trpc/lib/types/blocks.ts @@ -9,8 +9,11 @@ import type { shortcutsSchema } from "@scandic-hotels/trpc/routers/contentstack/ import type { tableSchema } from "@scandic-hotels/trpc/routers/contentstack/schemas/blocks/table" import type { textColsSchema } from "@scandic-hotels/trpc/routers/contentstack/schemas/blocks/textCols" import type { uspGridSchema } from "@scandic-hotels/trpc/routers/contentstack/schemas/blocks/uspGrid" +import type { videoCardSchema } from "@scandic-hotels/trpc/routers/contentstack/schemas/blocks/videoCard" import type { z } from "zod" +import type { videoBlockSchema } from "../routers/contentstack/schemas/blocks/video" + export interface TeaserCard extends z.output {} export interface CardsGrid extends z.output {} export interface Content extends z.output {} @@ -26,3 +29,5 @@ interface GetHotelListing export type HotelListing = GetHotelListing["hotel_listing"] export interface CarouselCards extends z.output {} export interface CardGallery extends z.output {} +export interface VideoCard extends z.output {} +export interface VideoBlock extends z.output {} diff --git a/packages/trpc/lib/types/blocksEnum.ts b/packages/trpc/lib/types/blocksEnum.ts index b0eb5c8eb..08599d914 100644 --- a/packages/trpc/lib/types/blocksEnum.ts +++ b/packages/trpc/lib/types/blocksEnum.ts @@ -18,5 +18,7 @@ export namespace BlocksEnums { TextContent = "TextContent", UspGrid = "UspGrid", Essentials = "Essentials", + VideoCard = "VideoCard", + Video = "Video", } } diff --git a/packages/trpc/lib/types/collectionPage.ts b/packages/trpc/lib/types/collectionPage.ts index dd9014843..b50aa5bf3 100644 --- a/packages/trpc/lib/types/collectionPage.ts +++ b/packages/trpc/lib/types/collectionPage.ts @@ -26,6 +26,8 @@ export namespace CollectionPageEnum { DynamicContent = "CollectionPageBlocksDynamicContent", Shortcuts = "CollectionPageBlocksShortcuts", UspGrid = "CollectionPageBlocksUspGrid", + VideoCard = "CollectionPageBlocksVideoCard", + Video = "CollectionPageBlocksVideo", } } } diff --git a/packages/trpc/lib/types/contentPage.ts b/packages/trpc/lib/types/contentPage.ts index 8d3b1a717..44055ee64 100644 --- a/packages/trpc/lib/types/contentPage.ts +++ b/packages/trpc/lib/types/contentPage.ts @@ -36,6 +36,8 @@ export namespace ContentPageEnum { UspGrid = "ContentPageBlocksUspGrid", Table = "ContentPageBlocksTable", HotelListing = "ContentPageBlocksHotelListing", + VideoCard = "ContentPageBlocksVideoCard", + Video = "ContentPageBlocksVideo", } export const enum sidebar {