diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/@breadcrumbs/campaign_overview_page/[uid]/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/@breadcrumbs/campaign_overview_page/[uid]/page.tsx new file mode 100644 index 000000000..eae837fe6 --- /dev/null +++ b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/@breadcrumbs/campaign_overview_page/[uid]/page.tsx @@ -0,0 +1,19 @@ +import { Suspense } from "react" + +import Breadcrumbs from "@/components/Breadcrumbs" +import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton" + +import type { BreadcrumbsProps } from "@/components/TempDesignSystem/Breadcrumbs/breadcrumbs" + +export default function CampaignOverviewPageBreadcrumbs() { + const variants: Pick = { + color: "Surface/Secondary/Default", + size: "contentWidth", + } + + return ( + }> + + + ) +} diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_overview_page/[uid]/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_overview_page/[uid]/page.tsx new file mode 100644 index 000000000..d388f5631 --- /dev/null +++ b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_overview_page/[uid]/page.tsx @@ -0,0 +1,21 @@ +import { notFound } from "next/navigation" +import { Suspense } from "react" + +import { env } from "@/env/server" + +import CampaignOverviewPage from "@/components/ContentType/CampaignOverviewPage" +import CampaignOverviewPageSkeleton from "@/components/ContentType/CampaignOverviewPage/CampaignOverviewPageSkeleton" + +export { generateMetadata } from "@/utils/generateMetadata" + +export default async function CampaignOverviewPagePage() { + if (!env.CAMPAIGN_PAGES_ENABLED) { + notFound() + } + + return ( + }> + + + ) +} diff --git a/apps/scandic-web/components/ContentType/CampaignOverviewPage/CampaignOverviewPageSkeleton.tsx b/apps/scandic-web/components/ContentType/CampaignOverviewPage/CampaignOverviewPageSkeleton.tsx new file mode 100644 index 000000000..5ad3a57c9 --- /dev/null +++ b/apps/scandic-web/components/ContentType/CampaignOverviewPage/CampaignOverviewPageSkeleton.tsx @@ -0,0 +1,15 @@ +"use client" + +import SkeletonShimmer from "@/components/SkeletonShimmer" + +import styles from "./campaignOverviewPage.module.css" + +export default function CampaignOverviewPageSkeleton() { + return ( +
+

+ +

