Merged in fix/SW-2290-ui-for-signup (pull request #2224)

Fix/SW-2290 new ui for signup on enter details page

* fix(SW-2290): New UI for join card


Approved-by: Michael Zetterberg
Approved-by: Tobias Johansson
Approved-by: Bianca Widstam
This commit is contained in:
Niclas Edenvin
2025-06-05 07:41:46 +00:00
committed by Linus Flood
parent 0383a332ad
commit d3ba9d4a99
9 changed files with 150 additions and 188 deletions

View File

@@ -8,7 +8,7 @@
display: grid; display: grid;
gap: var(--Spacing-x2); gap: var(--Spacing-x2);
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
width: min(700px, 100%); width: min(696px, 100%);
} }
.iconContainer { .iconContainer {

View File

@@ -8,5 +8,5 @@
display: grid; display: grid;
gap: var(--Spacing-x2); gap: var(--Spacing-x2);
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
width: min(700px, 100%); width: min(696px, 100%);
} }

View File

@@ -2,11 +2,15 @@
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { Typography } from "@scandic-hotels/design-system/Typography"
import { membershipTermsAndConditions } from "@/constants/webHrefs"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox" import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Link from "@/components/TempDesignSystem/Link"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import { useRoomContext } from "@/contexts/Details/Room" import { useRoomContext } from "@/contexts/Details/Room"
import useLang from "@/hooks/useLang"
import { formatPrice } from "@/utils/numberFormatting" import { formatPrice } from "@/utils/numberFormatting"
import styles from "./joinScandicFriendsCard.module.css" import styles from "./joinScandicFriendsCard.module.css"
@@ -17,6 +21,7 @@ import { CurrencyEnum } from "@/types/enums/currency"
export default function JoinScandicFriendsCard({ export default function JoinScandicFriendsCard({
name = "join", name = "join",
}: JoinScandicFriendsCardProps) { }: JoinScandicFriendsCardProps) {
const lang = useLang()
const intl = useIntl() const intl = useIntl()
const { const {
room, room,
@@ -32,72 +37,69 @@ export default function JoinScandicFriendsCard({
return null return null
} }
const list = [
{
title: intl.formatMessage({
defaultMessage: "Earn bonus nights & points",
}),
},
{
title: intl.formatMessage({
defaultMessage: "Get member benefits & offers",
}),
},
{
title: intl.formatMessage({
defaultMessage: "Join at no cost",
}),
},
]
const saveOnJoiningLabel = intl.formatMessage(
{
defaultMessage: "Pay the member price of {amount} for Room {roomNr}",
},
{
amount: formatPrice(
intl,
room.roomRate.member.localPrice.pricePerStay ?? 0,
room.roomRate.member.localPrice.currency ?? CurrencyEnum.Unknown
),
roomNr,
}
)
return ( return (
<div className={styles.cardContainer}> <div className={styles.cardContainer}>
<Typography variant="Title/Subtitle/md">
<h2 className={styles.priceContainer}>
<span>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${intl.formatMessage({
defaultMessage: "Get the member room price",
})}: `}
</span>
<span className={styles.price}>
{intl.formatMessage(
{
defaultMessage: "{amount} for room {roomNr}",
},
{
amount: formatPrice(
intl,
room.roomRate.member.localPrice.pricePerStay ?? 0,
room.roomRate.member.localPrice.currency ??
CurrencyEnum.Unknown
),
roomNr,
}
)}
</span>
</h2>
</Typography>
<Checkbox <Checkbox
name={name} name={name}
className={styles.checkBox} className={styles.checkBox}
registerOptions={{ onChange, value: room.guest.join }} registerOptions={{ onChange, value: room.guest.join }}
> >
<div> <Typography variant="Body/Paragraph/mdRegular">
<Caption type="label" textTransform="uppercase" color="red"> <div>
{saveOnJoiningLabel}
</Caption>
<Caption color="uiTextHighContrast">
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: "Join Scandic Friends", defaultMessage: "Join Scandic Friends before check-in",
})} })}
</Caption> </div>
</div> </Typography>
</Checkbox> </Checkbox>
<div className={styles.list}> <div className={styles.terms}>
{list.map((item) => ( <Footnote color="uiTextPlaceholder">
<Caption {intl.formatMessage(
key={item.title} {
className={styles.listItem} defaultMessage:
color="uiTextPlaceholder" "By joining you accept the <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. The Scandic Friends Membership is valid until further notice, but can at any time be terminated by contacting Scandic Customer Service.",
> },
<MaterialIcon {
icon="check" termsAndConditionsLink: (str) => (
size={20} <Link
color="Icon/Interactive/Placeholder" textDecoration="underline"
/> size="tiny"
{item.title} target="_blank"
</Caption> href={membershipTermsAndConditions[lang]}
))} >
{str}
</Link>
),
}
)}
</Footnote>
</div> </div>
</div> </div>
) )

View File

@@ -1,35 +1,48 @@
.cardContainer { .cardContainer {
align-self: flex-start; align-self: flex-start;
background-color: var(--Base-Surface-Primary-light-Normal); background-color: var(--Surface-Primary-Hover-Accent);
border: 1px solid var(--Base-Border-Subtle);
border-radius: var(--Corner-radius-lg); border-radius: var(--Corner-radius-lg);
display: grid; display: grid;
gap: var(--Spacing-x-one-and-half); gap: var(--Space-x2);
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2); padding: var(--Space-x2);
width: min(100%, 600px); grid-template-areas:
"price"
"checkbox"
"terms";
width: min(100%, 696px);
}
.priceContainer {
grid-area: price;
margin-bottom: var(--Space-x1);
}
.price {
color: var(--Text-Accent-Primary);
} }
.checkBox { .checkBox {
align-self: center; align-self: center;
grid-area: checkbox;
} }
.list { .terms {
display: flex; grid-area: terms;
gap: var(--Spacing-x1);
flex-direction: column;
}
.listItem {
display: flex;
} }
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.cardContainer { .cardContainer {
gap: var(--Spacing-x1); grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
gap: var(--Space-x3);
grid-template-areas:
"price checkbox"
"terms terms";
} }
.list { .priceContainer {
flex-direction: row; margin-bottom: 0;
flex-wrap: wrap; display: flex;
flex-direction: column;
} }
} }

View File

@@ -6,7 +6,7 @@
.container { .container {
display: grid; display: grid;
gap: var(--Spacing-x2); gap: var(--Spacing-x2);
width: min(100%, 600px); width: min(100%, 696px);
} }
.fullWidth { .fullWidth {
@@ -18,13 +18,7 @@
} }
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.form {
gap: var(--Spacing-x3);
}
.container { .container {
gap: var(--Spacing-x2);
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
width: min(100%, 600px);
} }
} }

View File

@@ -1,14 +1,14 @@
"use client" "use client"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { Typography } from "@scandic-hotels/design-system/Typography"
import { membershipTermsAndConditions } from "@/constants/webHrefs" import { membershipTermsAndConditions } from "@/constants/webHrefs"
import LoginButton from "@/components/LoginButton" import LoginButton from "@/components/LoginButton"
import Button from "@/components/TempDesignSystem/Button"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox" import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Footnote from "@/components/TempDesignSystem/Text/Footnote" import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import { useRoomContext } from "@/contexts/Details/Room" import { useRoomContext } from "@/contexts/Details/Room"
import useLang from "@/hooks/useLang" import useLang from "@/hooks/useLang"
@@ -37,98 +37,58 @@ export default function JoinScandicFriendsCard({
return null return null
} }
const list = [
{
title: intl.formatMessage({
defaultMessage: "Friendly room rates",
}),
},
{
title: intl.formatMessage({
defaultMessage: "Earn & spend points",
}),
},
{
title: intl.formatMessage({
defaultMessage: "Join for free",
}),
},
]
const saveOnJoiningLabel = intl.formatMessage(
{
defaultMessage: "Get the member room price: {amount}",
},
{
amount: formatPrice(
intl,
room.roomRate.member.localPrice.pricePerStay ?? 0,
room.roomRate.member.localPrice.currency ?? CurrencyEnum.Unknown
),
}
)
return ( return (
<div className={styles.cardContainer}> <div className={styles.cardContainer}>
<Typography variant="Title/Subtitle/md">
<h2 className={styles.priceContainer}>
<span>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${intl.formatMessage({
defaultMessage: "Get the member room price",
})}: `}
</span>
<span className={styles.price}>
{formatPrice(
intl,
room.roomRate.member.localPrice.pricePerStay ?? 0,
room.roomRate.member.localPrice.currency ?? CurrencyEnum.Unknown
)}
</span>
</h2>
</Typography>
<Checkbox <Checkbox
name={name} name={name}
className={styles.checkBox} className={styles.checkBox}
registerOptions={{ onChange }} registerOptions={{ onChange }}
> >
<div> <Typography variant="Body/Paragraph/mdRegular">
<Caption type="label" textTransform="uppercase" color="red"> <div>
{saveOnJoiningLabel}
</Caption>
<Caption
type="label"
textTransform="uppercase"
color="uiTextHighContrast"
>
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: "Join Scandic Friends", defaultMessage: "Join Scandic Friends now",
})} })}
</Caption> </div>
</div> </Typography>
</Checkbox> </Checkbox>
<Footnote color="uiTextHighContrast" className={styles.login}> <Button size="small" color="Primary" asChild>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${intl.formatMessage({
defaultMessage: "Already a friend?",
})} `}
<LoginButton <LoginButton
className={styles.login}
color="white"
position="enter details" position="enter details"
trackingId="join-scandic-friends-enter-details" trackingId="join-scandic-friends-enter-details"
variant="breadcrumb"
> >
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: "Log in", defaultMessage: "Log in",
})} })}
</LoginButton> </LoginButton>
</Footnote> </Button>
<div className={styles.list}>
{list.map((item) => (
<Caption
key={item.title}
color="uiTextPlaceholder"
className={styles.listItem}
>
<MaterialIcon
icon="check"
color="Icon/Interactive/Placeholder"
size={20}
/>
{item.title}
</Caption>
))}
</div>
<div className={styles.terms}> <div className={styles.terms}>
<Footnote color="uiTextPlaceholder"> <Footnote color="uiTextPlaceholder">
{intl.formatMessage( {intl.formatMessage(
{ {
defaultMessage: defaultMessage:
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service", "By joining you accept the <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. The Scandic Friends Membership is valid until further notice, but can at any time be terminated by contacting Scandic Customer Service.",
}, },
{ {
termsAndConditionsLink: (str) => ( termsAndConditionsLink: (str) => (

View File

@@ -1,21 +1,30 @@
.cardContainer { .cardContainer {
align-self: flex-start; align-self: flex-start;
background-color: var(--Base-Surface-Primary-light-Normal); background-color: var(--Surface-Primary-Hover-Accent);
border: 1px solid var(--Base-Border-Subtle);
border-radius: var(--Corner-radius-lg); border-radius: var(--Corner-radius-lg);
display: grid; display: grid;
gap: var(--Spacing-x-one-and-half); gap: var(--Space-x2);
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2); padding: var(--Space-x2);
grid-template-areas: grid-template-areas:
"checkbox" "price login"
"list" "checkbox checkbox"
"login" "terms terms";
"terms"; width: min(100%, 696px);
width: min(100%, 600px); }
.priceContainer {
grid-area: price;
margin-bottom: var(--Space-x1);
}
.price {
color: var(--Text-Accent-Primary);
} }
.login { .login {
grid-area: login; grid-area: login;
align-self: center;
justify-self: end;
} }
.checkBox { .checkBox {
@@ -23,33 +32,23 @@
grid-area: checkbox; grid-area: checkbox;
} }
.list {
display: grid;
grid-area: list;
gap: var(--Spacing-x1);
}
.listItem {
display: flex;
}
.terms { .terms {
border-top: 1px solid var(--Base-Border-Normal);
grid-area: terms; grid-area: terms;
padding-top: var(--Spacing-x1);
} }
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.cardContainer { .cardContainer {
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto auto;
gap: var(--Spacing-x2); grid-template-rows: auto auto;
gap: var(--Space-x3);
grid-template-areas: grid-template-areas:
"checkbox login" "price checkbox login"
"list list" "terms terms terms";
"terms terms";
} }
.list {
.priceContainer {
margin-bottom: 0;
display: flex; display: flex;
gap: var(--Spacing-x1); flex-direction: column;
} }
} }

View File

@@ -6,7 +6,7 @@
.container { .container {
display: grid; display: grid;
gap: var(--Spacing-x2); gap: var(--Spacing-x2);
width: min(100%, 600px); width: min(100%, 696px);
} }
.fullWidth { .fullWidth {
@@ -18,13 +18,7 @@
} }
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.form {
gap: var(--Spacing-x3);
}
.container { .container {
gap: var(--Spacing-x2);
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
width: min(100%, 600px);
} }
} }

View File

@@ -13,7 +13,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--Spacing-x4); gap: var(--Spacing-x4);
max-width: 480px; max-width: 696px;
} }
.section { .section {