diff --git a/apps/scandic-web/.env.local.example b/apps/scandic-web/.env.local.example index 4a5cd636e..30d67d4fe 100644 --- a/apps/scandic-web/.env.local.example +++ b/apps/scandic-web/.env.local.example @@ -71,4 +71,4 @@ DTMC_ENTRA_ID_CLIENT="" DTMC_ENTRA_ID_ISSUER="" DTMC_ENTRA_ID_SECRET="" -CAMPAIGN_PAGES_ENABLED="0" # 0 - disabled, 1 - enabled +PROMO_CAMPAIGN_PAGES_ENABLED="0" # 0 - disabled, 1 - enabled diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/@breadcrumbs/promo_campaign_page/[uid]/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/@breadcrumbs/promo_campaign_page/[uid]/page.tsx new file mode 100644 index 000000000..0a6a269ac --- /dev/null +++ b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/@breadcrumbs/promo_campaign_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 PromoCampaignPageBreadcrumbs() { + const variants: Pick = { + color: "Background/Primary", + 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 index f895003b3..86a19dd27 100644 --- 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 @@ -1,18 +1,11 @@ -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" import styles from "./page.module.css" export default async function CampaignOverviewPagePage() { - if (!env.CAMPAIGN_PAGES_ENABLED) { - notFound() - } - return (
}> diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_page/[uid]/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_page/[uid]/page.tsx index 550ce442d..c9b5b5e7a 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_page/[uid]/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_page/[uid]/page.tsx @@ -1,8 +1,5 @@ -import { notFound } from "next/navigation" import { Suspense } from "react" -import { env } from "@/env/server" - import CampaignPage from "@/components/ContentType/CampaignPage" import CampaignPageSkeleton from "@/components/ContentType/CampaignPage/CampaignPageSkeleton" @@ -11,10 +8,6 @@ import styles from "./page.module.css" export { generateMetadata } from "@/utils/metadata/generateMetadata" export default async function CampaignPagePage() { - if (!env.CAMPAIGN_PAGES_ENABLED) { - return notFound() - } - return (
}> diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/promo_campaign_page/[uid]/page.module.css b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/promo_campaign_page/[uid]/page.module.css new file mode 100644 index 000000000..86b95c989 --- /dev/null +++ b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/promo_campaign_page/[uid]/page.module.css @@ -0,0 +1,3 @@ +.page { + background-color: var(--Background-Primary); +} diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/promo_campaign_page/[uid]/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/promo_campaign_page/[uid]/page.tsx new file mode 100644 index 000000000..8c78407b8 --- /dev/null +++ b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/promo_campaign_page/[uid]/page.tsx @@ -0,0 +1,24 @@ +import { notFound } from "next/navigation" +import { Suspense } from "react" + +import { env } from "@/env/server" + +import PromoCampaignPage from "@/components/ContentType/PromoCampaignPage" +import PromoCampaignPageSkeleton from "@/components/ContentType/PromoCampaignPage/PromoCampaignPageSkeleton" + +import styles from "./page.module.css" + +export { generateMetadata } from "@/utils/metadata/generateMetadata" + +export default async function PromoCampaignPagePage() { + if (!env.PROMO_CAMPAIGN_PAGES_ENABLED) { + return notFound() + } + return ( +
+ }> + + +
+ ) +} diff --git a/apps/scandic-web/components/ContentType/PromoCampaignPage/PromoCampaignPageSkeleton.tsx b/apps/scandic-web/components/ContentType/PromoCampaignPage/PromoCampaignPageSkeleton.tsx new file mode 100644 index 000000000..fed3715bd --- /dev/null +++ b/apps/scandic-web/components/ContentType/PromoCampaignPage/PromoCampaignPageSkeleton.tsx @@ -0,0 +1,31 @@ +"use client" + +import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer" +import { Typography } from "@scandic-hotels/design-system/Typography" + +import styles from "./promoCampaignPage.module.css" + +export default function PromoCampaignPageSkeleton() { + return ( +
+ + +
+
+ + + + + + +
+
+
+ + + + +
+
+ ) +} diff --git a/apps/scandic-web/components/ContentType/PromoCampaignPage/index.tsx b/apps/scandic-web/components/ContentType/PromoCampaignPage/index.tsx new file mode 100644 index 000000000..fbb9ed954 --- /dev/null +++ b/apps/scandic-web/components/ContentType/PromoCampaignPage/index.tsx @@ -0,0 +1,43 @@ +import { notFound } from "next/navigation" +import { Suspense } from "react" + +import { Typography } from "@scandic-hotels/design-system/Typography" +import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK" + +import { getPromoCampaignPage } from "@/lib/trpc/memoizedRequests" + +import PromoCampaignPageSkeleton from "./PromoCampaignPageSkeleton" + +import styles from "./promoCampaignPage.module.css" + +export default async function PromoCampaignPage() { + const pageData = await getPromoCampaignPage() + if (!pageData) { + notFound() + } + //const isUserLoggedIn = await isLoggedInUser() + const { promo_campaign_page, tracking } = pageData + const { heading, subheading } = promo_campaign_page + + return ( + <> + }> +
+
+
+ +