+
+ ) +} diff --git a/apps/scandic-web/components/ContentType/CampaignOverviewPage/campaignOverviewPage.module.css b/apps/scandic-web/components/ContentType/CampaignOverviewPage/campaignOverviewPage.module.css new file mode 100644 index 000000000..91b6b2429 --- /dev/null +++ b/apps/scandic-web/components/ContentType/CampaignOverviewPage/campaignOverviewPage.module.css @@ -0,0 +1,52 @@ +.pageContainer { + display: grid; + gap: var(--Space-x5); + padding-bottom: var(--Space-x7); +} + +.header { + background-color: var(--Base-Surface-Subtle-Normal); + padding-bottom: var(--Space-x4); +} + +.headerContent { + display: grid; + gap: var(--Space-x3); + width: var(--max-width-content); + margin: 0 auto; +} + +.headerIntro { + display: grid; + max-width: var(--max-width-text-block); + gap: var(--Space-x3); +} + +.heading { + color: var(--Text-Heading); + text-wrap: balance; + hyphens: auto; +} + +.mainContent { + display: grid; + gap: var(--Space-x6); + width: var(--max-width-content); + margin: 0 auto; +} + +@media (min-width: 768px) { + .pageContainer { + gap: var(--Space-x9); + } + + .headerIntro { + gap: var(--Space-x3); + } +} + +@media screen and (min-width: 1367px) { + .mainContent { + gap: var(--Space-x9); + } +} diff --git a/apps/scandic-web/components/ContentType/CampaignOverviewPage/index.tsx b/apps/scandic-web/components/ContentType/CampaignOverviewPage/index.tsx new file mode 100644 index 000000000..859aa4f9e --- /dev/null +++ b/apps/scandic-web/components/ContentType/CampaignOverviewPage/index.tsx @@ -0,0 +1,57 @@ +import { notFound } from "next/navigation" +import { Suspense } from "react" + +import { Typography } from "@scandic-hotels/design-system/Typography" + +import { getCampaignOverviewPage } from "@/lib/trpc/memoizedRequests" + +import LinkChips from "@/components/TempDesignSystem/LinkChips" + +import CampaignOverviewPageSkeleton from "./CampaignOverviewPageSkeleton" + +import styles from "./campaignOverviewPage.module.css" + +export default async function CampaignOverviewPage() { + const pageData = await getCampaignOverviewPage() + + if (!pageData) { + notFound() + } + + const { campaignOverviewPage } = pageData + const { header } = campaignOverviewPage + + return ( + <> + }> +
+ {header ? ( +
+
+ <> +
+ +

{header.heading}

+
+ +

{header.preamble}

+
+
+ {header.navigation_links ? ( + + ) : null} + +
+
+ ) : null} + +
+ {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */} + {">>> MAIN CONTENT <<<"} +
+
+
+ {/* */} + + ) +} diff --git a/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/chip.module.css b/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/chip.module.css deleted file mode 100644 index f46cab27f..000000000 --- a/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/chip.module.css +++ /dev/null @@ -1,14 +0,0 @@ -.linkChip { - display: flex; - align-items: center; - gap: var(--Spacing-x-half); - padding: var(--Spacing-x1) var(--Spacing-x-one-and-half); - border-radius: var(--Corner-radius-sm); - background-color: var(--Base-Button-Inverted-Fill-Normal); - transition: background-color 0.3s; - text-decoration: none; -} - -.linkChip:hover { - background-color: var(--Base-Button-Inverted-Fill-Hover-alt); -} diff --git a/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/chip.ts b/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/chip.ts deleted file mode 100644 index 0a4116686..000000000 --- a/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/chip.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface LinkChipProps { - url: string - title: string -} diff --git a/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/index.tsx b/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/index.tsx deleted file mode 100644 index 82a5df3e7..000000000 --- a/apps/scandic-web/components/TempDesignSystem/LinkChips/Chip/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import Link from "next/link" - -import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" - -import Caption from "@/components/TempDesignSystem/Text/Caption" - -import styles from "./chip.module.css" - -import type { LinkChipProps } from "./chip" - -export default function LinkChip({ url, title }: LinkChipProps) { - return ( - - - {title} - - - - ) -} diff --git a/apps/scandic-web/components/TempDesignSystem/LinkChips/index.tsx b/apps/scandic-web/components/TempDesignSystem/LinkChips/index.tsx index dccefd9db..5d96fe1b0 100644 --- a/apps/scandic-web/components/TempDesignSystem/LinkChips/index.tsx +++ b/apps/scandic-web/components/TempDesignSystem/LinkChips/index.tsx @@ -1,8 +1,15 @@ -import LinkChip from "./Chip" +"use client" -import styles from "./linkChips.module.css" +import { ChipLink } from "@scandic-hotels/design-system/ChipLink" +import { Chips } from "@scandic-hotels/design-system/Chips" +import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" -import type { LinkChipsProps } from "./linkChips" +interface LinkChipsProps { + chips: { + url: string + title: string + }[] +} export default function LinkChips({ chips }: LinkChipsProps) { if (!chips.length) { @@ -10,12 +17,13 @@ export default function LinkChips({ chips }: LinkChipsProps) { } return ( -
    - {chips.map(({ url, title }) => ( -
  • - -
  • + + {chips.map(({ title, url }) => ( + + {title} + + ))} -
+ ) } diff --git a/apps/scandic-web/components/TempDesignSystem/LinkChips/linkChips.module.css b/apps/scandic-web/components/TempDesignSystem/LinkChips/linkChips.module.css deleted file mode 100644 index 9d7361824..000000000 --- a/apps/scandic-web/components/TempDesignSystem/LinkChips/linkChips.module.css +++ /dev/null @@ -1,8 +0,0 @@ -.linkChips { - list-style: none; - display: flex; - flex-wrap: wrap; - justify-content: flex-start; - align-items: center; - gap: var(--Spacing-x1); -} diff --git a/apps/scandic-web/components/TempDesignSystem/LinkChips/linkChips.ts b/apps/scandic-web/components/TempDesignSystem/LinkChips/linkChips.ts deleted file mode 100644 index dcc2be2a3..000000000 --- a/apps/scandic-web/components/TempDesignSystem/LinkChips/linkChips.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { LinkChipProps } from "./Chip/chip" - -export interface LinkChipsProps { - chips: LinkChipProps[] -} diff --git a/apps/scandic-web/lib/graphql/Fragments/CampaignOverviewPage/NavigationLinks.graphql b/apps/scandic-web/lib/graphql/Fragments/CampaignOverviewPage/NavigationLinks.graphql new file mode 100644 index 000000000..974166da9 --- /dev/null +++ b/apps/scandic-web/lib/graphql/Fragments/CampaignOverviewPage/NavigationLinks.graphql @@ -0,0 +1,70 @@ +#import "../PageLink/AccountPageLink.graphql" +#import "../PageLink/CampaignOverviewPageLink.graphql" +#import "../PageLink/CampaignPageLink.graphql" +#import "../PageLink/CollectionPageLink.graphql" +#import "../PageLink/ContentPageLink.graphql" +#import "../PageLink/DestinationCityPageLink.graphql" +#import "../PageLink/DestinationCountryPageLink.graphql" +#import "../PageLink/DestinationOverviewPageLink.graphql" +#import "../PageLink/HotelPageLink.graphql" +#import "../PageLink/LoyaltyPageLink.graphql" +#import "../PageLink/StartPageLink.graphql" + +#import "../AccountPage/Ref.graphql" +#import "../CampaignOverviewPage/Ref.graphql" +#import "../CampaignPage/Ref.graphql" +#import "../CollectionPage/Ref.graphql" +#import "../ContentPage/Ref.graphql" +#import "../DestinationCityPage/Ref.graphql" +#import "../DestinationCountryPage/Ref.graphql" +#import "../DestinationOverviewPage/Ref.graphql" +#import "../HotelPage/Ref.graphql" +#import "../LoyaltyPage/Ref.graphql" +#import "../StartPage/Ref.graphql" + +fragment NavigationLinks_CampaignOverviewPage on CampaignOverviewPageHeader { + navigation_links { + title + linkConnection { + edges { + node { + __typename + ...AccountPageLink + ...CampaignOverviewPageLink + ...CampaignPageLink + ...CollectionPageLink + ...ContentPageLink + ...DestinationCityPageLink + ...DestinationCountryPageLink + ...DestinationOverviewPageLink + ...HotelPageLink + ...LoyaltyPageLink + ...StartPageLink + } + } + } + } +} + +fragment NavigationLinksRef_CampaignOverviewPage on CampaignOverviewPageHeader { + navigation_links { + linkConnection { + edges { + node { + __typename + ...AccountPageRef + ...CampaignOverviewPageRef + ...CampaignPageRef + ...CollectionPageRef + ...ContentPageRef + ...DestinationCityPageRef + ...DestinationCountryPageRef + ...DestinationOverviewPageRef + ...HotelPageRef + ...LoyaltyPageRef + ...StartPageRef + } + } + } + } +} diff --git a/apps/scandic-web/lib/graphql/Fragments/CampaignOverviewPage/Ref.graphql b/apps/scandic-web/lib/graphql/Fragments/CampaignOverviewPage/Ref.graphql new file mode 100644 index 000000000..d35af3917 --- /dev/null +++ b/apps/scandic-web/lib/graphql/Fragments/CampaignOverviewPage/Ref.graphql @@ -0,0 +1,7 @@ +#import "../System.graphql" + +fragment CampaignOverviewPageRef on CampaignOverviewPage { + system { + ...System + } +} diff --git a/apps/scandic-web/lib/graphql/Fragments/PageLink/CampaignOverviewPageLink.graphql b/apps/scandic-web/lib/graphql/Fragments/PageLink/CampaignOverviewPageLink.graphql new file mode 100644 index 000000000..8ec9faa4a --- /dev/null +++ b/apps/scandic-web/lib/graphql/Fragments/PageLink/CampaignOverviewPageLink.graphql @@ -0,0 +1,9 @@ +#import "../System.graphql" + +fragment CampaignOverviewPageLink on CampaignOverviewPage { + title + url + system { + ...System + } +} diff --git a/apps/scandic-web/lib/graphql/Query/Breadcrumbs/CampaignOverviewPage.graphql b/apps/scandic-web/lib/graphql/Query/Breadcrumbs/CampaignOverviewPage.graphql new file mode 100644 index 000000000..144cb64b1 --- /dev/null +++ b/apps/scandic-web/lib/graphql/Query/Breadcrumbs/CampaignOverviewPage.graphql @@ -0,0 +1,29 @@ +#import "../../Fragments/Breadcrumbs/Breadcrumbs.graphql" +#import "../../Fragments/System.graphql" + +query GetCampaignOverviewPageBreadcrumbs($locale: String!, $uid: String!) { + campaign_overview_page(locale: $locale, uid: $uid) { + url + web { + breadcrumbs { + ...Breadcrumbs + } + } + system { + ...System + } + } +} + +query GetCampaignOverviewPageBreadcrumbsRefs($locale: String!, $uid: String!) { + campaign_overview_page(locale: $locale, uid: $uid) { + web { + breadcrumbs { + ...BreadcrumbsRefs + } + } + system { + ...System + } + } +} diff --git a/apps/scandic-web/lib/graphql/Query/CampaignOverviewPage/CampaignOverviewPage.graphql b/apps/scandic-web/lib/graphql/Query/CampaignOverviewPage/CampaignOverviewPage.graphql new file mode 100644 index 000000000..ae888feec --- /dev/null +++ b/apps/scandic-web/lib/graphql/Query/CampaignOverviewPage/CampaignOverviewPage.graphql @@ -0,0 +1,57 @@ +#import "../../Fragments/System.graphql" + +#import "../../Fragments/CampaignOverviewPage/NavigationLinks.graphql" + +query GetCampaignOverviewPage($locale: String!, $uid: String!) { + campaign_overview_page(uid: $uid, locale: $locale) { + title + header { + heading + preamble + ...NavigationLinks_CampaignOverviewPage + } + system { + ...System + created_at + updated_at + } + } + trackingProps: campaign_overview_page(locale: "en", uid: $uid) { + url + } +} + +query GetCampaignOverviewPageRefs($locale: String!, $uid: String!) { + campaign_overview_page(locale: $locale, uid: $uid) { + header { + ...NavigationLinksRef_CampaignOverviewPage + } + system { + ...System + } + } +} + +query GetDaDeEnUrlsCampaignOverviewPage($uid: String!) { + de: campaign_overview_page(locale: "de", uid: $uid) { + url + } + en: campaign_overview_page(locale: "en", uid: $uid) { + url + } + da: campaign_overview_page(locale: "da", uid: $uid) { + url + } +} + +query GetFiNoSvUrlsCampaignOverviewPage($uid: String!) { + fi: campaign_overview_page(locale: "fi", uid: $uid) { + url + } + no: campaign_overview_page(locale: "no", uid: $uid) { + url + } + sv: campaign_overview_page(locale: "sv", uid: $uid) { + url + } +} diff --git a/apps/scandic-web/lib/graphql/Query/CampaignOverviewPage/Metadata.graphql b/apps/scandic-web/lib/graphql/Query/CampaignOverviewPage/Metadata.graphql new file mode 100644 index 000000000..e0ce37000 --- /dev/null +++ b/apps/scandic-web/lib/graphql/Query/CampaignOverviewPage/Metadata.graphql @@ -0,0 +1,22 @@ +#import "../../Fragments/Metadata.graphql" +#import "../../Fragments/System.graphql" + +query GetCampaignOverviewPageMetadata($locale: String!, $uid: String!) { + campaign_overview_page(locale: $locale, uid: $uid) { + web { + breadcrumbs { + title + } + seo_metadata { + ...Metadata + } + } + header { + heading + preamble + } + system { + ...System + } + } +} diff --git a/apps/scandic-web/lib/graphql/Query/PageSettings.graphql b/apps/scandic-web/lib/graphql/Query/PageSettings.graphql index dd5d2c2ed..e83fe4a17 100644 --- a/apps/scandic-web/lib/graphql/Query/PageSettings.graphql +++ b/apps/scandic-web/lib/graphql/Query/PageSettings.graphql @@ -8,6 +8,14 @@ query GetAccountPageSettings($uid: String!, $locale: String!) { } } +query GetCampaignOverviewPageSettings($uid: String!, $locale: String!) { + page: campaign_overview_page(uid: $uid, locale: $locale) { + settings: page_settings { + ...PageSettings + } + } +} + query GetCampaignPageSettings($uid: String!, $locale: String!) { page: campaign_page(uid: $uid, locale: $locale) { settings: page_settings { diff --git a/apps/scandic-web/lib/graphql/Query/ResolveEntry.graphql b/apps/scandic-web/lib/graphql/Query/ResolveEntry.graphql index d26550941..c0de4348a 100644 --- a/apps/scandic-web/lib/graphql/Query/ResolveEntry.graphql +++ b/apps/scandic-web/lib/graphql/Query/ResolveEntry.graphql @@ -92,4 +92,12 @@ query EntryByUrlBatch2($locale: String!, $url: String!) { } total } + all_campaign_overview_page(where: { url: $url }, locale: $locale) { + items { + system { + ...System + } + } + total + } } diff --git a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts index 38d5d55d6..484f12593 100644 --- a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts +++ b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts @@ -422,3 +422,10 @@ export const getCampaignPage = cache(async function getMemoizedCampaignPage() { const caller = await serverClient() return caller.contentstack.campaignPage.get() }) + +export const getCampaignOverviewPage = cache( + async function getMemoizedCampaignOverviewPage() { + const caller = await serverClient() + return caller.contentstack.campaignOverviewPage.get() + } +) diff --git a/apps/scandic-web/server/routers/contentstack/breadcrumbs/query.ts b/apps/scandic-web/server/routers/contentstack/breadcrumbs/query.ts index 42a549f71..2dfa4a0b0 100644 --- a/apps/scandic-web/server/routers/contentstack/breadcrumbs/query.ts +++ b/apps/scandic-web/server/routers/contentstack/breadcrumbs/query.ts @@ -9,6 +9,10 @@ import { GetMyPagesBreadcrumbs, GetMyPagesBreadcrumbsRefs, } from "@/lib/graphql/Query/Breadcrumbs/AccountPage.graphql" +import { + GetCampaignOverviewPageBreadcrumbs, + GetCampaignOverviewPageBreadcrumbsRefs, +} from "@/lib/graphql/Query/Breadcrumbs/CampaignOverviewPage.graphql" import { GetCampaignPageBreadcrumbs, GetCampaignPageBreadcrumbsRefs, @@ -158,6 +162,17 @@ export const breadcrumbsQueryRouter = router({ }, variables ) + case PageContentTypeEnum.campaignOverviewPage: + return await getBreadcrumbs<{ + campaign_overview_page: RawBreadcrumbsSchema + }>( + { + dataKey: "campaign_overview_page", + refQuery: GetCampaignOverviewPageBreadcrumbsRefs, + query: GetCampaignOverviewPageBreadcrumbs, + }, + variables + ) case PageContentTypeEnum.campaignPage: return await getBreadcrumbs<{ campaign_page: RawBreadcrumbsSchema diff --git a/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/index.ts b/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/index.ts new file mode 100644 index 000000000..bbf2d0e79 --- /dev/null +++ b/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/index.ts @@ -0,0 +1,7 @@ +import { mergeRouters } from "@scandic-hotels/trpc" + +import { campaignOverviewPageQueryRouter } from "./query" + +export const campaignOverviewPageRouter = mergeRouters( + campaignOverviewPageQueryRouter +) diff --git a/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/output.ts b/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/output.ts new file mode 100644 index 000000000..b71ea6dd6 --- /dev/null +++ b/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/output.ts @@ -0,0 +1,57 @@ +import { z } from "zod" + +import { + linkAndTitleSchema, + linkConnectionRefs, +} from "@/server/routers/contentstack/schemas/linkConnection" + +import { systemSchema } from "../schemas/system" + +const navigationLinksSchema = z + .array(linkAndTitleSchema) + .nullable() + .transform((data) => { + if (!data) { + return null + } + + return data + .filter((item) => !!item.link) + .map((item) => ({ + url: item.link!.url, + title: item.title || item.link!.title, + })) + }) + +export const campaignOverviewPageSchema = z.object({ + campaign_overview_page: z.object({ + title: z.string(), + header: z.object({ + heading: z.string(), + preamble: z.string(), + navigation_links: navigationLinksSchema, + }), + system: systemSchema.merge( + z.object({ + created_at: z.string(), + updated_at: z.string(), + }) + ), + }), + trackingProps: z.object({ + url: z.string(), + }), +}) + +/** REFS */ + +const campaignOverviewPageHeaderRefs = z.object({ + navigation_links: z.array(linkConnectionRefs), +}) + +export const campaignOverviewPageRefsSchema = z.object({ + campaign_overview_page: z.object({ + header: campaignOverviewPageHeaderRefs, + system: systemSchema, + }), +}) diff --git a/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/query.ts b/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/query.ts new file mode 100644 index 000000000..956a88595 --- /dev/null +++ b/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/query.ts @@ -0,0 +1,135 @@ +import { createCounter } from "@scandic-hotels/common/telemetry" +import { router } from "@scandic-hotels/trpc" +import { notFound } from "@scandic-hotels/trpc/errors" +import { contentStackUidWithServiceProcedure } from "@scandic-hotels/trpc/procedures" + +import { + GetCampaignOverviewPage, + GetCampaignOverviewPageRefs, +} from "@/lib/graphql/Query/CampaignOverviewPage/CampaignOverviewPage.graphql" +import { request } from "@/lib/graphql/request" + +import { generateRefsResponseTag } from "@/utils/generateTag" + +import { + campaignOverviewPageRefsSchema, + campaignOverviewPageSchema, +} from "./output" +import { generatePageTags } from "./utils" + +import { + TrackingChannelEnum, + type TrackingSDKPageData, +} from "@/types/components/tracking" +import type { + GetCampaignOverviewPageData, + GetCampaignOverviewPageRefsData, +} from "@/types/trpc/routers/contentstack/campaignOverviewPage" + +export const campaignOverviewPageQueryRouter = router({ + get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => { + const { lang, uid } = ctx + + const getCampaignOverviewPageRefsCounter = createCounter( + "trpc.contentstack", + "campaignOverviewPage.get.refs" + ) + const metricsGetCampaignOverviewPageRefs = + getCampaignOverviewPageRefsCounter.init({ + lang, + uid, + }) + + metricsGetCampaignOverviewPageRefs.start() + + const refsResponse = await request( + GetCampaignOverviewPageRefs, + { locale: lang, uid }, + { + key: generateRefsResponseTag(lang, uid), + ttl: "max", + } + ) + + if (!refsResponse.data) { + const notFoundError = notFound(refsResponse) + metricsGetCampaignOverviewPageRefs.noDataError() + throw notFoundError + } + + const validatedRefsData = campaignOverviewPageRefsSchema.safeParse( + refsResponse.data + ) + if (!validatedRefsData.success) { + metricsGetCampaignOverviewPageRefs.validationError( + validatedRefsData.error + ) + return null + } + + metricsGetCampaignOverviewPageRefs.success() + + const tags = generatePageTags(validatedRefsData.data, lang) + + const getCampaignOverviewPageCounter = createCounter( + "trpc.contentstack", + "campaignOverviewPage.get" + ) + const metricsGetCampaignOverviewPage = getCampaignOverviewPageCounter.init({ + lang, + uid, + }) + + metricsGetCampaignOverviewPage.start() + + const response = await request( + GetCampaignOverviewPage, + { + locale: lang, + uid, + }, + { + key: tags, + ttl: "max", + } + ) + if (!response.data) { + const notFoundError = notFound(response) + metricsGetCampaignOverviewPage.noDataError() + throw notFoundError + } + + const validatedResponse = campaignOverviewPageSchema.safeParse( + response.data + ) + + if (!validatedResponse.success) { + metricsGetCampaignOverviewPage.validationError(validatedResponse.error) + return null + } + + const campaignOverviewPage = validatedResponse.data.campaign_overview_page + + metricsGetCampaignOverviewPage.success() + + const system = campaignOverviewPage.system + const pageName = `campaign-overview-page` + + const tracking: TrackingSDKPageData = { + pageId: system.uid, + domainLanguage: system.locale, + publishDate: system.updated_at, + createDate: system.created_at, + channel: TrackingChannelEnum["campaign-overview-page"], + pageType: "campaign-overview-page", + pageName, + siteSections: pageName, + siteVersion: "new-web", + } + + return { + campaignOverviewPage, + tracking, + } + }), +}) diff --git a/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/utils.ts b/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/utils.ts new file mode 100644 index 000000000..63ffe830f --- /dev/null +++ b/apps/scandic-web/server/routers/contentstack/campaignOverviewPage/utils.ts @@ -0,0 +1,33 @@ +import { generateTag, generateTagsFromSystem } from "@/utils/generateTag" + +import type { Lang } from "@scandic-hotels/common/constants/language" + +import type { System } from "@/types/requests/system" +import type { CampaignOverviewPageRefs } from "@/types/trpc/routers/contentstack/campaignOverviewPage" + +export function generatePageTags( + validatedData: CampaignOverviewPageRefs, + lang: Lang +): string[] { + const connections = getConnections(validatedData) + return [ + generateTagsFromSystem(lang, connections), + generateTag(lang, validatedData.campaign_overview_page.system.uid), + ].flat() +} + +export function getConnections({ + campaign_overview_page, +}: CampaignOverviewPageRefs) { + const connections: System["system"][] = [campaign_overview_page.system] + + if (campaign_overview_page.header.navigation_links) { + campaign_overview_page.header.navigation_links.forEach((link) => { + if (link.link) { + connections.push(link.link) + } + }) + } + + return connections +} diff --git a/apps/scandic-web/server/routers/contentstack/index.ts b/apps/scandic-web/server/routers/contentstack/index.ts index e5922bad5..cce713d99 100644 --- a/apps/scandic-web/server/routers/contentstack/index.ts +++ b/apps/scandic-web/server/routers/contentstack/index.ts @@ -3,6 +3,7 @@ import { router } from "@scandic-hotels/trpc" import { accountPageRouter } from "./accountPage" import { baseRouter } from "./base" import { breadcrumbsRouter } from "./breadcrumbs" +import { campaignOverviewPageRouter } from "./campaignOverviewPage" import { campaignPageRouter } from "./campaignPage" import { collectionPageRouter } from "./collectionPage" import { contentPageRouter } from "./contentPage" @@ -26,6 +27,7 @@ export const contentstackRouter = router({ hotelPage: hotelPageRouter, languageSwitcher: languageSwitcherRouter, loyaltyPage: loyaltyPageRouter, + campaignOverviewPage: campaignOverviewPageRouter, campaignPage: campaignPageRouter, collectionPage: collectionPageRouter, contentPage: contentPageRouter, diff --git a/apps/scandic-web/server/routers/contentstack/languageSwitcher/utils.ts b/apps/scandic-web/server/routers/contentstack/languageSwitcher/utils.ts index 0fbe784db..cbd19388d 100644 --- a/apps/scandic-web/server/routers/contentstack/languageSwitcher/utils.ts +++ b/apps/scandic-web/server/routers/contentstack/languageSwitcher/utils.ts @@ -7,6 +7,10 @@ import { GetDaDeEnUrlsAccountPage, GetFiNoSvUrlsAccountPage, } from "@/lib/graphql/Query/AccountPage/AccountPage.graphql" +import { + GetDaDeEnUrlsCampaignOverviewPage, + GetFiNoSvUrlsCampaignOverviewPage, +} from "@/lib/graphql/Query/CampaignOverviewPage/CampaignOverviewPage.graphql" import { GetDaDeEnUrlsCampaignPage, GetFiNoSvUrlsCampaignPage, @@ -100,6 +104,10 @@ export async function getUrlsOfAllLanguages( daDeEnDocument = GetDaDeEnUrlsCurrentBlocksPage fiNoSvDocument = GetFiNoSvUrlsCurrentBlocksPage break + case PageContentTypeEnum.campaignOverviewPage: + daDeEnDocument = GetDaDeEnUrlsCampaignOverviewPage + fiNoSvDocument = GetFiNoSvUrlsCampaignOverviewPage + break case PageContentTypeEnum.campaignPage: daDeEnDocument = GetDaDeEnUrlsCampaignPage fiNoSvDocument = GetFiNoSvUrlsCampaignPage diff --git a/apps/scandic-web/server/routers/contentstack/metadata/query.ts b/apps/scandic-web/server/routers/contentstack/metadata/query.ts index 7c06f4e77..51910ae98 100644 --- a/apps/scandic-web/server/routers/contentstack/metadata/query.ts +++ b/apps/scandic-web/server/routers/contentstack/metadata/query.ts @@ -7,6 +7,7 @@ import { contentStackUidWithServiceProcedure } from "@scandic-hotels/trpc/proced import { env } from "@/env/server" import { GetAccountPageMetadata } from "@/lib/graphql/Query/AccountPage/Metadata.graphql" +import { GetCampaignOverviewPageMetadata } from "@/lib/graphql/Query/CampaignOverviewPage/Metadata.graphql" import { GetCampaignPageMetadata } from "@/lib/graphql/Query/CampaignPage/Metadata.graphql" import { GetCollectionPageMetadata } from "@/lib/graphql/Query/CollectionPage/Metadata.graphql" import { GetContentPageMetadata } from "@/lib/graphql/Query/ContentPage/Metadata.graphql" @@ -131,6 +132,14 @@ export const metadataQueryRouter = router({ accountPageResponse.account_page ) break + case PageContentTypeEnum.campaignOverviewPage: + const campaignOverviewPageResponse = await fetchMetadata<{ + campaign_overview_page: RawMetadataSchema + }>(GetCampaignOverviewPageMetadata, variables) + metadata = await getTransformedMetadata( + campaignOverviewPageResponse.campaign_overview_page + ) + break case PageContentTypeEnum.campaignPage: const campaignPageResponse = await fetchMetadata<{ campaign_page: RawMetadataSchema diff --git a/apps/scandic-web/server/routers/contentstack/pageSettings/query.ts b/apps/scandic-web/server/routers/contentstack/pageSettings/query.ts index c4cd27e52..2e947e12c 100644 --- a/apps/scandic-web/server/routers/contentstack/pageSettings/query.ts +++ b/apps/scandic-web/server/routers/contentstack/pageSettings/query.ts @@ -5,6 +5,7 @@ import { contentstackBaseProcedure } from "@scandic-hotels/trpc/procedures" import { GetAccountPageSettings, + GetCampaignOverviewPageSettings, GetCampaignPageSettings, GetCollectionPageSettings, GetContentPageSettings, @@ -77,6 +78,7 @@ export const pageSettingsQueryRouter = router({ const graphqlQueriesForContentType: Record = { [PageContentTypeEnum.accountPage]: GetAccountPageSettings, + [PageContentTypeEnum.campaignOverviewPage]: GetCampaignOverviewPageSettings, [PageContentTypeEnum.campaignPage]: GetCampaignPageSettings, [PageContentTypeEnum.collectionPage]: GetCollectionPageSettings, [PageContentTypeEnum.contentPage]: GetContentPageSettings, diff --git a/apps/scandic-web/server/routers/contentstack/schemas/blocks/contentEmbeds.ts b/apps/scandic-web/server/routers/contentstack/schemas/blocks/contentEmbeds.ts index 49ab70d5f..7685171f7 100644 --- a/apps/scandic-web/server/routers/contentstack/schemas/blocks/contentEmbeds.ts +++ b/apps/scandic-web/server/routers/contentstack/schemas/blocks/contentEmbeds.ts @@ -2,6 +2,8 @@ import { z } from "zod" import { accountPageSchema, + campaignOverviewPageSchema, + campaignPageSchema, collectionPageSchema, contentPageSchema, destinationCityPageSchema, @@ -20,6 +22,8 @@ export const contentEmbedsSchema = z imageContainerSchema, sysAssetSchema, accountPageSchema, + campaignOverviewPageSchema, + campaignPageSchema, collectionPageSchema, contentPageSchema, destinationCityPageSchema, diff --git a/apps/scandic-web/server/routers/contentstack/schemas/pageLinks.ts b/apps/scandic-web/server/routers/contentstack/schemas/pageLinks.ts index 61f4866e9..f57fe9c66 100644 --- a/apps/scandic-web/server/routers/contentstack/schemas/pageLinks.ts +++ b/apps/scandic-web/server/routers/contentstack/schemas/pageLinks.ts @@ -32,6 +32,18 @@ export const extendedPageLinkSchema = pageLinkSchema.merge( .default({ original_url: "" }), }) ) + +export const campaignOverviewPageSchema = z + .object({ + __typename: z.literal(ContentEnum.blocks.CampaignOverviewPage), + }) + .merge(extendedPageLinkSchema) + +export const campaignOverviewPageRefSchema = z.object({ + __typename: z.literal(ContentEnum.blocks.CampaignOverviewPage), + system: systemSchema, +}) + export const collectionPageSchema = z .object({ __typename: z.literal(ContentEnum.blocks.CollectionPage), @@ -133,6 +145,7 @@ export const startPageRefSchema = z.object({ export const linkUnionSchema = z.discriminatedUnion("__typename", [ accountPageSchema, + campaignOverviewPageSchema, campaignPageSchema, collectionPageSchema, contentPageSchema, @@ -146,6 +159,7 @@ export const linkUnionSchema = z.discriminatedUnion("__typename", [ type Data = | z.output + | z.output | z.output | z.output | z.output @@ -161,6 +175,7 @@ export function transformPageLink(data: Data) { if (data && "__typename" in data) { switch (data.__typename) { case ContentEnum.blocks.AccountPage: + case ContentEnum.blocks.CampaignOverviewPage: case ContentEnum.blocks.CampaignPage: case ContentEnum.blocks.DestinationCityPage: case ContentEnum.blocks.DestinationCountryPage: @@ -173,7 +188,6 @@ export function transformPageLink(data: Data) { title: data.title, url: removeMultipleSlashes(`/${data.system.locale}/${data.url}`), } - case ContentEnum.blocks.CollectionPage: case ContentEnum.blocks.ContentPage: case ContentEnum.blocks.LoyaltyPage: @@ -195,6 +209,7 @@ export function transformPageLink(data: Data) { export const linkRefsUnionSchema = z.discriminatedUnion("__typename", [ accountPageRefSchema, + campaignOverviewPageRefSchema, campaignPageRefSchema, collectionPageRefSchema, contentPageRefSchema, @@ -208,6 +223,7 @@ export const linkRefsUnionSchema = z.discriminatedUnion("__typename", [ type RefData = | z.output + | z.output | z.output | z.output | z.output @@ -223,6 +239,7 @@ export function transformPageLinkRef(data: RefData) { if (data && "__typename" in data) { switch (data.__typename) { case ContentEnum.blocks.AccountPage: + case ContentEnum.blocks.CampaignOverviewPage: case ContentEnum.blocks.CampaignPage: case ContentEnum.blocks.CollectionPage: case ContentEnum.blocks.ContentPage: diff --git a/apps/scandic-web/types/components/tracking.ts b/apps/scandic-web/types/components/tracking.ts index ee738dd32..8bb4eaecc 100644 --- a/apps/scandic-web/types/components/tracking.ts +++ b/apps/scandic-web/types/components/tracking.ts @@ -9,6 +9,7 @@ export enum TrackingChannelEnum { "hotelreservation" = "hotelreservation", "collection-page" = "collection-page", "campaign-page" = "campaign-page", + "campaign-overview-page" = "campaign-overview-page", "hotels" = "hotels", "homepage" = "homepage", } diff --git a/apps/scandic-web/types/enums/content.ts b/apps/scandic-web/types/enums/content.ts index 42d820a83..ebad7590f 100644 --- a/apps/scandic-web/types/enums/content.ts +++ b/apps/scandic-web/types/enums/content.ts @@ -1,6 +1,7 @@ export namespace ContentEnum { export const enum blocks { AccountPage = "AccountPage", + CampaignOverviewPage = "CampaignOverviewPage", CampaignPage = "CampaignPage", CollectionPage = "CollectionPage", ContentPage = "ContentPage", diff --git a/apps/scandic-web/types/params.ts b/apps/scandic-web/types/params.ts index 8485444d4..545504899 100644 --- a/apps/scandic-web/types/params.ts +++ b/apps/scandic-web/types/params.ts @@ -23,6 +23,8 @@ export type StatusParams = { export type ContentTypeParams = { contentType: | PageContentTypeEnum.loyaltyPage + | PageContentTypeEnum.campaignOverviewPage + | PageContentTypeEnum.campaignPage | PageContentTypeEnum.contentPage | PageContentTypeEnum.hotelPage | PageContentTypeEnum.collectionPage diff --git a/apps/scandic-web/types/requests/contentType.ts b/apps/scandic-web/types/requests/contentType.ts index 866e1303b..8c0a99f24 100644 --- a/apps/scandic-web/types/requests/contentType.ts +++ b/apps/scandic-web/types/requests/contentType.ts @@ -1,12 +1,13 @@ export enum PageContentTypeEnum { accountPage = "account_page", + campaignOverviewPage = "campaign_overview_page", campaignPage = "campaign_page", collectionPage = "collection_page", contentPage = "content_page", currentBlocksPage = "current_blocks_page", - destinationOverviewPage = "destination_overview_page", - destinationCountryPage = "destination_country_page", destinationCityPage = "destination_city_page", + destinationCountryPage = "destination_country_page", + destinationOverviewPage = "destination_overview_page", hotelPage = "hotel_page", loyaltyPage = "loyalty_page", startPage = "start_page", diff --git a/apps/scandic-web/types/requests/entry.ts b/apps/scandic-web/types/requests/entry.ts index 647829eab..e43da4cb7 100644 --- a/apps/scandic-web/types/requests/entry.ts +++ b/apps/scandic-web/types/requests/entry.ts @@ -14,6 +14,7 @@ const entryResolveSchema = z.object({ export const validateEntryResolveSchema = z.object({ all_account_page: entryResolveSchema, + all_campaign_overview_page: entryResolveSchema, all_campaign_page: entryResolveSchema, all_collection_page: entryResolveSchema, all_content_page: entryResolveSchema, diff --git a/apps/scandic-web/types/trpc/routers/contentstack/campaignOverviewPage.ts b/apps/scandic-web/types/trpc/routers/contentstack/campaignOverviewPage.ts new file mode 100644 index 000000000..bc7f43a66 --- /dev/null +++ b/apps/scandic-web/types/trpc/routers/contentstack/campaignOverviewPage.ts @@ -0,0 +1,19 @@ +import type { z } from "zod" + +import type { + campaignOverviewPageRefsSchema, + campaignOverviewPageSchema, +} from "@/server/routers/contentstack/campaignOverviewPage/output" + +export interface GetCampaignOverviewPageData + extends z.input {} +export interface CampaignOverviewPage + extends z.output {} +export type CampaignOverviewPageData = + CampaignOverviewPage["campaign_overview_page"] + +export interface GetCampaignOverviewPageRefsData + extends z.input {} + +export interface CampaignOverviewPageRefs + extends z.output {} diff --git a/packages/design-system/example/components/ContentPage/Header/Header.tsx b/packages/design-system/example/components/ContentPage/Header/Header.tsx index 2c129e729..773373e0c 100644 --- a/packages/design-system/example/components/ContentPage/Header/Header.tsx +++ b/packages/design-system/example/components/ContentPage/Header/Header.tsx @@ -1,7 +1,7 @@ import { Button } from '@scandic-hotels/design-system/Button' -import { Chips } from '@scandic-hotels/design-system/Chips' import { ChipButton } from '@scandic-hotels/design-system/ChipButton' import { ChipLink } from '@scandic-hotels/design-system/ChipLink' +import { Chips } from '@scandic-hotels/design-system/Chips' import { Typography } from '@scandic-hotels/design-system/Typography' import styles from './header.module.css' @@ -10,8 +10,6 @@ import type { PressEvent } from 'react-aria-components' export function Header() { const onPress = (e: PressEvent) => alert(e.target.innerHTML) - const onClick = (e: React.MouseEvent) => - alert(`Link to: ${e.currentTarget.href}`) return (
@@ -42,27 +40,27 @@ export function Header() {
Restaurants and bars - + Kristianshavn Tivoli Gardens - + See all Copenhagen See all Copenhagen - + See all Copenhagen See all Copenhagen - + See all Copenhagen See all Copenhagen - + See all Copenhagen See all Copenhagen - + See all Copenhagen diff --git a/packages/design-system/lib/components/ChipButton/chip-button.module.css b/packages/design-system/lib/components/ChipButton/chip-button.module.css index 10479a692..35dab7ff4 100644 --- a/packages/design-system/lib/components/ChipButton/chip-button.module.css +++ b/packages/design-system/lib/components/ChipButton/chip-button.module.css @@ -26,7 +26,6 @@ background-color: var(--Surface-Primary-Hover); } -.Outlined:focus, .Outlined:active { border-color: var(--Border-Interactive-Selected); } diff --git a/packages/design-system/lib/components/ChipLink/ChipLink.stories.tsx b/packages/design-system/lib/components/ChipLink/ChipLink.stories.tsx index a18688bfb..385fdd763 100644 --- a/packages/design-system/lib/components/ChipLink/ChipLink.stories.tsx +++ b/packages/design-system/lib/components/ChipLink/ChipLink.stories.tsx @@ -1,10 +1,10 @@ import type { Meta, StoryObj } from '@storybook/react' -import { ChipLink } from './ChipLink.tsx' import { MaterialIcon } from '../Icons/MaterialIcon' +import { ChipLink } from './ChipLink.tsx' const meta: Meta = { - title: 'Components/Chip/ChipLInk 🚧', + title: 'Components/Chip/ChipLink', component: ChipLink, } @@ -15,7 +15,7 @@ type Story = StoryObj export const Default: Story = { args: { href: '/', - onClick: (e) => e.preventDefault(), + onPress: (e) => console.log(e), children: ( <> Link Chip diff --git a/packages/design-system/lib/components/ChipLink/ChipLink.tsx b/packages/design-system/lib/components/ChipLink/ChipLink.tsx index f79d8ee3b..1c2d3cd50 100644 --- a/packages/design-system/lib/components/ChipLink/ChipLink.tsx +++ b/packages/design-system/lib/components/ChipLink/ChipLink.tsx @@ -1,19 +1,24 @@ +import { cx } from 'class-variance-authority' import { Typography } from '../Typography' import styles from './chip-link.module.css' import type { PropsWithChildren } from 'react' +import { + Link as LinkRAC, + LinkProps as LinkRACProps, +} from 'react-aria-components' export function ChipLink({ children, className, ...props -}: PropsWithChildren>) { +}: PropsWithChildren) { return ( - + {children} - + ) } diff --git a/packages/design-system/lib/components/ChipLink/chip-link.module.css b/packages/design-system/lib/components/ChipLink/chip-link.module.css index 24fbca78f..6c0d5bf56 100644 --- a/packages/design-system/lib/components/ChipLink/chip-link.module.css +++ b/packages/design-system/lib/components/ChipLink/chip-link.module.css @@ -1,18 +1,21 @@ .chip { background-color: var(--Component-Button-Inverted-Fill-Default); - border-color: var(--Component-Button-Inverted-Border-Default); - border-style: solid; - border-width: 1px; + border: 1px solid var(--Component-Button-Inverted-Border-Default); border-radius: var(--Corner-radius-sm); padding: var(--Space-x1) var(--Space-x15); color: var(--Text-Interactive-Default); display: inline-flex; align-items: center; + justify-content: center; + cursor: pointer; + gap: var(--Space-x05); } .chip:hover { - /* TODO: change to proper Component-variable once it is available */ - background-color: var(--Scandic-Peach-10); - /* TODO: change to proper Component-variable once it is available */ - color: var(--Scandic-Red-100); + background-color: var(--Surface-Primary-Hover-Accent); +} + +.chip:focus { + outline-offset: 4px; + outline-color: var(--Border-Interactive-Focus); }