Files
web/apps/scandic-web/components/Blocks/DynamicContent/Overview/MembershipOverviewCard/UsePoints/UsePointsModal.tsx
Erik Tiekstra 8e08af718c feat(BOOK-743): Replaced deprecated Button component
Approved-by: Bianca Widstam
2026-01-21 09:38:38 +00:00

197 lines
6.6 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 { 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 InfoCardWithImage from "@/components/ContentType/StartPage/InfoCardWithImage"
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 ? (
<InfoCardWithImage
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,
text: intl.formatMessage({
id: "myPages.membershipPointsModal.bookNow",
defaultMessage: "Book now",
}),
// Kind of a hack to make sure the link opens with focus inside the booking widget
// This should be refactored and the booking widget should handle this itself.
// Currently it only listens to the search param on first load.
isExternal: true,
onClick: () => trackButtonClick("book now"),
}}
secondaryButton={{
href: rewardNightsURL,
text: intl.formatMessage({
id: "myPages.membershipPointsModal.priceList",
defaultMessage: "Price list",
}),
openInNewTab: true,
trailingIconName: "open_in_new",
onClick: () => trackButtonClick("price list"),
}}
theme="Primary 3"
imagePosition="top"
height="dynamic"
/>
) : (
<InfoCardWithImage
heading={intl.formatMessage({
id: "myPages.membershipPointsModal.headingBelowThreshold",
defaultMessage:
"Earn at least 10 000 points for a reward night",
})}
secondaryButton={{
href: rewardNightsURL,
text: intl.formatMessage({
id: "myPages.membershipPointsModal.priceList",
defaultMessage: "Price list",
}),
openInNewTab: true,
trailingIconName: "open_in_new",
onClick: () => trackButtonClick("price list"),
}}
theme="Primary 3"
imagePosition="top"
height="dynamic"
/>
)}
</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>
</>
)
}