Files
web/apps/scandic-web/components/Blocks/DynamicContent/Overview/MembershipOverviewCard/UsePoints/UsePointsModal.tsx
Emma Zettervall 34526bbaf1 Merged in fix/LOY-400-make-contentstack-data-optional (pull request #3266)
fix(LOY-400): made contentstack data optional so the whole page does not crash if it's empty.

* fix(LOY-400): made contentstack data optional so the whole page does not crash if it's empty.

* Merged in fix/LOY-400-make-contentstack-data-optional-2 (pull request #3269)

Fix/LOY-400 make contentstack data optional 2

* Test

* .

* Correct ref tag

* refactor

* Correct key

* fix(LOY-400): cleaned up


Approved-by: Linus Flood
Approved-by: Matilda Landström
2025-12-02 08:08:12 +00:00

207 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { usePathname, useSearchParams } from "next/navigation"
import { useState } from "react"
import { useIntl } from "react-intl"
import { serializeBookingSearchParams } from "@scandic-hotels/booking-flow/utils/url"
import { BOOK_NOW_SESSION_KEY } from "@scandic-hotels/common/constants/sessionKeys"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { LinkList } from "@scandic-hotels/design-system/LinkList"
import { MessageBanner } from "@scandic-hotels/design-system/MessageBanner"
import Modal from "@scandic-hotels/design-system/Modal"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { trackClick, trackEvent } from "@scandic-hotels/tracking/base"
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { rewardNightsURL } from "@/constants/rewards"
import InfoCard from "@/components/ContentType/StartPage/InfoCard"
import { UsePointsButton } from "./UsePointsButton"
import styles from "./UsePoints.module.css"
import type { ButtonProps } from "@scandic-hotels/design-system/Button"
import type { UsePointsModalData } from "@scandic-hotels/trpc/routers/contentstack/UsePointsModal/output"
function trackButtonClick(label: string) {
trackClick(label)
if (label === "book now") {
sessionStorage.setItem(BOOK_NOW_SESSION_KEY, "true")
}
}
function trackLinkListClick(linkText: string) {
if (!linkText) return
trackEvent({
event: "linkClick",
link: { name: linkText },
})
}
type UsePointsModalProps = {
buttonVariant: ButtonProps["variant"]
contentData: UsePointsModalData
points: number
className?: string
}
export function UsePointsModal({
buttonVariant,
contentData,
points,
className,
}: UsePointsModalProps) {
const intl = useIntl()
const [isOpen, setIsOpen] = useState(false)
const pathname = usePathname()
const initialSearchParams = useSearchParams()
const searchParams = serializeBookingSearchParams(
{
searchType: SEARCH_TYPE_REDEMPTION,
focusWidget: true,
},
{ initialSearchParams }
)
const bookLink = `${pathname}?${searchParams}`
const items = contentData?.all_usepointsmodal?.items?.[0]
if (!items) {
return null
}
const linkListItems = items.link_group.map((link) => ({
text: link.text,
isExternal: link.isExternal,
href: link.href,
illustration: link.illustration,
onClick: () => trackLinkListClick(link.text),
}))
return (
<>
<UsePointsButton
variant={buttonVariant}
onPress={() => {
setIsOpen(true)
trackButtonClick("use points")
}}
className={className}
/>
<Modal isOpen={isOpen} onToggle={setIsOpen} withActions>
<div className={styles.modalContent}>
<div className={styles.header}>
<Typography variant="Tag/sm">
<p className={styles.pointsText}>
{intl.formatMessage({
id: "myPages.membershipPoints.youHave",
defaultMessage: "You have",
})}
</p>
</Typography>
<div className={styles.points}>
<Typography variant="Title/lg">
<p className={styles.pointsNumber}>
{intl.formatNumber(points)}
</p>
</Typography>
<Typography variant="Tag/sm">
<p className={styles.pointsText}>
{intl.formatMessage({
id: "myPages.membershipPoints.pointsToSpend",
defaultMessage: "points to spend",
})}
</p>
</Typography>
</div>
</div>
<div>
{points >= 10000 ? (
<InfoCard
image={items.image}
heading={intl.formatMessage({
id: "myPages.membershipPointsModal.headingAboveThreshold",
defaultMessage: "Youve earned a night away",
})}
bodyText={intl.formatMessage({
id: "myPages.membershipPointsModal.bodytext",
defaultMessage: "Reward nights start at 10,000 points",
})}
primaryButton={{
href: bookLink,
title: intl.formatMessage({
id: "myPages.membershipPointsModal.bookNow",
defaultMessage: "Book now",
}),
forceReload: true,
}}
secondaryButton={{
href: rewardNightsURL,
title: intl.formatMessage({
id: "myPages.membershipPointsModal.priceList",
defaultMessage: "Price list",
}),
openInNewTab: true,
materialIcon: (
<MaterialIcon
icon="open_in_new"
color="CurrentColor"
size={20}
/>
),
}}
onPrimaryButtonClick={() => trackButtonClick("book now")}
onSecondaryButtonClick={() => trackButtonClick("price list")}
theme="primaryDark"
imagePosition="top"
height="dynamic"
></InfoCard>
) : (
<InfoCard
heading={intl.formatMessage({
id: "myPages.membershipPointsModal.headingBelowThreshold",
defaultMessage:
"Earn at least 10 000 points for a reward night",
})}
secondaryButton={{
href: rewardNightsURL,
title: intl.formatMessage({
id: "myPages.membershipPointsModal.priceList",
defaultMessage: "Price list",
}),
openInNewTab: true,
materialIcon: (
<MaterialIcon
icon="open_in_new"
color="CurrentColor"
size={20}
/>
),
}}
onSecondaryButtonClick={() => trackButtonClick("price list")}
theme="primaryDark"
imagePosition="top"
height="dynamic"
></InfoCard>
)}
</div>
<div>
<LinkList linkListItems={linkListItems}></LinkList>
</div>
<MessageBanner
type="info"
text={intl.formatMessage({
id: "myPages.membershipPointsModal.infoBanner",
defaultMessage:
"Spending points do not affect your level progress.",
})}
/>
</div>
</Modal>
</>
)
}