Merged in LOY-188-employee-benefit-call-to-actions (pull request #1954)
feat(LOY-188): dynamic content support in content pages headers & use in DTMC employee benefits page * feat(LOY-188): add dynamic content handling for DTMC employee benefits page header * fix(LOY-188): change section to div in EmployeeBenefitsCallToActions component * refactor(LOY-188): switch to ButtonLink * refactor(LOY-188): replace enum with as const objects in DynamicContentEnum * chore(LOY-188): change ComponentValue type exports to internal scope in DynamicContentEnum * fix(EmployeeBenefitsCallToActions): replace div with fragment * chore(LOY-188): update translations Approved-by: Christian Andolf
This commit is contained in:
@@ -2,6 +2,7 @@ import Link from "next/link"
|
|||||||
import { Suspense } from "react"
|
import { Suspense } from "react"
|
||||||
|
|
||||||
import Blocks from "@/components/Blocks"
|
import Blocks from "@/components/Blocks"
|
||||||
|
import HeaderDynamicContent from "@/components/Headers/DynamicContent"
|
||||||
import Hero from "@/components/Hero"
|
import Hero from "@/components/Hero"
|
||||||
import MeetingPackageWidget from "@/components/MeetingPackageWidget"
|
import MeetingPackageWidget from "@/components/MeetingPackageWidget"
|
||||||
import Sidebar from "@/components/Sidebar"
|
import Sidebar from "@/components/Sidebar"
|
||||||
@@ -52,6 +53,9 @@ export default async function StaticPage({
|
|||||||
{header.navigation_links ? (
|
{header.navigation_links ? (
|
||||||
<LinkChips chips={header.navigation_links} />
|
<LinkChips chips={header.navigation_links} />
|
||||||
) : null}
|
) : null}
|
||||||
|
{"dynamic_content" in header && header.dynamic_content ? (
|
||||||
|
<HeaderDynamicContent {...header.dynamic_content} />
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
|
<>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
|
<p>
|
||||||
|
{intl.formatMessage({
|
||||||
|
defaultMessage: "Already a Scandic Friends account?",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
|
<ButtonLink href={login[lang]} size="Medium" variant="Tertiary">
|
||||||
|
{intl.formatMessage({
|
||||||
|
defaultMessage: "Log in and link employment",
|
||||||
|
})}
|
||||||
|
</ButtonLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.container}>
|
||||||
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
|
<p>
|
||||||
|
{intl.formatMessage({
|
||||||
|
defaultMessage: "Don't have a Scandic Friends account yet?",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
|
<ButtonLink href={signup[lang]} size="Medium" variant="Secondary">
|
||||||
|
{intl.formatMessage({
|
||||||
|
defaultMessage: "Sign up and link employment",
|
||||||
|
})}
|
||||||
|
</ButtonLink>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- TODO [LOY-196] --
|
||||||
|
// Handle case of authed user and already connected work account.
|
||||||
|
// Show member card modal.
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
{/*/ TODO [LOY-229]: Update href once we have new auth url. */}
|
||||||
|
<ButtonLink href="#" size="Medium" variant="Tertiary" color="Primary">
|
||||||
|
{intl.formatMessage({
|
||||||
|
defaultMessage: "Link Employment",
|
||||||
|
})}
|
||||||
|
</ButtonLink>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
27
apps/scandic-web/components/Headers/DynamicContent/index.tsx
Normal file
27
apps/scandic-web/components/Headers/DynamicContent/index.tsx
Normal file
@@ -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 (
|
||||||
|
<Suspense fallback={<LoadingSpinner />}>
|
||||||
|
<HeaderDynamicContentComponent {...props} />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function HeaderDynamicContentComponent(props: HeaderDynamicContentProps) {
|
||||||
|
const { component } = props
|
||||||
|
|
||||||
|
switch (component) {
|
||||||
|
case DynamicContentEnum.Headers.components
|
||||||
|
.dtmc_employee_benefits_call_to_actions:
|
||||||
|
return <EmployeeBenefitsCallToActions />
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import EmployeeBenefitsAuthCard from "@/components/DigitalTeamMemberCard/EmployeeBenefits/AuthCard"
|
||||||
import JsonToHtml from "@/components/JsonToHtml"
|
import JsonToHtml from "@/components/JsonToHtml"
|
||||||
|
|
||||||
import ShortcutsList from "../Blocks/ShortcutsList"
|
import ShortcutsList from "../Blocks/ShortcutsList"
|
||||||
import Card from "../TempDesignSystem/Card"
|
import Card from "../TempDesignSystem/Card"
|
||||||
import TeaserCard from "../TempDesignSystem/TeaserCard"
|
import TeaserCard from "../TempDesignSystem/TeaserCard"
|
||||||
import EmployeeBenefitsAuthCard from "./EmployeeBenefits/AuthCard"
|
|
||||||
import JoinLoyaltyContact from "./JoinLoyalty"
|
import JoinLoyaltyContact from "./JoinLoyalty"
|
||||||
import MyPagesNavigation from "./MyPagesNavigation"
|
import MyPagesNavigation from "./MyPagesNavigation"
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ query GetContentPage($locale: String!, $uid: String!) {
|
|||||||
header {
|
header {
|
||||||
heading
|
heading
|
||||||
preamble
|
preamble
|
||||||
|
dynamic_content {
|
||||||
|
component
|
||||||
|
}
|
||||||
...TopPrimaryButton_ContentPage
|
...TopPrimaryButton_ContentPage
|
||||||
...NavigationLinks_ContentPage
|
...NavigationLinks_ContentPage
|
||||||
}
|
}
|
||||||
@@ -84,6 +87,9 @@ query GetContentPageBlocksBatch2($locale: String!, $uid: String!) {
|
|||||||
query GetContentPageRefs($locale: String!, $uid: String!) {
|
query GetContentPageRefs($locale: String!, $uid: String!) {
|
||||||
content_page(locale: $locale, uid: $uid) {
|
content_page(locale: $locale, uid: $uid) {
|
||||||
header {
|
header {
|
||||||
|
dynamic_content {
|
||||||
|
component
|
||||||
|
}
|
||||||
...NavigationLinksRef_ContentPage
|
...NavigationLinksRef_ContentPage
|
||||||
...TopPrimaryButtonRef_ContentPage
|
...TopPrimaryButtonRef_ContentPage
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ import {
|
|||||||
import { tableSchema } from "../schemas/blocks/table"
|
import { tableSchema } from "../schemas/blocks/table"
|
||||||
import { textColsRefsSchema, textColsSchema } from "../schemas/blocks/textCols"
|
import { textColsRefsSchema, textColsSchema } from "../schemas/blocks/textCols"
|
||||||
import { uspGridRefsSchema, uspGridSchema } from "../schemas/blocks/uspGrid"
|
import { uspGridRefsSchema, uspGridSchema } from "../schemas/blocks/uspGrid"
|
||||||
|
import {
|
||||||
|
dynamicContentRefsSchema as headerDynamicContentRefsSchema,
|
||||||
|
dynamicContentSchema as headerDynamicContentSchema,
|
||||||
|
} from "../schemas/headers/dynamicContent"
|
||||||
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
|
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
|
||||||
import {
|
import {
|
||||||
linkAndTitleSchema,
|
linkAndTitleSchema,
|
||||||
@@ -210,6 +214,7 @@ export const contentPageSchema = z.object({
|
|||||||
preamble: z.string(),
|
preamble: z.string(),
|
||||||
top_primary_button: topPrimaryButtonSchema,
|
top_primary_button: topPrimaryButtonSchema,
|
||||||
navigation_links: navigationLinksSchema,
|
navigation_links: navigationLinksSchema,
|
||||||
|
dynamic_content: headerDynamicContentSchema.optional(),
|
||||||
}),
|
}),
|
||||||
meeting_package: z
|
meeting_package: z
|
||||||
.object({
|
.object({
|
||||||
@@ -325,6 +330,7 @@ const contentPageSidebarRefsItem = z.discriminatedUnion("__typename", [
|
|||||||
const contentPageHeaderRefs = z.object({
|
const contentPageHeaderRefs = z.object({
|
||||||
navigation_links: z.array(linkConnectionRefs),
|
navigation_links: z.array(linkConnectionRefs),
|
||||||
top_primary_button: linkConnectionRefs.nullable(),
|
top_primary_button: linkConnectionRefs.nullable(),
|
||||||
|
dynamic_content: headerDynamicContentRefsSchema.optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const contentPageRefsSchema = z.object({
|
export const contentPageRefsSchema = z.object({
|
||||||
|
|||||||
@@ -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),
|
||||||
|
})
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import type { z } from "zod"
|
||||||
|
|
||||||
|
import type { dynamicContentSchema } from "@/server/routers/contentstack/schemas/headers/dynamicContent"
|
||||||
|
|
||||||
|
export type HeaderDynamicContentProps = z.infer<typeof dynamicContentSchema>
|
||||||
@@ -1,61 +1,90 @@
|
|||||||
|
// Helper type to get a union of an object's string literal values
|
||||||
|
type ValueOf<T> = T[keyof T]
|
||||||
|
|
||||||
export namespace DynamicContentEnum {
|
export namespace DynamicContentEnum {
|
||||||
export namespace Blocks {
|
export namespace Blocks {
|
||||||
export const enum components {
|
export const components = {
|
||||||
current_benefits = "current_benefits",
|
current_benefits: "current_benefits",
|
||||||
earn_and_burn = "earn_and_burn",
|
earn_and_burn: "earn_and_burn",
|
||||||
expiring_points = "expiring_points",
|
expiring_points: "expiring_points",
|
||||||
how_it_works = "how_it_works",
|
how_it_works: "how_it_works",
|
||||||
jobylon_feed = "jobylon_feed",
|
jobylon_feed: "jobylon_feed",
|
||||||
loyalty_levels = "loyalty_levels",
|
loyalty_levels: "loyalty_levels",
|
||||||
membership_overview = "membership_overview",
|
membership_overview: "membership_overview",
|
||||||
my_points = "my_points",
|
my_points: "my_points",
|
||||||
next_benefits = "next_benefits",
|
next_benefits: "next_benefits",
|
||||||
overview_table = "overview_table",
|
overview_table: "overview_table",
|
||||||
points_overview = "points_overview",
|
points_overview: "points_overview",
|
||||||
previous_stays = "previous_stays",
|
previous_stays: "previous_stays",
|
||||||
sign_up_form = "sign_up_form",
|
sign_up_form: "sign_up_form",
|
||||||
sign_up_verification = "sign_up_verification",
|
sign_up_verification: "sign_up_verification",
|
||||||
soonest_stays = "soonest_stays",
|
soonest_stays: "soonest_stays",
|
||||||
upcoming_stays = "upcoming_stays",
|
upcoming_stays: "upcoming_stays",
|
||||||
sas_linked_account = "sas_linked_account",
|
sas_linked_account: "sas_linked_account",
|
||||||
sas_transfer_points = "sas_transfer_points",
|
sas_transfer_points: "sas_transfer_points",
|
||||||
sas_tier_comparison = "sas_tier_comparison",
|
sas_tier_comparison: "sas_tier_comparison",
|
||||||
}
|
} as const
|
||||||
|
|
||||||
/** Type needed to satisfy zod enum type */
|
/**
|
||||||
export const enums: [components, ...components[]] = [
|
* Represents one of the possible component string literal values from Blocks.components.
|
||||||
components.current_benefits,
|
* e.g., "current_benefits" | "earn_and_burn" | ...
|
||||||
components.earn_and_burn,
|
*/
|
||||||
components.expiring_points,
|
type ComponentValue = ValueOf<typeof components>
|
||||||
components.how_it_works,
|
|
||||||
components.jobylon_feed,
|
/**
|
||||||
components.loyalty_levels,
|
* Array of all component string literal values from Blocks.components.
|
||||||
components.membership_overview,
|
* Typed as a non-empty tuple `[ComponentValue, ...ComponentValue[]]`,
|
||||||
components.my_points,
|
* to ensure it meets Zod's z.enum() requirements.
|
||||||
components.next_benefits,
|
*/
|
||||||
components.overview_table,
|
export const enums = Object.values(components) as [
|
||||||
components.points_overview,
|
ComponentValue,
|
||||||
components.previous_stays,
|
...ComponentValue[],
|
||||||
components.sign_up_form,
|
]
|
||||||
components.sign_up_verification,
|
}
|
||||||
components.soonest_stays,
|
|
||||||
components.upcoming_stays,
|
export namespace Headers {
|
||||||
components.sas_linked_account,
|
export const components = {
|
||||||
components.sas_transfer_points,
|
dtmc_employee_benefits_call_to_actions:
|
||||||
components.sas_tier_comparison,
|
"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<typeof components>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 namespace Sidebar {
|
||||||
export const enum components {
|
export const components = {
|
||||||
my_pages_navigation = "my_pages_navigation",
|
my_pages_navigation: "my_pages_navigation",
|
||||||
employee_benefits_auth_card = "employee_benefits_auth_card",
|
employee_benefits_auth_card: "employee_benefits_auth_card",
|
||||||
}
|
} as const
|
||||||
|
|
||||||
/** Type needed to satisfy zod enum type */
|
/**
|
||||||
export const enums: [string, ...string[]] = [
|
* Represents one of the possible component string literal values from Sidebar.components.
|
||||||
components.my_pages_navigation,
|
* e.g., "my_pages_navigation" | "employee_benefits_auth_card"
|
||||||
components.employee_benefits_auth_card,
|
*/
|
||||||
|
type ComponentValue = ValueOf<typeof components>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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[],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user