diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Rewards/ScriptedRewardText/index.tsx b/apps/scandic-web/components/Blocks/DynamicContent/Rewards/ScriptedRewardText/index.tsx index f7aad059f..cc8d868ea 100644 --- a/apps/scandic-web/components/Blocks/DynamicContent/Rewards/ScriptedRewardText/index.tsx +++ b/apps/scandic-web/components/Blocks/DynamicContent/Rewards/ScriptedRewardText/index.tsx @@ -1,9 +1,10 @@ import { useIntl } from "react-intl" +import { isMembershipLevel } from "@scandic-hotels/common/utils/membershipLevels" + import { TIER_TO_FRIEND_MAP } from "@/constants/membershipLevels" import BiroScript from "@/components/TempDesignSystem/Text/BiroScript" -import { isMembershipLevel } from "@/utils/membershipLevels" import type { ScriptedRewardTextProps } from "@/types/components/myPages/myPage/accountPage" diff --git a/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/IneligibleMessage/index.tsx b/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/IneligibleMessage/index.tsx new file mode 100644 index 000000000..8419dd2f4 --- /dev/null +++ b/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/IneligibleMessage/index.tsx @@ -0,0 +1,47 @@ +import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" +import { Typography } from "@scandic-hotels/design-system/Typography" + +import { getIntl } from "@/i18n" + +import styles from "../hero.module.css" + +// @TODO: The ineligble message has not been finalized yet and is prone to change. +export default async function IneligibleMessage() { + const intl = await getIntl() + + return ( +
+ + + +
+ +

+ {intl.formatMessage({ + defaultMessage: "This campaign is not available for you", + })} +

+
+ +

+ {intl.formatMessage({ + defaultMessage: + "See terms & conditions for all qualifying requirements.", + })} +