{heading}

+
+ {subheading ? ( + +

{subheading}

+
+ ) : null} +
+
+
+
+ + + ) +} diff --git a/apps/scandic-web/components/ContentType/PromoCampaignPage/promoCampaignPage.module.css b/apps/scandic-web/components/ContentType/PromoCampaignPage/promoCampaignPage.module.css new file mode 100644 index 000000000..8a8ce1bca --- /dev/null +++ b/apps/scandic-web/components/ContentType/PromoCampaignPage/promoCampaignPage.module.css @@ -0,0 +1,29 @@ +.pageContainer { + display: grid; + gap: var(--Space-x6); + padding-bottom: var(--Space-x7); + width: var(--max-width-content); + margin: 0 auto; +} + +.intro { + display: grid; + gap: var(--Space-x5); +} + +.headingWrapper { + display: grid; + gap: var(--Space-x2); +} + +.heading { + color: var(--Text-Heading); + text-wrap: balance; + hyphens: auto; +} + +@media screen and (min-width: 1367px) { + .pageContainer { + gap: var(--Space-x9); + } +} diff --git a/apps/scandic-web/env/server.ts b/apps/scandic-web/env/server.ts index 45bd0ae25..00acfc19a 100644 --- a/apps/scandic-web/env/server.ts +++ b/apps/scandic-web/env/server.ts @@ -149,7 +149,7 @@ export const env = createEnv({ * We currently have the secret in local and test environments. */ DTMC_ENTRA_ID_SECRET: z.string().optional(), - CAMPAIGN_PAGES_ENABLED: z + PROMO_CAMPAIGN_PAGES_ENABLED: z .string() .refine((s) => s === "1" || s === "0") .transform((s) => s === "1") @@ -247,7 +247,7 @@ export const env = createEnv({ DTMC_ENTRA_ID_CLIENT: process.env.DTMC_ENTRA_ID_CLIENT, DTMC_ENTRA_ID_ISSUER: process.env.DTMC_ENTRA_ID_ISSUER, DTMC_ENTRA_ID_SECRET: process.env.DTMC_ENTRA_ID_SECRET, - CAMPAIGN_PAGES_ENABLED: process.env.CAMPAIGN_PAGES_ENABLED, + PROMO_CAMPAIGN_PAGES_ENABLED: process.env.PROMO_CAMPAIGN_PAGES_ENABLED, WEBVIEW_SHOW_OVERVIEW: process.env.WEBVIEW_SHOW_OVERVIEW, ENABLE_NEW_OVERVIEW_SECTION: process.env.ENABLE_NEW_OVERVIEW_SECTION, CHATBOT_LIVE_LANGS: process.env.CHATBOT_LIVE_LANGS, diff --git a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts index 084272f58..cb2538622 100644 --- a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts +++ b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts @@ -232,3 +232,10 @@ export const getCampaignOverviewPage = cache( return caller.contentstack.campaignOverviewPage.get() } ) + +export const getPromoCampaignPage = cache( + async function getMemoizedPromoCampaignPage() { + const caller = await serverClient() + return caller.contentstack.promoCampaignPage.get() + } +) diff --git a/packages/tracking/lib/types.ts b/packages/tracking/lib/types.ts index e3f765ba2..95e7fa552 100644 --- a/packages/tracking/lib/types.ts +++ b/packages/tracking/lib/types.ts @@ -11,6 +11,7 @@ export enum TrackingChannelEnum { "campaign" = "campaign", "hotels" = "hotels", "homepage" = "homepage", + "promo-campaign" = "promo-campaign", } export type TrackingChannel = keyof typeof TrackingChannelEnum diff --git a/packages/trpc/lib/enums/contentType.ts b/packages/trpc/lib/enums/contentType.ts index 8c0a99f24..2522f54d7 100644 --- a/packages/trpc/lib/enums/contentType.ts +++ b/packages/trpc/lib/enums/contentType.ts @@ -11,4 +11,5 @@ export enum PageContentTypeEnum { hotelPage = "hotel_page", loyaltyPage = "loyalty_page", startPage = "start_page", + promoCampaignPage = "promo_campaign_page", } diff --git a/packages/trpc/lib/graphql/Fragments/Breadcrumbs/PromoCampaignPage.graphql b/packages/trpc/lib/graphql/Fragments/Breadcrumbs/PromoCampaignPage.graphql new file mode 100644 index 000000000..e8b4af0b2 --- /dev/null +++ b/packages/trpc/lib/graphql/Fragments/Breadcrumbs/PromoCampaignPage.graphql @@ -0,0 +1,24 @@ +#import "../System.graphql" + +fragment PromoCampaignPageBreadcrumb on PromoCampaignPage { + web { + breadcrumbs { + title + } + } + system { + ...System + } + url +} + +fragment PromoCampaignPageBreadcrumbRef on PromoCampaignPage { + web { + breadcrumbs { + title + } + } + system { + ...System + } +} diff --git a/packages/trpc/lib/graphql/Fragments/PageLink/PromoCampaignPageLink.graphql b/packages/trpc/lib/graphql/Fragments/PageLink/PromoCampaignPageLink.graphql new file mode 100644 index 000000000..155471960 --- /dev/null +++ b/packages/trpc/lib/graphql/Fragments/PageLink/PromoCampaignPageLink.graphql @@ -0,0 +1,9 @@ +#import "../System.graphql" + +fragment PromoCampaignPageLink on PromoCampaignPage { + title + url + system { + ...System + } +} diff --git a/packages/trpc/lib/graphql/Fragments/PromoCampaignPage/Ref.graphql b/packages/trpc/lib/graphql/Fragments/PromoCampaignPage/Ref.graphql new file mode 100644 index 000000000..dfda0e620 --- /dev/null +++ b/packages/trpc/lib/graphql/Fragments/PromoCampaignPage/Ref.graphql @@ -0,0 +1,7 @@ +#import "../System.graphql" + +fragment PromoCampaignPageRef on PromoCampaignPage { + system { + ...System + } +} diff --git a/packages/trpc/lib/graphql/Query/Breadcrumbs/PromoCampaignPage.graphql b/packages/trpc/lib/graphql/Query/Breadcrumbs/PromoCampaignPage.graphql new file mode 100644 index 000000000..a0ccdc597 --- /dev/null +++ b/packages/trpc/lib/graphql/Query/Breadcrumbs/PromoCampaignPage.graphql @@ -0,0 +1,29 @@ +#import "../../Fragments/Breadcrumbs/Breadcrumbs.graphql" +#import "../../Fragments/System.graphql" + +query GetPromoCampaignPageBreadcrumbs($locale: String!, $uid: String!) { + promo_campaign_page(locale: $locale, uid: $uid) { + url + web { + breadcrumbs { + ...Breadcrumbs + } + } + system { + ...System + } + } +} + +query GetPromoCampaignPageBreadcrumbsRefs($locale: String!, $uid: String!) { + promo_campaign_page(locale: $locale, uid: $uid) { + web { + breadcrumbs { + ...BreadcrumbsRefs + } + } + system { + ...System + } + } +} diff --git a/packages/trpc/lib/graphql/Query/PageSettings.graphql b/packages/trpc/lib/graphql/Query/PageSettings.graphql index e83fe4a17..627ee5b69 100644 --- a/packages/trpc/lib/graphql/Query/PageSettings.graphql +++ b/packages/trpc/lib/graphql/Query/PageSettings.graphql @@ -95,3 +95,11 @@ query GetStartPageSettings($uid: String!, $locale: String!) { } } } + +query GetPromoCampaignPageSettings($uid: String!, $locale: String!) { + page: promo_campaign_page(uid: $uid, locale: $locale) { + settings: page_settings { + ...PageSettings + } + } +} diff --git a/packages/trpc/lib/graphql/Query/PromoCampaignPage/Metadata.graphql b/packages/trpc/lib/graphql/Query/PromoCampaignPage/Metadata.graphql new file mode 100644 index 000000000..10d10fcda --- /dev/null +++ b/packages/trpc/lib/graphql/Query/PromoCampaignPage/Metadata.graphql @@ -0,0 +1,19 @@ +#import "../../Fragments/Metadata.graphql" +#import "../../Fragments/System.graphql" + +query GetPromoCampaignPageMetadata($locale: String!, $uid: String!) { + promo_campaign_page(locale: $locale, uid: $uid) { + web { + breadcrumbs { + title + } + seo_metadata { + ...Metadata + } + } + heading + system { + ...System + } + } +} diff --git a/packages/trpc/lib/graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql b/packages/trpc/lib/graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql new file mode 100644 index 000000000..cd5b8253f --- /dev/null +++ b/packages/trpc/lib/graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql @@ -0,0 +1,64 @@ +#import "../../Fragments/System.graphql" + +#import "../../Fragments/CampaignPage/IncludedHotels.graphql" +#import "../../Fragments/CampaignPage/Hero.graphql" + +#import "../../Fragments/Blocks/Accordion.graphql" +#import "../../Fragments/Blocks/Essentials.graphql" +#import "../../Fragments/Blocks/CarouselCards.graphql" +#import "../../Fragments/Blocks/HotelListing.graphql" + +query GetPromoCampaignPage($locale: String!, $uid: String!) { + promo_campaign_page(uid: $uid, locale: $locale) { + title + heading + subheading + page_settings { + booking_code + } + campaign_type + promo_code + startdate + enddate + system { + ...System + created_at + updated_at + } + } + trackingProps: promo_campaign_page(locale: "en", uid: $uid) { + url + } +} + +query GetPromoCampaignPageRefs($locale: String!, $uid: String!) { + promo_campaign_page(locale: $locale, uid: $uid) { + system { + ...System + } + } +} + +query GetDaDeEnUrlsPromoCampaignPage($uid: String!) { + de: promo_campaign_page(locale: "de", uid: $uid) { + url + } + en: promo_campaign_page(locale: "en", uid: $uid) { + url + } + da: promo_campaign_page(locale: "da", uid: $uid) { + url + } +} + +query GetFiNoSvUrlsPromoCampaignPage($uid: String!) { + fi: promo_campaign_page(locale: "fi", uid: $uid) { + url + } + no: promo_campaign_page(locale: "no", uid: $uid) { + url + } + sv: promo_campaign_page(locale: "sv", uid: $uid) { + url + } +} diff --git a/packages/trpc/lib/graphql/Query/ResolveEntry.graphql b/packages/trpc/lib/graphql/Query/ResolveEntry.graphql index c0de4348a..5141e9f5c 100644 --- a/packages/trpc/lib/graphql/Query/ResolveEntry.graphql +++ b/packages/trpc/lib/graphql/Query/ResolveEntry.graphql @@ -101,3 +101,14 @@ query EntryByUrlBatch2($locale: String!, $url: String!) { total } } + +query EntryByUrlBatch3($locale: String!, $url: String!) { + all_promo_campaign_page(where: { url: $url }, locale: $locale) { + items { + system { + ...System + } + } + total + } +} diff --git a/packages/trpc/lib/routers/contentstack/breadcrumbs/query.ts b/packages/trpc/lib/routers/contentstack/breadcrumbs/query.ts index 72ed4c7ec..aa1a8349e 100644 --- a/packages/trpc/lib/routers/contentstack/breadcrumbs/query.ts +++ b/packages/trpc/lib/routers/contentstack/breadcrumbs/query.ts @@ -45,6 +45,10 @@ import { GetLoyaltyPageBreadcrumbs, GetLoyaltyPageBreadcrumbsRefs, } from "../../../graphql/Query/Breadcrumbs/LoyaltyPage.graphql" +import { + GetPromoCampaignPageBreadcrumbs, + GetPromoCampaignPageBreadcrumbsRefs, +} from "../../../graphql/Query/Breadcrumbs/PromoCampaignPage.graphql" import { request } from "../../../graphql/request" import { contentstackExtendedProcedureUID } from "../../../procedures" import { generateRefsResponseTag } from "../../../utils/generateTag" @@ -259,6 +263,17 @@ export const breadcrumbsQueryRouter = router({ }, variables ) + case PageContentTypeEnum.promoCampaignPage: + return await getBreadcrumbs<{ + promo_campaign_page: RawBreadcrumbsSchema + }>( + { + dataKey: "promo_campaign_page", + refQuery: GetPromoCampaignPageBreadcrumbsRefs, + query: GetPromoCampaignPageBreadcrumbs, + }, + variables + ) default: return [] } diff --git a/packages/trpc/lib/routers/contentstack/index.ts b/packages/trpc/lib/routers/contentstack/index.ts index 32d2abd37..780bd3165 100644 --- a/packages/trpc/lib/routers/contentstack/index.ts +++ b/packages/trpc/lib/routers/contentstack/index.ts @@ -16,6 +16,7 @@ import { loyaltyPageRouter } from "./loyaltyPage" import { metadataRouter } from "./metadata" import { pageSettingsRouter } from "./pageSettings" import { partnerRouter } from "./partner" +import { promoCampaignPageRouter } from "./promoCampaignPage" import { rewardRouter } from "./reward" import { startPageRouter } from "./startPage" @@ -39,4 +40,5 @@ export const contentstackRouter = router({ loyaltyLevels: loyaltyLevelRouter, startPage: startPageRouter, partner: partnerRouter, + promoCampaignPage: promoCampaignPageRouter, }) diff --git a/packages/trpc/lib/routers/contentstack/languageSwitcher/utils.ts b/packages/trpc/lib/routers/contentstack/languageSwitcher/utils.ts index 6efeb3383..0750c40ec 100644 --- a/packages/trpc/lib/routers/contentstack/languageSwitcher/utils.ts +++ b/packages/trpc/lib/routers/contentstack/languageSwitcher/utils.ts @@ -46,6 +46,10 @@ import { GetDaDeEnUrlsLoyaltyPage, GetFiNoSvUrlsLoyaltyPage, } from "../../../graphql/Query/LoyaltyPage/LoyaltyPage.graphql" +import { + GetDaDeEnUrlsPromoCampaignPage, + GetFiNoSvUrlsPromoCampaignPage, +} from "../../../graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql" import { GetDaDeEnUrlsStartPage, GetFiNoSvUrlsStartPage, @@ -139,6 +143,10 @@ export async function getUrlsOfAllLanguages( daDeEnDocument = GetDaDeEnUrlsStartPage fiNoSvDocument = GetFiNoSvUrlsStartPage break + case PageContentTypeEnum.promoCampaignPage: + daDeEnDocument = GetDaDeEnUrlsPromoCampaignPage + fiNoSvDocument = GetFiNoSvUrlsPromoCampaignPage + break default: languageSwitcherLogger.error( `Trying to get a content type that is not supported, ${contentType}` diff --git a/packages/trpc/lib/routers/contentstack/metadata/query.ts b/packages/trpc/lib/routers/contentstack/metadata/query.ts index a4c04b90d..a51fe5d0c 100644 --- a/packages/trpc/lib/routers/contentstack/metadata/query.ts +++ b/packages/trpc/lib/routers/contentstack/metadata/query.ts @@ -15,6 +15,7 @@ import { GetDestinationCountryPageMetadata } from "../../../graphql/Query/Destin import { GetDestinationOverviewPageMetadata } from "../../../graphql/Query/DestinationOverviewPage/Metadata.graphql" import { GetHotelPageMetadata } from "../../../graphql/Query/HotelPage/Metadata.graphql" import { GetLoyaltyPageMetadata } from "../../../graphql/Query/LoyaltyPage/Metadata.graphql" +import { GetPromoCampaignPageMetadata } from "../../../graphql/Query/PromoCampaignPage/Metadata.graphql" import { GetStartPageMetadata } from "../../../graphql/Query/StartPage/Metadata.graphql" import { request } from "../../../graphql/request" import { contentStackUidWithServiceProcedure } from "../../../procedures" @@ -202,6 +203,12 @@ export const metadataQueryRouter = router({ }>(GetStartPageMetadata, variables) data = startPageResponse.start_page break + case PageContentTypeEnum.promoCampaignPage: + const promoCampaignPageResponse = await fetchMetadata<{ + promo_campaign_page: RawMetadataSchema + }>(GetPromoCampaignPageMetadata, variables) + data = promoCampaignPageResponse.promo_campaign_page + break default: break } diff --git a/packages/trpc/lib/routers/contentstack/pageSettings/query.ts b/packages/trpc/lib/routers/contentstack/pageSettings/query.ts index 13a04ce15..6ea2a1c59 100644 --- a/packages/trpc/lib/routers/contentstack/pageSettings/query.ts +++ b/packages/trpc/lib/routers/contentstack/pageSettings/query.ts @@ -14,6 +14,7 @@ import { GetDestinationOverviewPageSettings, GetHotelPageSettings, GetLoyaltyPageSettings, + GetPromoCampaignPageSettings, GetStartPageSettings, } from "../../../graphql/Query/PageSettings.graphql" import { request } from "../../../graphql/request" @@ -87,4 +88,5 @@ const graphqlQueriesForContentType: Record = { [PageContentTypeEnum.hotelPage]: GetHotelPageSettings, [PageContentTypeEnum.loyaltyPage]: GetLoyaltyPageSettings, [PageContentTypeEnum.startPage]: GetStartPageSettings, + [PageContentTypeEnum.promoCampaignPage]: GetPromoCampaignPageSettings, } diff --git a/packages/trpc/lib/routers/contentstack/promoCampaignPage/index.ts b/packages/trpc/lib/routers/contentstack/promoCampaignPage/index.ts new file mode 100644 index 000000000..e3f6b0b77 --- /dev/null +++ b/packages/trpc/lib/routers/contentstack/promoCampaignPage/index.ts @@ -0,0 +1,6 @@ +import { mergeRouters } from "../../.." +import { promoCampaignPageQueryRouter } from "./query" + +export const promoCampaignPageRouter = mergeRouters( + promoCampaignPageQueryRouter +) diff --git a/packages/trpc/lib/routers/contentstack/promoCampaignPage/output.ts b/packages/trpc/lib/routers/contentstack/promoCampaignPage/output.ts new file mode 100644 index 000000000..300c6a226 --- /dev/null +++ b/packages/trpc/lib/routers/contentstack/promoCampaignPage/output.ts @@ -0,0 +1,56 @@ +import { z } from "zod" + +import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator" + +import { systemSchema } from "../schemas/system" + +export const CAMPAIGN_TYPES = { + TIER: "TIER", + POINT: "POINT", +} as const + +export const promoCampaignPageSchema = z + .object({ + promo_campaign_page: z.object({ + title: z.string(), + heading: z.string(), + subheading: z.string().nullish(), + page_settings: z + .object({ + booking_code: z.string().nullish(), + }) + .nullish(), + campaign_type: z.nativeEnum(CAMPAIGN_TYPES), + promo_code: z.string(), + startdate: nullableStringValidator, + enddate: nullableStringValidator, + system: systemSchema.merge( + z.object({ + created_at: z.string(), + updated_at: z.string(), + }) + ), + }), + trackingProps: z.object({ + url: z.string(), + }), + }) + .transform(({ promo_campaign_page, ...data }) => { + const { page_settings, ...promoCampaignPageData } = promo_campaign_page + const bookingCode = page_settings?.booking_code || null + + return { + ...data, + promo_campaign_page: { + bookingCode, + ...promoCampaignPageData, + }, + } + }) + +/** REFS */ +export const promoCampaignPageRefsSchema = z.object({ + promo_campaign_page: z.object({ + system: systemSchema, + }), +}) diff --git a/packages/trpc/lib/routers/contentstack/promoCampaignPage/query.ts b/packages/trpc/lib/routers/contentstack/promoCampaignPage/query.ts new file mode 100644 index 000000000..6c7e64c2f --- /dev/null +++ b/packages/trpc/lib/routers/contentstack/promoCampaignPage/query.ts @@ -0,0 +1,122 @@ +import { createCounter } from "@scandic-hotels/common/telemetry" + +import { router } from "../../.." +import { notFound } from "../../../errors" +import { + GetPromoCampaignPage, + GetPromoCampaignPageRefs, +} from "../../../graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql" +import { request } from "../../../graphql/request" +import { contentStackUidWithServiceProcedure } from "../../../procedures" +import { generateRefsResponseTag } from "../../../utils/generateTag" +import { promoCampaignPageRefsSchema, promoCampaignPageSchema } from "./output" +import { generatePageTags } from "./utils" + +import type { + GetPromoCampaignPageData, + GetPromoCampaignPageRefsData, +} from "../../../types/promoCampaignPage" +import type { TrackingPageData } from "../../types" + +export const promoCampaignPageQueryRouter = router({ + get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => { + const { lang, uid } = ctx + + const getPromoCampaignPageRefsCounter = createCounter( + "trpc.contentstack", + "promoCampaignPage.get.refs" + ) + const metricsGetPromoCampaignPageRefs = + getPromoCampaignPageRefsCounter.init({ + lang, + uid, + }) + + metricsGetPromoCampaignPageRefs.start() + + const refsResponse = await request( + GetPromoCampaignPageRefs, + { locale: lang, uid }, + { + key: generateRefsResponseTag(lang, uid), + ttl: "max", + } + ) + if (!refsResponse.data) { + const notFoundError = notFound(refsResponse) + metricsGetPromoCampaignPageRefs.noDataError() + throw notFoundError + } + + const validatedRefsData = promoCampaignPageRefsSchema.safeParse( + refsResponse.data + ) + if (!validatedRefsData.success) { + metricsGetPromoCampaignPageRefs.validationError(validatedRefsData.error) + return null + } + + metricsGetPromoCampaignPageRefs.success() + + const tags = generatePageTags(validatedRefsData.data, lang) + + const getPromoCampaignPageCounter = createCounter( + "trpc.contentstack", + "promoCampaignPage.get" + ) + const metricsGetPromoCampaignPage = getPromoCampaignPageCounter.init({ + lang, + uid, + }) + + metricsGetPromoCampaignPage.start() + + const response = await request( + GetPromoCampaignPage, + { + locale: lang, + uid, + }, + { + key: tags, + ttl: "max", + } + ) + if (!response.data) { + const notFoundError = notFound(response) + metricsGetPromoCampaignPage.noDataError() + throw notFoundError + } + + const validatedResponse = promoCampaignPageSchema.safeParse(response.data) + + if (!validatedResponse.success) { + metricsGetPromoCampaignPage.validationError(validatedResponse.error) + return null + } + + const { promo_campaign_page, trackingProps } = validatedResponse.data + + metricsGetPromoCampaignPage.success() + + const system = promo_campaign_page.system + const pageName = trackingProps.url + + const tracking: TrackingPageData = { + pageId: system.uid, + domainLanguage: system.locale, + publishDate: system.updated_at, + createDate: system.created_at, + channel: "promo-campaign", + pageType: "promocampaignpage", + pageName, + siteSections: pageName, + siteVersion: "new-web", + } + + return { + promo_campaign_page, + tracking, + } + }), +}) diff --git a/packages/trpc/lib/routers/contentstack/promoCampaignPage/utils.ts b/packages/trpc/lib/routers/contentstack/promoCampaignPage/utils.ts new file mode 100644 index 000000000..9384b523c --- /dev/null +++ b/packages/trpc/lib/routers/contentstack/promoCampaignPage/utils.ts @@ -0,0 +1,23 @@ +import { generateTag, generateTagsFromSystem } from "../../../utils/generateTag" + +import type { Lang } from "@scandic-hotels/common/constants/language" + +import type { PromoCampaignPageRefs } from "../../../types/promoCampaignPage" +import type { System } from "../schemas/system" + +export function generatePageTags( + validatedData: PromoCampaignPageRefs, + lang: Lang +): string[] { + const connections = getConnections(validatedData) + return [ + generateTagsFromSystem(lang, connections), + generateTag(lang, validatedData.promo_campaign_page.system.uid), + ].flat() +} + +export function getConnections({ promo_campaign_page }: PromoCampaignPageRefs) { + const connections: System["system"][] = [promo_campaign_page.system] + + return connections +} diff --git a/packages/trpc/lib/routers/types.ts b/packages/trpc/lib/routers/types.ts index 0c843e1b6..186976c68 100644 --- a/packages/trpc/lib/routers/types.ts +++ b/packages/trpc/lib/routers/types.ts @@ -26,6 +26,7 @@ type TrackingSDKChannel = | "campaign" | "hotels" | "homepage" + | "promo-campaign" export type TrackingUserData = | { diff --git a/packages/trpc/lib/types/entry.ts b/packages/trpc/lib/types/entry.ts index e43da4cb7..75bd50962 100644 --- a/packages/trpc/lib/types/entry.ts +++ b/packages/trpc/lib/types/entry.ts @@ -25,4 +25,5 @@ export const validateEntryResolveSchema = z.object({ all_destination_country_page: entryResolveSchema, all_destination_city_page: entryResolveSchema, all_start_page: entryResolveSchema, + all_promo_campaign_page: entryResolveSchema, }) diff --git a/packages/trpc/lib/types/promoCampaignPage.ts b/packages/trpc/lib/types/promoCampaignPage.ts new file mode 100644 index 000000000..866239c22 --- /dev/null +++ b/packages/trpc/lib/types/promoCampaignPage.ts @@ -0,0 +1,29 @@ +import type { z } from "zod" + +import type { + promoCampaignPageRefsSchema, + promoCampaignPageSchema, +} from "../routers/contentstack/promoCampaignPage/output" + +export namespace PromoCampaignPageEnum { + export namespace ContentStack { + export const enum blocks { + Accordion = "PromoCampaignPageBlocksAccordion", + } + } +} + +export interface GetPromoCampaignPageData + extends z.input {} + +export interface PromoCampaignPage + extends z.output {} + +export type PromoCampaignPageData = PromoCampaignPage["promo_campaign_page"] + +/* REFS */ +export interface GetPromoCampaignPageRefsData + extends z.input {} + +export interface PromoCampaignPageRefs + extends z.output {} diff --git a/packages/trpc/lib/utils/entry.ts b/packages/trpc/lib/utils/entry.ts index 9f2da65bd..18caf86f5 100644 --- a/packages/trpc/lib/utils/entry.ts +++ b/packages/trpc/lib/utils/entry.ts @@ -4,6 +4,7 @@ import { batchRequest } from "../graphql/batchRequest" import { EntryByUrlBatch1, EntryByUrlBatch2, + EntryByUrlBatch3, } from "../graphql/Query/ResolveEntry.graphql" import { validateEntryResolveSchema } from "../types/entry" @@ -34,10 +35,16 @@ export async function resolve(url: string, lang = Lang.en) { key: cacheKey, }, }, + { + document: EntryByUrlBatch3, + variables, + cacheOptions: { + ttl: "max", + key: cacheKey, + }, + }, ]) - const validatedData = validateEntryResolveSchema.safeParse(response.data) - if (!validatedData.success) { return { error: validatedData.error,