Files
web/apps/scandic-web/components/Blocks/DynamicContent/Overview/MembershipOverviewCard/UsePoints/UsePointsModal.tsx
Emma Zettervall f443bae46e Merged in feat/LOY-400-create-spend-points-modal (pull request #3131)
Feat/LOY-400 create spend points modal

* feat(LOY-400): Added custom button to my pages overview and skeleton file to custom modal for my points.

* feat(LOY-400): Added custom button to my pages overview and components for custom modal for my points.

* feat(LOY-400): Changed some style and infogridcardover

* feat(LOY-400):Removed custom card components and changed in infoCard: Added imagePosition top, added optional height prop. In Card: Changed Text-wrap styling, added min-width styling to buttons, added optional Icon prop, added optional height prop

* feat(LOY-400):Added linkList, LinkListItem component and messageBanner component. Added granola illustration.

* feat(LOY-400): Removed background in several illustrations. Added component for illustration. Fixed LinkedList and styling for UsePointsButton.

* feat(LOY-400): Added modal to PointsToSpendCard and fixed UsePointsButton.

* fix(LOY-400):added some styling

* feat(LOY-400): Linked Modal to contentstack and fetch the data in cards with UsePointsModal for now

* feat(LOY-400): changed link to aria-component, cleaned up a bit

* feat(LOY-400): Changed height for larger modals in mobile, fixed zod schema for no illustration input, cleaned up

* fix(LOY-400): fixed graphql after rebase

* fix(LOY-400): mini fix

* fix(LOY-400): fixed pr-comments

* fix(LOY-400): fixed some PR-comments

* fix(LOY-400): fixed a PR-comment

* feat(LOY-400): added size prop to ilustration in LinkListItem to be able to use illustrations in IllustrationByIconName

* fix(LOY-400): fixed pr-comments

* Merged in feat/LOY-402-pre-ticked-book-reward-night-in-booking-flow (pull request #3210)

Feat/LOY-402 pre ticked book reward night in booking flow

* feat(LOY-402): Changed UsePointsModal structure to handle button actions in card.

* feat(LOY-402): added functionality for book now button

* feat(LOY-400): pr comment fix

* feat(LOY-402): transformed the contentstack data

* fix(LOY-402): fixed pr comments

Approved-by: Chuma Mcphoy (We Ahead)
Approved-by: Anton Gunnarsson
Approved-by: Matilda Landström

* Merged in feat/LOY-404-add-tracking-for-spend-points-modal (pull request #3229)

Feat/LOY-404 add tracking for spend points modal

* feat(LOY-402): Changed UsePointsModal structure to handle button actions in card.

* feat(LOY-402): added functionality for book now button

* feat(LOY-400): pr comment fix

* feat(LOY-402): transformed the contentstack data

* feat(LOY-404): added tracking

* fix(LOY-404): fix for session storage removal of bookNowFromPointsModal

* feat(LOY-404): added consts

* fix(LOY-404): moved foxusWidget const

* fix(LOY-404): moved BOOKING_WIDGET_STATE const

* fix(LOY-404):fix


Approved-by: Matilda Landström

* fix(LOY-400): some fixes

* feat(LOY-400): created linkList storybook


Approved-by: Chuma Mcphoy (We Ahead)
Approved-by: Matilda Landström
2025-11-28 15:08:06 +00:00

203 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
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.heading",
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.heading",
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>
</>
)
}