+
+
+
+ ) +} diff --git a/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/hero.module.css b/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/hero.module.css index b398bdf23..494a5b06d 100644 --- a/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/hero.module.css +++ b/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/hero.module.css @@ -47,6 +47,18 @@ .benefitsCard { background-color: var(--Surface-Brand-Accent-Default); + display: grid; +} + +.benefitsCard:has(.ineligibleMessage) { + grid-template-rows: 1fr auto; +} + +.benefitsContent { + gap: var(--Space-x2); + display: flex; + flex-direction: column; + justify-content: center; } @media (max-width: 767px) { @@ -88,6 +100,27 @@ width: 100%; } +.ineligibleMessage { + display: grid; + grid-template-columns: 20px 1fr; + padding: var(--Space-x15); + gap: var(--Space-x1); + background-color: var(--Surface-Primary-Default); + border-radius: var(--Corner-radius-md); + border: 1px solid var(--Border-Default); + align-items: start; +} + +.ineligibleIcon { + justify-self: center; + align-self: center; +} + +.ineligibleTitle, +.ineligibleSubtitle { + color: var(--Text-Default); +} + .authHeading { color: var(--Text-Inverted); } diff --git a/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/index.tsx b/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/index.tsx index 37a43b8ae..2f93bad6a 100644 --- a/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/index.tsx +++ b/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/index.tsx @@ -8,27 +8,38 @@ import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import Image from "@scandic-hotels/design-system/Image" import { Typography } from "@scandic-hotels/design-system/Typography" +import { getProfileSafely } from "@/lib/trpc/memoizedRequests" + import { getIntl } from "@/i18n" import { getLang } from "@/i18n/serverContext" -import { isLoggedInUser } from "@/utils/isLoggedInUser" import ActivateOfferButton from "./ActivateOfferButton" +import IneligibleMessage from "./IneligibleMessage" +import { isUserEligibleForPromo } from "./utils" import styles from "./hero.module.css" +import type { MembershipLevel } from "@scandic-hotels/common/constants/membershipLevels" import type { PromoHero } from "@scandic-hotels/trpc/types/promoCampaignPage" interface PromoCampaignHeroProps extends React.HTMLAttributes { promoHero: PromoHero + eligibleLevels: MembershipLevel[] } export default async function PromoCampaignHero({ promoHero, + eligibleLevels, className, ...props }: PromoCampaignHeroProps) { const { image, heading, benefits } = promoHero - const isLoggedIn = await isLoggedInUser() + const profile = await getProfileSafely() + const isLoggedIn = !!profile + const userMembershipLevel = profile?.membership?.membershipLevel + const isEligible = isLoggedIn + ? isUserEligibleForPromo(userMembershipLevel, eligibleLevels) + : false const intl = await getIntl() const lang = await getLang() @@ -57,36 +68,34 @@ export default async function PromoCampaignHero({
- -

{heading}

-
- {benefits?.length ? ( -
    - {benefits.map((benefit) => ( -
  • - - - {benefit} - -
  • - ))} -
- ) : null} +
+ +

{heading}

+
+ {benefits?.length ? ( +
    + {benefits.map((benefit) => ( +
  • + + + {benefit} + +
  • + ))} +
+ ) : null} +
- {isLoggedIn && ( -
- {/* TODO: Account for more activation states. */} - -
- )} + {isLoggedIn && + (isEligible ? : )}
{!isLoggedIn && ( diff --git a/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/utils.ts b/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/utils.ts new file mode 100644 index 000000000..ed2e84164 --- /dev/null +++ b/apps/scandic-web/components/ContentType/PromoCampaignPage/Hero/utils.ts @@ -0,0 +1,18 @@ +import type { MembershipLevel } from "@scandic-hotels/common/constants/membershipLevels" + +/** + * Checks if a user's membership level is eligible for a promotional campaign. + * @param userLevel - The user's current membership level + * @param eligibleLevels - Array of membership levels eligible for the promo + * @returns true if user is eligible, false otherwise + */ +export function isUserEligibleForPromo( + userLevel: MembershipLevel | undefined, + eligibleLevels: MembershipLevel[] +): boolean { + if (!userLevel || eligibleLevels.length === 0) { + return false + } + + return eligibleLevels.includes(userLevel) +} diff --git a/apps/scandic-web/components/ContentType/PromoCampaignPage/index.tsx b/apps/scandic-web/components/ContentType/PromoCampaignPage/index.tsx index 07e93a0e0..992a98ab8 100644 --- a/apps/scandic-web/components/ContentType/PromoCampaignPage/index.tsx +++ b/apps/scandic-web/components/ContentType/PromoCampaignPage/index.tsx @@ -18,9 +18,10 @@ export default async function PromoCampaignPage() { if (!pageData) { notFound() } - //const isUserLoggedIn = await isLoggedInUser() const { promo_campaign_page, tracking } = pageData - const { heading, subheading, enddate, promo_hero } = promo_campaign_page + + const { heading, subheading, enddate, promo_hero, eligibleLevels } = + promo_campaign_page const isCampaignExpired = enddate ? dt().isAfter(dt(enddate).endOf("day")) @@ -30,7 +31,14 @@ export default async function PromoCampaignPage() { <> }>
- + {isCampaignExpired ? ( + + ) : ( + + )}
@@ -43,7 +51,6 @@ export default async function PromoCampaignPage() { ) : null}
- {isCampaignExpired ? : null}
diff --git a/apps/scandic-web/utils/membershipLevels.ts b/apps/scandic-web/utils/membershipLevels.ts deleted file mode 100644 index 2599caf2c..000000000 --- a/apps/scandic-web/utils/membershipLevels.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { MembershipLevelEnum } from "@scandic-hotels/common/constants/membershipLevels" - -export function isMembershipLevel(value: string): value is MembershipLevelEnum { - return Object.values(MembershipLevelEnum).some((level) => level === value) -} diff --git a/packages/common/package.json b/packages/common/package.json index 5ecd2c549..5b394d44f 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -54,6 +54,7 @@ "./utils/isValidJson": "./utils/isValidJson.ts", "./utils/languages": "./utils/languages.ts", "./utils/maskValue": "./utils/maskValue.ts", + "./utils/membershipLevels": "./utils/membershipLevels.ts", "./utils/numberFormatting": "./utils/numberFormatting.ts", "./utils/rangeArray": "./utils/rangeArray.ts", "./utils/safeTry": "./utils/safeTry.ts", diff --git a/packages/common/utils/membershipLevels.ts b/packages/common/utils/membershipLevels.ts new file mode 100644 index 000000000..7cfb36c03 --- /dev/null +++ b/packages/common/utils/membershipLevels.ts @@ -0,0 +1,10 @@ +import { MembershipLevelEnum } from "../constants/membershipLevels" + +/** + * Type guard to check if a string value is a valid MembershipLevel + * @param value - The string value to check + * @returns true if the value is a valid MembershipLevel, false otherwise + */ +export function isMembershipLevel(value: string): value is MembershipLevelEnum { + return Object.values(MembershipLevelEnum).some((level) => level === value) +} diff --git a/packages/trpc/lib/graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql b/packages/trpc/lib/graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql index 09e54a7ea..debe92057 100644 --- a/packages/trpc/lib/graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql +++ b/packages/trpc/lib/graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql @@ -25,6 +25,7 @@ query GetPromoCampaignPage($locale: String!, $uid: String!) { promo_code startdate enddate + level_selection system { ...System created_at diff --git a/packages/trpc/lib/routers/contentstack/promoCampaignPage/output.ts b/packages/trpc/lib/routers/contentstack/promoCampaignPage/output.ts index c4e85ebbd..4654851cd 100644 --- a/packages/trpc/lib/routers/contentstack/promoCampaignPage/output.ts +++ b/packages/trpc/lib/routers/contentstack/promoCampaignPage/output.ts @@ -1,6 +1,7 @@ import { z } from "zod" import { transformedImageVaultAssetSchema } from "@scandic-hotels/common/utils/imageVault" +import { isMembershipLevel } from "@scandic-hotels/common/utils/membershipLevels" import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator" import { systemSchema } from "../schemas/system" @@ -35,6 +36,13 @@ export const promoCampaignPageSchema = z promo_code: z.string(), startdate: nullableStringValidator, enddate: nullableStringValidator, + level_selection: z + .array(z.string()) + .nullish() + .transform((data) => { + if (!data) return [] + return data.filter(isMembershipLevel) + }), system: systemSchema.merge( z.object({ created_at: z.string(), @@ -47,13 +55,15 @@ export const promoCampaignPageSchema = z }), }) .transform(({ promo_campaign_page, ...data }) => { - const { page_settings, ...promoCampaignPageData } = promo_campaign_page + const { page_settings, level_selection, ...promoCampaignPageData } = + promo_campaign_page const bookingCode = page_settings?.booking_code || null return { ...data, promo_campaign_page: { bookingCode, + eligibleLevels: level_selection, ...promoCampaignPageData, }, }