diff --git a/components/Blocks/FullWidthCampaign/fullWidthCampaign.module.css b/components/Blocks/FullWidthCampaign/fullWidthCampaign.module.css
new file mode 100644
index 000000000..efdee8666
--- /dev/null
+++ b/components/Blocks/FullWidthCampaign/fullWidthCampaign.module.css
@@ -0,0 +1,41 @@
+.container {
+ position: relative;
+ overflow: hidden;
+ height: 640px;
+}
+
+@media screen and (min-width: 768px) {
+ .container {
+ height: 880px;
+ }
+}
+
+.content {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ max-width: 800px;
+ margin: 0 auto;
+ gap: var(--Spacing-x1);
+ padding: var(--Spacing-x4) var(--Spacing-x3);
+}
+
+.mainContent {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--Spacing-x2);
+}
+
+.buttons {
+ display: flex;
+ gap: var(--Spacing-x1);
+}
+
+.image {
+ max-width: 100%;
+ height: 100%;
+}
diff --git a/components/Blocks/FullWidthCampaign/index.tsx b/components/Blocks/FullWidthCampaign/index.tsx
new file mode 100644
index 000000000..3a7a0a1ce
--- /dev/null
+++ b/components/Blocks/FullWidthCampaign/index.tsx
@@ -0,0 +1,76 @@
+import Image from "@/components/Image"
+import Button from "@/components/TempDesignSystem/Button"
+import Link from "@/components/TempDesignSystem/Link"
+import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
+import Preamble from "@/components/TempDesignSystem/Text/Preamble"
+import Title from "@/components/TempDesignSystem/Text/Title"
+
+import styles from "./fullWidthCampaign.module.css"
+
+import type { FullWidthCampaign } from "@/types/trpc/routers/contentstack/startPage"
+
+interface FullWidthCampaignProps {
+ content: FullWidthCampaign
+}
+
+export default function FullWidthCampaign({ content }: FullWidthCampaignProps) {
+ const { background_image, primary_button, secondary_button } = content
+
+ return (
+
+ {background_image ? (
+
+ ) : null}
+
+
+ {content.scripted_top_title}
+
+
+
+ {content.heading}
+
+
+ {content.body_text}
+
+
+ {content.has_primary_button ? (
+
+ ) : null}
+ {content.has_secondary_button ? (
+
+ ) : null}
+
+
+
+
+ )
+}
diff --git a/components/Blocks/index.tsx b/components/Blocks/index.tsx
index a67a3880e..87283a5a9 100644
--- a/components/Blocks/index.tsx
+++ b/components/Blocks/index.tsx
@@ -7,6 +7,7 @@ import JsonToHtml from "@/components/JsonToHtml"
import { SasTierComparison } from "@/components/SasTierComparison"
import AccordionSection from "./Accordion"
+import FullWidthCampaign from "./FullWidthCampaign"
import HotelListing from "./HotelListing"
import Table from "./Table"
@@ -98,6 +99,8 @@ export default function Blocks({ blocks }: BlocksProps) {
firstItem={firstItem}
/>
)
+ case BlocksEnums.block.FullWidthCampaign:
+ return
default:
return null
}
diff --git a/components/ContentType/StartPage/index.tsx b/components/ContentType/StartPage/index.tsx
index 33972541f..d94a7d2ff 100644
--- a/components/ContentType/StartPage/index.tsx
+++ b/components/ContentType/StartPage/index.tsx
@@ -1,8 +1,10 @@
+import { assertNullableType } from "graphql"
import { Suspense } from "react"
import { getStartPage } from "@/lib/trpc/memoizedRequests"
import Blocks from "@/components/Blocks"
+import FullWidthCampaign from "@/components/Blocks/FullWidthCampaign"
import Image from "@/components/Image"
import PageContainer from "@/components/PageContainer"
import Title from "@/components/TempDesignSystem/Text/Title"
@@ -10,6 +12,8 @@ import TrackingSDK from "@/components/TrackingSDK"
import styles from "./startPage.module.css"
+import { BlocksEnums } from "@/types/enums/blocks"
+
export default async function StartPage() {
const content = await getStartPage()
if (!content) {
@@ -43,13 +47,24 @@ export default async function StartPage() {
) : null}
-
- JSON data
- {JSON.stringify(content, null, 2)}
-
- {blocks ? : null}
+ {blocks
+ ? blocks.map((block) => {
+ if (block.typename === BlocksEnums.block.FullWidthCampaign) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+
+ )
+ })
+ : null}
diff --git a/components/ContentType/StartPage/startPage.module.css b/components/ContentType/StartPage/startPage.module.css
index 378d16845..d69fef887 100644
--- a/components/ContentType/StartPage/startPage.module.css
+++ b/components/ContentType/StartPage/startPage.module.css
@@ -40,9 +40,19 @@
.main {
display: grid;
- width: 100%;
gap: var(--Spacing-x6);
- margin: 0 auto;
- max-width: var(--max-width-content);
- padding: var(--Spacing-x4) 0;
+ padding: calc(var(--Spacing-x5) * 2) 0 calc(var(--Spacing-x5) * 4);
+}
+
+@media screen and (min-width: 768px) {
+ .main {
+ gap: calc(var(--Spacing-x5) * 3);
+ }
+}
+
+.section {
+ margin-left: auto;
+ margin-right: auto;
+ max-width: var(--max-width-content);
+ width: 100%;
}
diff --git a/components/TempDesignSystem/Text/Preamble/preamble.module.css b/components/TempDesignSystem/Text/Preamble/preamble.module.css
index 007cfe56f..bfb796746 100644
--- a/components/TempDesignSystem/Text/Preamble/preamble.module.css
+++ b/components/TempDesignSystem/Text/Preamble/preamble.module.css
@@ -33,6 +33,10 @@
color: var(--Base-Text-UI-Medium-contrast);
}
+.baseText {
+ color: var(--Base-Text-Inverted);
+}
+
.center {
text-align: center;
}
diff --git a/components/TempDesignSystem/Text/Preamble/variants.ts b/components/TempDesignSystem/Text/Preamble/variants.ts
index 0eebf4d17..d007d985b 100644
--- a/components/TempDesignSystem/Text/Preamble/variants.ts
+++ b/components/TempDesignSystem/Text/Preamble/variants.ts
@@ -9,6 +9,7 @@ const config = {
burgundy: styles.burgundy,
pale: styles.pale,
textMediumContrast: styles.textMediumContrast,
+ baseText: styles.baseText,
},
textAlign: {
center: styles.center,
diff --git a/lib/graphql/Fragments/Blocks/FullWidthCampaign.graphql b/lib/graphql/Fragments/Blocks/FullWidthCampaign.graphql
new file mode 100644
index 000000000..2b5886ce2
--- /dev/null
+++ b/lib/graphql/Fragments/Blocks/FullWidthCampaign.graphql
@@ -0,0 +1,117 @@
+#import "../PageLink/AccountPageLink.graphql"
+#import "../PageLink/ContentPageLink.graphql"
+#import "../PageLink/LoyaltyPageLink.graphql"
+#import "../PageLink/HotelPageLink.graphql"
+#import "../PageLink/CollectionPageLink.graphql"
+#import "../PageLink/DestinationCityPageLink.graphql"
+#import "../PageLink/DestinationCountryPageLink.graphql"
+#import "../PageLink/DestinationOverviewPageLink.graphql"
+
+#import "../AccountPage/Ref.graphql"
+#import "../ContentPage/Ref.graphql"
+#import "../HotelPage/Ref.graphql"
+#import "../LoyaltyPage/Ref.graphql"
+#import "../CollectionPage/Ref.graphql"
+#import "../DestinationCityPage/Ref.graphql"
+#import "../DestinationCountryPage/Ref.graphql"
+#import "../DestinationOverviewPage/Ref.graphql"
+
+fragment FullWidthCampaign on FullWidthCampaign {
+ background_image
+ scripted_top_title
+ heading
+ body_text
+ has_primary_button
+ primary_button {
+ cta_text
+ open_in_new_tab
+ is_contentstack_link
+ external_link {
+ href
+ title
+ }
+ linkConnection {
+ edges {
+ node {
+ __typename
+ ...AccountPageLink
+ ...ContentPageLink
+ ...LoyaltyPageLink
+ ...HotelPageLink
+ ...CollectionPageLink
+ ...DestinationCityPageLink
+ ...DestinationCountryPageLink
+ ...DestinationOverviewPageLink
+ }
+ }
+ }
+ }
+ has_secondary_button
+ secondary_button {
+ cta_text
+ open_in_new_tab
+ is_contentstack_link
+ external_link {
+ href
+ title
+ }
+ linkConnection {
+ edges {
+ node {
+ __typename
+ ...AccountPageLink
+ ...ContentPageLink
+ ...LoyaltyPageLink
+ ...HotelPageLink
+ ...CollectionPageLink
+ ...DestinationCityPageLink
+ ...DestinationCountryPageLink
+ ...DestinationOverviewPageLink
+ }
+ }
+ }
+ }
+ system {
+ ...System
+ }
+}
+
+fragment FullWidthCampaignRefs on FullWidthCampaign {
+ primary_button {
+ linkConnection {
+ edges {
+ node {
+ __typename
+ ...AccountPageRef
+ ...ContentPageRef
+ ...HotelPageRef
+ ...LoyaltyPageRef
+ ...CollectionPageRef
+ ...DestinationCityPageRef
+ ...DestinationCountryPageRef
+ ...DestinationOverviewPageRef
+ }
+ }
+ }
+ }
+ secondary_button {
+ linkConnection {
+ edges {
+ node {
+ __typename
+ ...AccountPageRef
+ ...ContentPageRef
+ ...HotelPageRef
+ ...LoyaltyPageRef
+ ...CollectionPageRef
+ ...DestinationCityPageRef
+ ...DestinationCountryPageRef
+ ...DestinationOverviewPageRef
+ }
+ }
+ }
+ }
+ system {
+ ...System
+ }
+}
diff --git a/lib/graphql/Query/StartPage/StartPage.graphql b/lib/graphql/Query/StartPage/StartPage.graphql
index a3bcfea4e..4e118ceba 100644
--- a/lib/graphql/Query/StartPage/StartPage.graphql
+++ b/lib/graphql/Query/StartPage/StartPage.graphql
@@ -1,14 +1,30 @@
#import "../../Fragments/System.graphql"
#import "../../Fragments/Blocks/CardsGrid.graphql"
+#import "../../Fragments/Blocks/FullWidthCampaign.graphql"
query GetStartPage($locale: String!, $uid: String!) {
- start_page(uid: $uid, locale: $locale) {
+ start_page(locale: $locale, uid: $uid) {
title
url
header {
heading
hero_image
}
+ blocks {
+ __typename
+ ... on StartPageBlocksFullWidthCampaign {
+ __typename
+ full_width_campaign {
+ full_width_campaignConnection {
+ edges {
+ node {
+ ...FullWidthCampaign
+ }
+ }
+ }
+ }
+ }
+ }
system {
...System
created_at
@@ -29,6 +45,17 @@ query GetStartPageRefs($locale: String!, $uid: String!) {
blocks {
__typename
...CardsGrid_StartPageRefs
+ ... on StartPageBlocksFullWidthCampaign {
+ full_width_campaign {
+ full_width_campaignConnection {
+ edges {
+ node {
+ ...FullWidthCampaignRefs
+ }
+ }
+ }
+ }
+ }
}
system {
...System
diff --git a/server/routers/contentstack/schemas/blocks/fullWidthCampaign.ts b/server/routers/contentstack/schemas/blocks/fullWidthCampaign.ts
new file mode 100644
index 000000000..20a9c0962
--- /dev/null
+++ b/server/routers/contentstack/schemas/blocks/fullWidthCampaign.ts
@@ -0,0 +1,65 @@
+import { z } from "zod"
+
+import * as pageLinks from "@/server/routers/contentstack/schemas/pageLinks"
+
+import { tempImageVaultAssetSchema } from "../imageVault"
+import { systemSchema } from "../system"
+import { buttonSchema } from "./utils/buttonLinkSchema"
+
+import { BlocksEnums } from "@/types/enums/blocks"
+
+export const fullWidthCampaignSchema = z.object({
+ full_width_campaign: z
+ .object({
+ full_width_campaignConnection: z.object({
+ edges: z.array(
+ z.object({
+ node: z.object({
+ background_image: tempImageVaultAssetSchema,
+ heading: z.string().optional(),
+ body_text: z.string().optional(),
+ scripted_top_title: z.string().optional(),
+ has_primary_button: z.boolean().default(false),
+ primary_button: buttonSchema,
+ has_secondary_button: z.boolean().default(false),
+ secondary_button: buttonSchema,
+ system: systemSchema,
+ }),
+ })
+ ),
+ }),
+ })
+ .transform((data) => {
+ return data.full_width_campaignConnection.edges[0]?.node || null
+ }),
+})
+
+export const fullWidthCampaignBlockSchema = z
+ .object({
+ typename: z
+ .literal(BlocksEnums.block.FullWidthCampaign)
+ .optional()
+ .default(BlocksEnums.block.FullWidthCampaign),
+ })
+ .merge(fullWidthCampaignSchema)
+
+export const fullWidthCampaignBlockRefsSchema = z.object({
+ full_width_campaign: z.object({
+ full_width_campaignConnection: z.object({
+ edges: z.array(
+ z.object({
+ node: z.discriminatedUnion("__typename", [
+ pageLinks.accountPageRefSchema,
+ pageLinks.contentPageRefSchema,
+ pageLinks.loyaltyPageRefSchema,
+ pageLinks.collectionPageRefSchema,
+ pageLinks.hotelPageRefSchema,
+ pageLinks.destinationCityPageRefSchema,
+ pageLinks.destinationCountryPageRefSchema,
+ pageLinks.destinationOverviewPageRefSchema,
+ ]),
+ })
+ ),
+ }),
+ }),
+})
diff --git a/server/routers/contentstack/startPage/output.ts b/server/routers/contentstack/startPage/output.ts
index f52c911eb..69c6dc02b 100644
--- a/server/routers/contentstack/startPage/output.ts
+++ b/server/routers/contentstack/startPage/output.ts
@@ -6,27 +6,40 @@ import {
cardGridRefsSchema,
cardsGridSchema,
} from "../schemas/blocks/cardsGrid"
+import {
+ fullWidthCampaignBlockRefsSchema,
+ fullWidthCampaignBlockSchema,
+} from "../schemas/blocks/fullWidthCampaign"
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
import { systemSchema } from "../schemas/system"
import { StartPageEnum } from "@/types/enums/startPage"
-export const startPageCards = z
+const startPageCards = z
.object({
__typename: z.literal(StartPageEnum.ContentStack.blocks.CardsGrid),
})
.merge(cardsGridSchema)
-export const blocksSchema = z.discriminatedUnion("__typename", [startPageCards])
+const startPageFullWidthCampaign = z
+ .object({
+ __typename: z.literal(StartPageEnum.ContentStack.blocks.FullWidthCampaign),
+ })
+ .merge(fullWidthCampaignBlockSchema)
+
+export const blocksSchema = z.discriminatedUnion("__typename", [
+ startPageCards,
+ startPageFullWidthCampaign,
+])
export const startPageSchema = z.object({
start_page: z.object({
- blocks: discriminatedUnionArray(blocksSchema.options).nullable(),
title: z.string(),
header: z.object({
heading: z.string(),
hero_image: tempImageVaultAssetSchema,
}),
+ blocks: discriminatedUnionArray(blocksSchema.options).nullable(),
system: systemSchema.merge(
z.object({
created_at: z.string(),
@@ -46,8 +59,15 @@ const startPageCardsRefs = z
})
.merge(cardGridRefsSchema)
+const startPageFullWidthCampaignRef = z
+ .object({
+ __typename: z.literal(StartPageEnum.ContentStack.blocks.FullWidthCampaign),
+ })
+ .merge(fullWidthCampaignBlockRefsSchema)
+
const startPageBlockRefsItem = z.discriminatedUnion("__typename", [
startPageCardsRefs,
+ startPageFullWidthCampaignRef,
])
export const startPageRefsSchema = z.object({
diff --git a/server/routers/contentstack/startPage/query.ts b/server/routers/contentstack/startPage/query.ts
index d6cdf1986..5ce2c5ace 100644
--- a/server/routers/contentstack/startPage/query.ts
+++ b/server/routers/contentstack/startPage/query.ts
@@ -6,7 +6,7 @@ import { request } from "@/lib/graphql/request"
import { notFound } from "@/server/errors/trpc"
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
-import { generateTag } from "@/utils/generateTag"
+import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
import { startPageRefsSchema, startPageSchema } from "./output"
import {
@@ -17,6 +17,7 @@ import {
getStartPageRefsSuccessCounter,
getStartPageSuccessCounter,
} from "./telemetry"
+import { getConnections } from "./utils"
import {
TrackingChannelEnum,
@@ -103,6 +104,13 @@ export const startPageQueryRouter = router({
query: { lang, uid },
})
)
+
+ const connections = getConnections(validatedRefsData.data)
+
+ const tags = [
+ generateTagsFromSystem(lang, connections),
+ generateTag(lang, validatedRefsData.data.start_page.system.uid),
+ ].flat()
const response = await request(
GetStartPage,
{
@@ -112,10 +120,11 @@ export const startPageQueryRouter = router({
{
cache: "force-cache",
next: {
- tags: [generateTag(lang, uid)],
+ tags,
},
}
)
+
if (!response.data) {
const notFoundError = notFound(response)
getStartPageFailCounter.add(1, {
diff --git a/server/routers/contentstack/startPage/utils.ts b/server/routers/contentstack/startPage/utils.ts
new file mode 100644
index 000000000..9edb0720e
--- /dev/null
+++ b/server/routers/contentstack/startPage/utils.ts
@@ -0,0 +1,24 @@
+import { StartPageEnum } from "@/types/enums/startPage"
+import type { System } from "@/types/requests/system"
+import type { StartPageRefs } from "@/types/trpc/routers/contentstack/startPage"
+
+export function getConnections({ start_page }: StartPageRefs) {
+ const connections: System["system"][] = [start_page.system]
+
+ if (start_page.blocks) {
+ start_page.blocks.forEach((block) => {
+ switch (block.__typename) {
+ case StartPageEnum.ContentStack.blocks.FullWidthCampaign: {
+ block.full_width_campaign.full_width_campaignConnection.edges.forEach(
+ ({ node }) => {
+ connections.push(node.system)
+ }
+ )
+ break
+ }
+ }
+ })
+ }
+
+ return connections
+}
diff --git a/types/enums/blocks.ts b/types/enums/blocks.ts
index baf9a4a9c..4fd753abf 100644
--- a/types/enums/blocks.ts
+++ b/types/enums/blocks.ts
@@ -11,5 +11,6 @@ export namespace BlocksEnums {
UspGrid = "UspGrid",
SasTierComparison = "SasTierComparison",
HotelListing = "HotelListing",
+ FullWidthCampaign = "FullWidthCampaign",
}
}
diff --git a/types/enums/startPage.ts b/types/enums/startPage.ts
index fff235db4..339e5f26e 100644
--- a/types/enums/startPage.ts
+++ b/types/enums/startPage.ts
@@ -2,6 +2,7 @@ export namespace StartPageEnum {
export namespace ContentStack {
export const enum blocks {
CardsGrid = "StartPageBlocksCardsGrid",
+ FullWidthCampaign = "StartPageBlocksFullWidthCampaign",
}
}
}
diff --git a/types/trpc/routers/contentstack/startPage.ts b/types/trpc/routers/contentstack/startPage.ts
index 61219679e..ae93ef46a 100644
--- a/types/trpc/routers/contentstack/startPage.ts
+++ b/types/trpc/routers/contentstack/startPage.ts
@@ -1,5 +1,6 @@
import type { z } from "zod"
+import type { fullWidthCampaignSchema } from "@/server/routers/contentstack/schemas/blocks/fullWidthCampaign"
import type {
blocksSchema,
startPageRefsSchema,
@@ -15,3 +16,7 @@ export interface GetStartPageRefsSchema
export interface StartPageRefs extends z.output {}
export type Block = z.output
+
+export type FullWidthCampaign = z.output<
+ typeof fullWidthCampaignSchema
+>["full_width_campaign"]