Files
web/apps/scandic-web/components/MyPages/ProfilingConsent/Modal/index.tsx
Matilda Landström 01901ab02e Merged in fix/remove-duplicate-localise-keys (pull request #3171)
fix: remove duplicate Lokalise translations

* fix: remove duplicate Lokalise translations


Approved-by: Chuma Mcphoy (We Ahead)
2025-11-19 07:40:10 +00:00

223 lines
7.8 KiB
TypeScript

"use client"
import { AnimatePresence, motion } from "motion/react"
import { useCallback, useEffect, useState } from "react"
import { Dialog, Modal, ModalOverlay } from "react-aria-components"
import { useIntl } from "react-intl"
import { Button } from "@scandic-hotels/design-system/Button"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import ScandicLogoIcon from "@scandic-hotels/design-system/Icons/ScandicLogoIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { useUpdateProfilingConsent } from "@/hooks/useUpdateProfilingConsent"
import {
profilingConsentOpenEvent,
readDismissed,
setDismissed as persistDismissed,
} from "@/utils/profilingConsent"
import { trackConsentAction } from "@/utils/tracking/profilingConsent"
import ProfilingConsentAccordion from "../Accordion"
import { GetMainIconByCSIdentifier } from "../utils"
import BenefitCards from "./BenefitCards"
import styles from "./profilingConsentModal.module.css"
import type { ProfilingConsentModal as ProfilingConsentModalType } from "@scandic-hotels/trpc/types/profilingConsent"
type ProfilingConsentModalProps = {
memberKey?: string
content: ProfilingConsentModalType
iconIdentifier: string
readOnly?: boolean
}
const MotionModal = motion.create(Modal)
export default function ProfilingConsentModal({
memberKey,
content,
iconIdentifier,
readOnly = false,
}: ProfilingConsentModalProps) {
const intl = useIntl()
const [open, setOpen] = useState(false)
const { initiateUpdateConsent, isLoading, isSuccess } =
useUpdateProfilingConsent()
const [activeChoice, setActiveChoice] = useState<boolean | null>(null)
const handleClick = (consent: boolean) => {
setActiveChoice(consent)
initiateUpdateConsent(consent)
}
useEffect(() => {
if (!memberKey) return
setOpen(!readDismissed(memberKey))
}, [memberKey])
const onClose = useCallback(() => {
if (memberKey) {
persistDismissed(memberKey)
}
setOpen(false)
}, [memberKey])
useEffect(() => {
const handleOpen: EventListener = () => setOpen(true)
window.addEventListener(profilingConsentOpenEvent, handleOpen)
return () => {
window.removeEventListener(profilingConsentOpenEvent, handleOpen)
}
}, [])
useEffect(() => {
if (isSuccess) onClose()
}, [isSuccess, onClose])
if (!memberKey && !readOnly) return null
return (
<ModalOverlay
className={styles.overlay}
isOpen={open}
onOpenChange={(isOpen) => {
if (!isOpen) {
onClose()
}
}}
isKeyboardDismissDisabled
isDismissable={false}
>
<AnimatePresence mode="wait">
{open && (
<MotionModal
className={styles.modal}
initial={{ y: 32, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 32, opacity: 0 }}
transition={{
y: { duration: 0.4, ease: "easeInOut" },
opacity: { duration: 0.4, ease: "easeInOut" },
}}
>
<Dialog
aria-label={intl.formatMessage({
id: "profilingConsent.profilingConsent",
defaultMessage: "Profiling consent",
})}
className={styles.dialog}
>
<header className={styles.header}>
<div className={styles.logoWrap}>
<ScandicLogoIcon height={20} width={94} />
</div>
<Button
type="button"
variant="Text"
size="Large"
color="Primary"
className={styles.closeBtn}
onClick={() => {
trackConsentAction({ position: "modal", name: "close" })
onClose()
}}
aria-label={intl.formatMessage({
id: "common.close",
defaultMessage: "Close",
})}
>
<MaterialIcon color="CurrentColor" icon="close" />
</Button>
</header>
<main className={styles.content}>
<GetMainIconByCSIdentifier identifier={iconIdentifier} />
<div className={styles.textContent}>
<Typography className={styles.heading} variant="Title/md">
<h2>{content.header}</h2>
</Typography>
<Typography className={styles.text} variant="Body/Lead text">
<p>{content.sub_header}</p>
</Typography>
</div>
<BenefitCards cards={content.cards} />
<section className={styles.container}>
<header className={styles.header}>
<Typography variant="Title/Subtitle/lg">
<h3>
{intl.formatMessage({
id: "profilingConsent.personalization&privacy",
defaultMessage: "Personalization & privacy",
})}
</h3>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "profilingConsent.byAcceptingThisIConsent",
defaultMessage:
"By accepting this I consent to Scandic using my information to give me even more personalized travel inspiration and offers from Scandic and trusted Scandic Friends partners. This means Scandic may use information about my interactions with Scandic Friends partners, and share details of my interactions with Scandic with those partners, to make the experience even more relevant to me.",
})}
</p>
</Typography>
</header>
<ProfilingConsentAccordion component="modal" />
</section>
</main>
{!readOnly && (
<footer className={styles.actions}>
<Button
variant="Primary"
color="Primary"
size="Large"
typography="Body/Supporting text (caption)/smRegular"
type="button"
isDisabled={isLoading}
isPending={isLoading && activeChoice === true}
onClick={() => {
trackConsentAction({
position: "modal",
name: "accept personalization",
})
handleClick(true)
}}
>
{intl.formatMessage({
id: "profilingConsent.acceptPersonalization",
defaultMessage: "Accept Personalization",
})}
</Button>
<Button
variant="Secondary"
size="Large"
color="Primary"
typography="Body/Supporting text (caption)/smRegular"
type="button"
isDisabled={isLoading}
isPending={isLoading && activeChoice === false}
onClick={() => {
trackConsentAction({ position: "modal", name: "decline" })
handleClick(false)
}}
>
{intl.formatMessage({
id: "common.decline",
defaultMessage: "Decline",
})}
</Button>
</footer>
)}
</Dialog>
</MotionModal>
)}
</AnimatePresence>
</ModalOverlay>
)
}