diff --git a/apps/scandic-web/components/ContentType/StaticPages/index.tsx b/apps/scandic-web/components/ContentType/StaticPages/index.tsx index f70b9beb0..bd1582273 100644 --- a/apps/scandic-web/components/ContentType/StaticPages/index.tsx +++ b/apps/scandic-web/components/ContentType/StaticPages/index.tsx @@ -2,6 +2,7 @@ import Link from "next/link" import { Suspense } from "react" import Blocks from "@/components/Blocks" +import HeaderDynamicContent from "@/components/Headers/DynamicContent" import Hero from "@/components/Hero" import MeetingPackageWidget from "@/components/MeetingPackageWidget" import Sidebar from "@/components/Sidebar" @@ -52,6 +53,9 @@ export default async function StaticPage({ {header.navigation_links ? ( ) : null} + {"dynamic_content" in header && header.dynamic_content ? ( + + ) : null} ) : null} diff --git a/apps/scandic-web/components/Sidebar/EmployeeBenefits/AuthCard/authCard.module.css b/apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/AuthCard/authCard.module.css similarity index 100% rename from apps/scandic-web/components/Sidebar/EmployeeBenefits/AuthCard/authCard.module.css rename to apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/AuthCard/authCard.module.css diff --git a/apps/scandic-web/components/Sidebar/EmployeeBenefits/AuthCard/index.tsx b/apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/AuthCard/index.tsx similarity index 100% rename from apps/scandic-web/components/Sidebar/EmployeeBenefits/AuthCard/index.tsx rename to apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/AuthCard/index.tsx diff --git a/apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/CallToActions/callToActions.module.css b/apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/CallToActions/callToActions.module.css new file mode 100644 index 000000000..bd002c2c8 --- /dev/null +++ b/apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/CallToActions/callToActions.module.css @@ -0,0 +1,7 @@ +.container { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--Spacing-x-one-and-half); + padding-bottom: var(--Spacing-x3); +} diff --git a/apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/CallToActions/index.tsx b/apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/CallToActions/index.tsx new file mode 100644 index 000000000..54242fe4a --- /dev/null +++ b/apps/scandic-web/components/DigitalTeamMemberCard/EmployeeBenefits/CallToActions/index.tsx @@ -0,0 +1,71 @@ +import React from "react" + +import { Typography } from "@scandic-hotels/design-system/Typography" + +import { login } from "@/constants/routes/handleAuth" +import { signup } from "@/constants/routes/signup" + +import { auth } from "@/auth" +import ButtonLink from "@/components/ButtonLink" +import { getIntl } from "@/i18n" +import { getLang } from "@/i18n/serverContext" +import { isValidSession } from "@/utils/session" + +import styles from "./callToActions.module.css" + +export default async function EmployeeBenefitsCallToActions() { + const session = await auth() + const intl = await getIntl() + const lang = getLang() + + if (!isValidSession(session)) { + return ( + <> +
+ +

+ {intl.formatMessage({ + defaultMessage: "Already a Scandic Friends account?", + })} +

+
+ + {intl.formatMessage({ + defaultMessage: "Log in and link employment", + })} + +
+ +
+ +

+ {intl.formatMessage({ + defaultMessage: "Don't have a Scandic Friends account yet?", + })} +

+
+ + {intl.formatMessage({ + defaultMessage: "Sign up and link employment", + })} + +
+ + ) + } + + // -- TODO [LOY-196] -- + // Handle case of authed user and already connected work account. + // Show member card modal. + + return ( +
+ {/*/ TODO [LOY-229]: Update href once we have new auth url. */} + + {intl.formatMessage({ + defaultMessage: "Link Employment", + })} + +
+ ) +} diff --git a/apps/scandic-web/components/Headers/DynamicContent/index.tsx b/apps/scandic-web/components/Headers/DynamicContent/index.tsx new file mode 100644 index 000000000..fdfa67fec --- /dev/null +++ b/apps/scandic-web/components/Headers/DynamicContent/index.tsx @@ -0,0 +1,27 @@ +import { Suspense } from "react" + +import EmployeeBenefitsCallToActions from "@/components/DigitalTeamMemberCard/EmployeeBenefits/CallToActions" +import LoadingSpinner from "@/components/LoadingSpinner" + +import type { HeaderDynamicContentProps } from "@/types/components/headers/dynamicContent" +import { DynamicContentEnum } from "@/types/enums/dynamicContent" + +export default function HeaderDynamicContent(props: HeaderDynamicContentProps) { + return ( + }> + + + ) +} + +function HeaderDynamicContentComponent(props: HeaderDynamicContentProps) { + const { component } = props + + switch (component) { + case DynamicContentEnum.Headers.components + .dtmc_employee_benefits_call_to_actions: + return + default: + return null + } +} diff --git a/apps/scandic-web/components/Sidebar/index.tsx b/apps/scandic-web/components/Sidebar/index.tsx index 72d62fbbf..ac1ad3c32 100644 --- a/apps/scandic-web/components/Sidebar/index.tsx +++ b/apps/scandic-web/components/Sidebar/index.tsx @@ -1,9 +1,9 @@ +import EmployeeBenefitsAuthCard from "@/components/DigitalTeamMemberCard/EmployeeBenefits/AuthCard" import JsonToHtml from "@/components/JsonToHtml" import ShortcutsList from "../Blocks/ShortcutsList" import Card from "../TempDesignSystem/Card" import TeaserCard from "../TempDesignSystem/TeaserCard" -import EmployeeBenefitsAuthCard from "./EmployeeBenefits/AuthCard" import JoinLoyaltyContact from "./JoinLoyalty" import MyPagesNavigation from "./MyPagesNavigation" diff --git a/apps/scandic-web/lib/graphql/Query/ContentPage/ContentPage.graphql b/apps/scandic-web/lib/graphql/Query/ContentPage/ContentPage.graphql index 0508fea97..3efa50899 100644 --- a/apps/scandic-web/lib/graphql/Query/ContentPage/ContentPage.graphql +++ b/apps/scandic-web/lib/graphql/Query/ContentPage/ContentPage.graphql @@ -26,6 +26,9 @@ query GetContentPage($locale: String!, $uid: String!) { header { heading preamble + dynamic_content { + component + } ...TopPrimaryButton_ContentPage ...NavigationLinks_ContentPage } @@ -84,6 +87,9 @@ query GetContentPageBlocksBatch2($locale: String!, $uid: String!) { query GetContentPageRefs($locale: String!, $uid: String!) { content_page(locale: $locale, uid: $uid) { header { + dynamic_content { + component + } ...NavigationLinksRef_ContentPage ...TopPrimaryButtonRef_ContentPage } diff --git a/apps/scandic-web/server/routers/contentstack/contentPage/output.ts b/apps/scandic-web/server/routers/contentstack/contentPage/output.ts index 631efcf4f..17eb6ebd6 100644 --- a/apps/scandic-web/server/routers/contentstack/contentPage/output.ts +++ b/apps/scandic-web/server/routers/contentstack/contentPage/output.ts @@ -26,6 +26,10 @@ import { import { tableSchema } from "../schemas/blocks/table" import { textColsRefsSchema, textColsSchema } from "../schemas/blocks/textCols" import { uspGridRefsSchema, uspGridSchema } from "../schemas/blocks/uspGrid" +import { + dynamicContentRefsSchema as headerDynamicContentRefsSchema, + dynamicContentSchema as headerDynamicContentSchema, +} from "../schemas/headers/dynamicContent" import { tempImageVaultAssetSchema } from "../schemas/imageVault" import { linkAndTitleSchema, @@ -210,6 +214,7 @@ export const contentPageSchema = z.object({ preamble: z.string(), top_primary_button: topPrimaryButtonSchema, navigation_links: navigationLinksSchema, + dynamic_content: headerDynamicContentSchema.optional(), }), meeting_package: z .object({ @@ -325,6 +330,7 @@ const contentPageSidebarRefsItem = z.discriminatedUnion("__typename", [ const contentPageHeaderRefs = z.object({ navigation_links: z.array(linkConnectionRefs), top_primary_button: linkConnectionRefs.nullable(), + dynamic_content: headerDynamicContentRefsSchema.optional(), }) export const contentPageRefsSchema = z.object({ diff --git a/apps/scandic-web/server/routers/contentstack/schemas/headers/dynamicContent.ts b/apps/scandic-web/server/routers/contentstack/schemas/headers/dynamicContent.ts new file mode 100644 index 000000000..fb513cc1f --- /dev/null +++ b/apps/scandic-web/server/routers/contentstack/schemas/headers/dynamicContent.ts @@ -0,0 +1,11 @@ +import { z } from "zod" + +import { DynamicContentEnum } from "@/types/enums/dynamicContent" + +export const dynamicContentSchema = z.object({ + component: z.enum(DynamicContentEnum.Headers.enums), +}) + +export const dynamicContentRefsSchema = z.object({ + component: z.enum(DynamicContentEnum.Headers.enums), +}) diff --git a/apps/scandic-web/types/components/headers/dynamicContent.ts b/apps/scandic-web/types/components/headers/dynamicContent.ts new file mode 100644 index 000000000..ef89d3f41 --- /dev/null +++ b/apps/scandic-web/types/components/headers/dynamicContent.ts @@ -0,0 +1,5 @@ +import type { z } from "zod" + +import type { dynamicContentSchema } from "@/server/routers/contentstack/schemas/headers/dynamicContent" + +export type HeaderDynamicContentProps = z.infer diff --git a/apps/scandic-web/types/enums/dynamicContent.ts b/apps/scandic-web/types/enums/dynamicContent.ts index e4f022f3e..8e29e2cc9 100644 --- a/apps/scandic-web/types/enums/dynamicContent.ts +++ b/apps/scandic-web/types/enums/dynamicContent.ts @@ -1,61 +1,90 @@ +// Helper type to get a union of an object's string literal values +type ValueOf = T[keyof T] + export namespace DynamicContentEnum { export namespace Blocks { - export const enum components { - current_benefits = "current_benefits", - earn_and_burn = "earn_and_burn", - expiring_points = "expiring_points", - how_it_works = "how_it_works", - jobylon_feed = "jobylon_feed", - loyalty_levels = "loyalty_levels", - membership_overview = "membership_overview", - my_points = "my_points", - next_benefits = "next_benefits", - overview_table = "overview_table", - points_overview = "points_overview", - previous_stays = "previous_stays", - sign_up_form = "sign_up_form", - sign_up_verification = "sign_up_verification", - soonest_stays = "soonest_stays", - upcoming_stays = "upcoming_stays", - sas_linked_account = "sas_linked_account", - sas_transfer_points = "sas_transfer_points", - sas_tier_comparison = "sas_tier_comparison", - } + export const components = { + current_benefits: "current_benefits", + earn_and_burn: "earn_and_burn", + expiring_points: "expiring_points", + how_it_works: "how_it_works", + jobylon_feed: "jobylon_feed", + loyalty_levels: "loyalty_levels", + membership_overview: "membership_overview", + my_points: "my_points", + next_benefits: "next_benefits", + overview_table: "overview_table", + points_overview: "points_overview", + previous_stays: "previous_stays", + sign_up_form: "sign_up_form", + sign_up_verification: "sign_up_verification", + soonest_stays: "soonest_stays", + upcoming_stays: "upcoming_stays", + sas_linked_account: "sas_linked_account", + sas_transfer_points: "sas_transfer_points", + sas_tier_comparison: "sas_tier_comparison", + } as const - /** Type needed to satisfy zod enum type */ - export const enums: [components, ...components[]] = [ - components.current_benefits, - components.earn_and_burn, - components.expiring_points, - components.how_it_works, - components.jobylon_feed, - components.loyalty_levels, - components.membership_overview, - components.my_points, - components.next_benefits, - components.overview_table, - components.points_overview, - components.previous_stays, - components.sign_up_form, - components.sign_up_verification, - components.soonest_stays, - components.upcoming_stays, - components.sas_linked_account, - components.sas_transfer_points, - components.sas_tier_comparison, + /** + * Represents one of the possible component string literal values from Blocks.components. + * e.g., "current_benefits" | "earn_and_burn" | ... + */ + type ComponentValue = ValueOf + + /** + * Array of all component string literal values from Blocks.components. + * Typed as a non-empty tuple `[ComponentValue, ...ComponentValue[]]`, + * to ensure it meets Zod's z.enum() requirements. + */ + export const enums = Object.values(components) as [ + ComponentValue, + ...ComponentValue[], + ] + } + + export namespace Headers { + export const components = { + dtmc_employee_benefits_call_to_actions: + "dtmc_employee_benefits_call_to_actions", + } as const + + /** + * Represents one of the possible component string literal values from Headers.components. + * e.g., "dtmc_employee_benefits_call_to_actions" + */ + type ComponentValue = ValueOf + + /** + * Array of all component string literal values from Headers.components. + * Typed as a non-empty tuple `[ComponentValue, ...ComponentValue[]]`, + * to ensure it meets Zod's z.enum() requirements. + */ + export const enums = Object.values(components) as [ + ComponentValue, + ...ComponentValue[], ] } export namespace Sidebar { - export const enum components { - my_pages_navigation = "my_pages_navigation", - employee_benefits_auth_card = "employee_benefits_auth_card", - } + export const components = { + my_pages_navigation: "my_pages_navigation", + employee_benefits_auth_card: "employee_benefits_auth_card", + } as const - /** Type needed to satisfy zod enum type */ - export const enums: [string, ...string[]] = [ - components.my_pages_navigation, - components.employee_benefits_auth_card, + /** + * Represents one of the possible component string literal values from Sidebar.components. + * e.g., "my_pages_navigation" | "employee_benefits_auth_card" + */ + type ComponentValue = ValueOf + + /** + * Array of all component string literal values from Sidebar.components. + * Typed as a non-empty tuple `[ComponentValue, ...ComponentValue[]]`, + * to ensure it meets Zod's z.enum() requirements. + */ + export const enums = Object.values(components) as [ + ComponentValue, + ...ComponentValue[], ] } }