Merged in feat/profile-consent-feature-branch (pull request #2900)

feat(LOY-268): Feature branch for profiling consent work

* feat: Add feature branch for profile and consent work

* Merged in feat/LOY-268-profile-consent-banner-comp (pull request #2908)

Feat/LOY-358 profile consent banner component

* feat: Add feature branch for profile and consent work

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component


Approved-by: Chuma Mcphoy (We Ahead)

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* Merged in feat/profile-consent-contentstack (pull request #2921)

Feat(LOY-389): Profile consent in Contentstack

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* chore(LOY-348): add profiling consent as CS entry

* chore(LOY-348): add banner as dynamic content

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-347-Profile-consent-modal-phase-1 (pull request #2901)

Feat(LOY-347): Profiling Consent Modal (phase 1)

* feat(LOY-347): Profile Consent Modal base functionality

* feat(LOY-347): Add Icon

* feat(LOY-347): Add Benefit Cards to Profile consent modal

* feat(LOY-347): Add accordion to profile consent modal

* fix(LOY-347): scroll behaviour

* Fix(LOY-347): fade in/out animations of profile consent modal

* fix(LOY-347): White Ellipsis Around Icons

* feat(LOY-347): Implement ability to open modal from banner

* fix(LOY-347): minor fixes

* fix(LOY-347): replace old spa icon

* fix(LOY-347): re-add env vars

* fix(LOY-347): File renaming and cleanup

* chore(LOY-347): Update readme

* fix(LOY-347): use correct space var

* fix(LOY-347): Add TODO comment for adding link to accordion

Approved-by: Matilda Landström

* Merged in fix/LOY-386-profiling-consent-modal-contentstack (pull request #2930)

Fix(LOY-386): profiling consent modal contentstack

* feat(LOY-347): Profile Consent Modal base functionality

* feat(LOY-347): Add Icon

* feat(LOY-347): Add Benefit Cards to Profile consent modal

* feat(LOY-347): Add accordion to profile consent modal

* fix(LOY-347): scroll behaviour

* Fix(LOY-347): fade in/out animations of profile consent modal

* fix(LOY-347): White Ellipsis Around Icons

* feat(LOY-347): Implement ability to open modal from banner

* fix(LOY-347): minor fixes

* fix(LOY-347): replace old spa icon

* fix(LOY-347): re-add env vars

* fix(LOY-347): File renaming and cleanup

* fix(LOY-386): Use contentstack content for profile consent modal

* fix(LOY-386): beneift cards schema transform

* chore(LOY-386): remove usememo

* fix(LOY-386): fix modalcontent check

* fix(LOY-386): remove uneeded vars


Approved-by: Matilda Landström

* Merged in feat/LOY-412-profiling-consent-in-signup (pull request #2976)

Feat(LOY-412): profiling consent in signup

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* Merged in feat/profile-consent-contentstack (pull request #2921)

Feat(LOY-389): Profile consent in Contentstack

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* chore(LOY-348): add profiling consent as CS entry

* chore(LOY-348): add banner as dynamic content

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-347-Profile-consent-modal-phase-1 (pull request #2901)

Feat(LOY-347): Profiling Consent Modal (phase 1)

* feat(LOY-347): Profile Consent Modal base functionality

* feat(LOY-347): Add Icon

* feat(LOY-347): Add Benefit Cards to Profile consent modal

* feat(LOY-347): Add accordion to profile consent modal

* fix(LOY-347): scroll behaviour

* Fix(LOY-347): fade in/out animations of profile consent modal

* fix(LOY-347): White Ellipsis Around Ico…
* Merged in fix/lokalise-ids (pull request #3013)

fix: add ids to translations in Profiling Consent

* fix: add ids to translations


Approved-by: Erik Tiekstra
Approved-by: Chuma Mcphoy (We Ahead)

* Merged in LOY-436-my-pages-profiling-consent (pull request #3011)

LOY-436: Profiling Consent on My Profile, no api

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-418-profiling-consent-ui-text-update (pull request #3080)

Feat/LOY-418: Profiling consent ui and text update

* chore(LOY-418): update /consent buttons

* chore(LOY-418): update legal texts


Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-268-profiling-consent-api (pull request #3088)

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-413-Signup-API-Profiling-Consent (pull request #3105)

Feat/LOY-413 Signup API Profiling Consent

* feat(LOY-413): signup profiling consent

* chore(LOY-413): remove todo

* fix(LOY-413): only pass in profilingConsent if true

* fix(LOY-413): proper spread of profilingConsent in signup input


Approved-by: Christel Westerberg

* Merged in fix/LOY-413-use-v2-for-signup-call (pull request #3112)

fix(LOY-413): use v2 endpoint for profile POST in signup

* fix(LOY-413): use v2 endpoint for profile POST in signup


Approved-by: Erik Tiekstra

* Merged in feat/LOY-268-profiling-consent-improvements (pull request #3094)

Feat/LOY-268: Profiling consent improvements

* Merged in feat/profile-consent-contentstack (pull request #2921)

Feat(LOY-389): Profile consent in Contentstack

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* chore(LOY-348): add profiling consent as CS entry

* chore(LOY-348): add banner as dynamic content

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-347-Profile-consent-modal-phase-1 (pull request #2901)

Feat(LOY-347): Profiling Consent Modal (phase 1)

* feat(LOY-347): Profile Consent Modal base functionality

* feat(LOY-347): Add Icon

* feat(LOY-347): Add Benefit Cards to Profile consent modal

* feat(LOY-347): Add accordion to profile consent modal

* fix(LOY-347): scroll behaviour

* Fix(LOY-347): fade in/out animations of profile consent modal

* fix(LOY-347): White Ellipsis Around Icons

* feat(LOY-347): Implement ability to open modal from banner

* fix(LOY-347): min…
* Merged in fix/update-graphql (pull request #3130)

fix: update graphql

* fix: update graphql


Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-414-prof-consent-tracking (pull request #3127)

Feat/LOY-414 profile consent tracking + credit card ui update

* chore(LOY-414): create track link function

* chore(LOY-414): add cta tracking

* chore(LOY-414): add profileConsent to userInfo datalayer

* chore(LOY-414): update credit card ui

* chore(LOY-414): update tracking specs

* chore(LOY-414): add pageView tracking to modal

Approved-by: Chuma Mcphoy (We Ahead)

* fix: remove old flag

* Merged in fix/LOY-268-prof-consent-button-fix (pull request #3162)

fix(LOY-268): add button as link

* fix(LOY-268): add button as link


Approved-by: Chuma Mcphoy (We Ahead)


Approved-by: Matilda Landström
This commit is contained in:
Chuma Mcphoy (We Ahead)
2025-11-14 13:28:56 +00:00
parent 9260cc0f81
commit 494bfe10f3
79 changed files with 9332 additions and 268 deletions

View File

@@ -0,0 +1,239 @@
"use client"
import { useIntl } from "react-intl"
import { privacy } from "@scandic-hotels/common/constants/routes/customerService"
import Accordion from "@scandic-hotels/design-system/Accordion"
import AccordionItem from "@scandic-hotels/design-system/Accordion/AccordionItem"
import { IconByIconName } from "@scandic-hotels/design-system/Icons/IconByIconName"
import { IconName } from "@scandic-hotels/design-system/Icons/iconName"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { TextLink } from "@scandic-hotels/design-system/TextLink"
import { Typography } from "@scandic-hotels/design-system/Typography"
import useLang from "@/hooks/useLang"
import { trackLinkClick } from "@/utils/tracking/profilingConsent"
import styles from "./profilingConsentAccordion.module.css"
interface ProfilingConsentAccordionProps {
component: "modal" | "profile"
}
export default function ProfilingConsentAccordion({
component,
}: ProfilingConsentAccordionProps) {
const intl = useIntl()
const lang = useLang()
const dataWeProcess = [
{
icon: IconName.Person,
title: intl.formatMessage({
id: "profilingConsent.NameAndContactInformation",
defaultMessage: "Name and contact information",
}),
subtitle: intl.formatMessage({
id: "profilingConsent.NameEmailPhoneAddress",
defaultMessage:
"Your name, email address, phone number and postal address",
}),
},
{
icon: IconName.Luggage,
title: intl.formatMessage({
id: "profilingConsent.YourBookingsWithScandic",
defaultMessage: "Your bookings with Scandic",
}),
subtitle: intl.formatMessage({
id: "profilingConsent.BookingsDatesDestinationsPreferences",
defaultMessage: "Hotel bookings, dates, destinations and preferences",
}),
},
{
icon: IconName.Diamond,
title: intl.formatMessage({
id: "profilingConsent.YourMembershipInformation",
defaultMessage: "Your membership information",
}),
subtitle: intl.formatMessage({
id: "profilingConsent.ScandicFriendsPointsBenefits",
defaultMessage: "Scandic Friends status, points and member benefits",
}),
},
{
icon: IconName.Business,
title: intl.formatMessage({
id: "profilingConsent.CompanyInformation",
defaultMessage: "Company information",
}),
subtitle: intl.formatMessage({
id: "profilingConsent.InformationAboutYourCompany",
defaultMessage:
"Information about the company you work for (if available)",
}),
},
{
icon: IconName.Globe,
title: intl.formatMessage({
id: "profilingConsent.UserGeneratedData",
defaultMessage: "User-generated data",
}),
subtitle: intl.formatMessage({
id: "profilingConsent.ClicksBrowsingPurchasingInteractions",
defaultMessage:
"Clicks, browsing, purchase history and website interactions",
}),
},
]
const rights = [
intl.formatMessage({
id: "profilingConsent.GetInformation",
defaultMessage: "Get information about how we process your data",
}),
intl.formatMessage({
id: "profilingConsent.RequestDeletion",
defaultMessage: "Request deletion of your personal data",
}),
intl.formatMessage({
id: "profilingConsent.WithdrawConsent",
defaultMessage: "Withdraw your consent at any time",
}),
]
const security = [
intl.formatMessage({
id: "profilingConsent.EncryptedSecureDataStorage",
defaultMessage: "Encrypted & secure data storage",
}),
intl.formatMessage({
id: "profilingConsent.WeWillNeverSellYourData",
defaultMessage: "We will never sell your data",
}),
intl.formatMessage({
id: "profilingConsent.RegularSecurityAudits",
defaultMessage: "Regular security audits",
}),
intl.formatMessage({
id: "profilingConsent.GDPRCompliantProcessing",
defaultMessage: "GDPR-compliant processing",
}),
]
return (
<Accordion type="sidepeek" className={styles.accordion}>
<AccordionItem
title={intl.formatMessage({
id: "profilingConsent.whatDoWeProcess",
defaultMessage: "What data do we process?",
})}
iconName={IconName.InfoCircle}
className={styles.accordionItem}
openedOnRender
>
<ul className={styles.list}>
{dataWeProcess.map((item, i) => (
<li key={i} className={styles.row}>
<span className={styles.rowIcon} aria-hidden>
<IconByIconName
iconName={item.icon}
color="Icon/Interactive/Default"
size={24}
/>
</span>
<span className={styles.rowText}>
<Typography variant="Body/Paragraph/mdBold">
<p>{item.title}</p>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>{item.subtitle}</p>
</Typography>
</span>
</li>
))}
</ul>
</AccordionItem>
<AccordionItem
title={intl.formatMessage({
id: "profilingConsent.transparency&processing",
defaultMessage: "Transparency & data processing",
})}
iconName={IconName.EyeShow}
className={styles.accordionItem}
openedOnRender
>
<div className={styles.columns}>
<div className={styles.column}>
<Typography variant="Title/Subtitle/md">
<h4>
{intl.formatMessage({
id: "profilingConsent.youHaveTheRightTo",
defaultMessage: "You have the right to",
})}
</h4>
</Typography>
<ul className={styles.bullets}>
{rights.map((text, i) => (
<li key={i} className={styles.bulletRow}>
<MaterialIcon
icon="favorite"
size={20}
color="Icon/Accent"
isFilled
/>
<Typography variant="Body/Paragraph/mdRegular">
<p>{text}</p>
</Typography>
</li>
))}
</ul>
</div>
<div className={styles.column}>
<Typography variant="Title/Subtitle/md">
<h4>
{intl.formatMessage({
id: "profilingConsent.dataSecurity",
defaultMessage: "Data security",
})}
</h4>
</Typography>
<ul className={styles.bullets}>
{security.map((text, i) => (
<li key={i} className={styles.bulletRow}>
<MaterialIcon icon="lock" size={20} color="Icon/Accent" />
<Typography variant="Body/Paragraph/mdRegular">
<p>{text}</p>
</Typography>
</li>
))}
</ul>
</div>
</div>
<TextLink
href={privacy[lang]}
color="Text/Interactive/Secondary"
typography="Link/md"
className={styles.learnMoreLink}
onClick={() =>
trackLinkClick({
position: component,
name: "learn more about how we process your data",
})
}
>
{intl.formatMessage({
id: "profilingConsent.learnMoreAboutProcessing",
defaultMessage: "Learn more about how we process your data",
})}
<MaterialIcon
icon="open_in_new"
size={20}
color="Icon/Interactive/Secondary"
/>
</TextLink>
</AccordionItem>
</Accordion>
)
}

View File

@@ -0,0 +1,77 @@
.divider {
margin: var(--Space-x1) 0;
}
.accordion {
width: 100%;
}
.list {
display: grid;
gap: var(--Space-x15);
}
.row {
display: grid;
grid-template-columns: 40px 1fr;
gap: var(--Space-x2);
align-items: start;
border: 0.696px solid var(--Border-Divider-Subtle);
border-radius: var(--Corner-radius-md);
background: var(--Surface-Primary-OnSurface-Default);
padding: var(--Space-x1);
}
.accordionItem:first-child {
border-top: 1px solid var(--Border-Default);
}
.rowIcon {
display: flex;
align-items: center;
justify-content: center;
place-self: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--Surface-Primary-Default);
}
.rowText {
display: grid;
gap: 2px;
}
.columns {
display: grid;
gap: var(--Space-x3);
margin-bottom: var(--Space-x3);
}
.column {
display: grid;
gap: var(--Space-x1);
}
.bullets {
display: grid;
gap: var(--Space-x1);
}
.bulletRow {
display: grid;
grid-template-columns: 20px 1fr;
gap: var(--Space-x1);
align-items: start;
}
.learnMoreLink {
display: flex;
gap: var(--Space-x05);
}
@media (min-width: 768px) {
.columns {
grid-template-columns: 1fr 1fr;
}
}