Merged in fix/BOOK-529-my-stay-guarantee (pull request #3282)

fix(BOOK-529): add card icon to payment cards, show scrollbar, add missing text

* fix(BOOK-529): add card icon to payment cards, show scrollbar, add missing text

* fix(BOOK-529): refactor savdecard

* fix(BOOK-529): fix lokaliseid

* fix(BOOK-529): paymentmethods


Approved-by: Joakim Jäderberg
This commit is contained in:
Bianca Widstam
2025-12-03 13:04:02 +00:00
parent 6730575f7a
commit 7db225a3ee
9 changed files with 74 additions and 38 deletions

View File

@@ -20,6 +20,14 @@
padding: var(--Space-x2); padding: var(--Space-x2);
} }
.refundPolicy {
color: var(--Text-Secondary);
}
.pointsAvailable {
text-align: end;
}
.paymentInfo { .paymentInfo {
display: flex; display: flex;
gap: var(--Space-x1); gap: var(--Space-x1);

View File

@@ -2,7 +2,6 @@ import { useWatch } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod" import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import { dt } from "@scandic-hotels/common/dt"
import AccordionItem from "@scandic-hotels/design-system/Accordion/AccordionItem" import AccordionItem from "@scandic-hotels/design-system/Accordion/AccordionItem"
import { Alert } from "@scandic-hotels/design-system/Alert" import { Alert } from "@scandic-hotels/design-system/Alert"
import { Divider } from "@scandic-hotels/design-system/Divider" import { Divider } from "@scandic-hotels/design-system/Divider"
@@ -15,7 +14,6 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
import { useAddAncillaryStore } from "@/stores/my-stay/add-ancillary-flow" import { useAddAncillaryStore } from "@/stores/my-stay/add-ancillary-flow"
import TermsAndConditions from "@/components/HotelReservation/MyStay/TermsAndConditions" import TermsAndConditions from "@/components/HotelReservation/MyStay/TermsAndConditions"
import useLang from "@/hooks/useLang"
import { trackUpdatePaymentMethod } from "@/utils/tracking" import { trackUpdatePaymentMethod } from "@/utils/tracking"
import Summary from "../Summary" import Summary from "../Summary"
@@ -30,19 +28,15 @@ export default function ConfirmationStep({
error, error,
}: ConfirmationStepProps) { }: ConfirmationStepProps) {
const intl = useIntl() const intl = useIntl()
const lang = useLang()
const { checkInDate, guaranteeInfo, selectedAncillary, booking } = const { guaranteeInfo, selectedAncillary, booking } = useAddAncillaryStore(
useAddAncillaryStore((state) => ({ (state) => ({
checkInDate: state.booking.checkInDate, checkInDate: state.booking.checkInDate,
guaranteeInfo: state.booking.guaranteeInfo, guaranteeInfo: state.booking.guaranteeInfo,
selectedAncillary: state.selectedAncillary, selectedAncillary: state.selectedAncillary,
booking: state.booking, booking: state.booking,
})) })
const refundableDate = dt(checkInDate) )
.subtract(1, "day")
.locale(lang)
.format("23:59, dddd, D MMMM YYYY")
const mustBeGuaranteed = !guaranteeInfo && booking.isGuaranteeable const mustBeGuaranteed = !guaranteeInfo && booking.isGuaranteeable
const quantityWithCard = useWatch({ name: "quantityWithCard" }) const quantityWithCard = useWatch({ name: "quantityWithCard" })
@@ -99,7 +93,7 @@ export default function ConfirmationStep({
</Typography> </Typography>
</div> </div>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p> <p className={styles.pointsAvailable}>
{intl.formatMessage( {intl.formatMessage(
{ {
id: "addAncillary.confirmationStep.pointsAvailable", id: "addAncillary.confirmationStep.pointsAvailable",
@@ -112,7 +106,7 @@ export default function ConfirmationStep({
</div> </div>
</> </>
)} )}
{!!quantityWithCard && ( {!!quantityWithCard ? (
<> <>
<Typography variant="Title/Subtitle/md"> <Typography variant="Title/Subtitle/md">
<h2> <h2>
@@ -123,15 +117,12 @@ export default function ConfirmationStep({
</h2> </h2>
</Typography> </Typography>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p> <p className={styles.refundPolicy}>
{intl.formatMessage( {intl.formatMessage({
{ id: "addAncillary.confirmationStep.refundPolicyNightBefore",
id: "addAncillary.confirmationStep.refundPolicy", defaultMessage:
defaultMessage: "All extras can be cancelled until 23:59 the night before arrival. Time selection and special requests can also be modified.",
"All ancillaries are fully refundable until {date}. Time selection and special requests are also modifiable.", })}
},
{ date: refundableDate }
)}
</p> </p>
</Typography> </Typography>
<div className={styles.guarantee}> <div className={styles.guarantee}>
@@ -171,6 +162,7 @@ export default function ConfirmationStep({
<PaymentOptionsGroup name="paymentMethod"> <PaymentOptionsGroup name="paymentMethod">
<PaymentOption <PaymentOption
value={PaymentMethodEnum.card} value={PaymentMethodEnum.card}
type={PaymentMethodEnum.card}
cardNumber={guaranteeInfo.maskedCard.slice(-4)} cardNumber={guaranteeInfo.maskedCard.slice(-4)}
label={intl.formatMessage({ label={intl.formatMessage({
id: "common.card", id: "common.card",
@@ -209,7 +201,7 @@ export default function ConfirmationStep({
</Typography> </Typography>
</AccordionItem> </AccordionItem>
)} )}
{savedCreditCards && <Divider />} {!!savedCreditCards?.length && <Divider />}
<SelectPaymentMethod <SelectPaymentMethod
paymentMethods={(savedCreditCards ?? []).map((card) => ({ paymentMethods={(savedCreditCards ?? []).map((card) => ({
...card, ...card,
@@ -224,6 +216,16 @@ export default function ConfirmationStep({
)} )}
</div> </div>
</> </>
) : (
<Typography variant="Body/Paragraph/mdRegular">
<p className={styles.refundPolicy}>
{intl.formatMessage({
id: "addAncillary.confirmationStep.refundPolicyNightBefore",
defaultMessage:
"All extras can be cancelled until 23:59 the night before arrival. Time selection and special requests can also be modified.",
})}
</p>
</Typography>
)} )}
<TermsAndConditions /> <TermsAndConditions />
<Summary isConfirmation /> <Summary isConfirmation />

View File

@@ -5,10 +5,6 @@
overflow-y: auto; overflow-y: auto;
} }
.form::-webkit-scrollbar {
display: none;
}
.modalScrollable { .modalScrollable {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -110,6 +110,18 @@ export function calculateTotalPrice(rooms: Room[], currency: CurrencyEnum) {
} }
} }
break break
case PriceTypeEnum.money:
{
if (room.totalPoints) {
total.local.additionalPrice =
(total.local.additionalPrice || 0) + room.totalPoints
}
if (!total.local.additionalPriceCurrency) {
total.local.additionalPriceCurrency = CurrencyEnum.POINTS
}
}
break
} }
return total return total

View File

@@ -64,8 +64,9 @@ export function ConfirmBookingPaymentOptions({
{savedCreditCards.map((savedCreditCard) => ( {savedCreditCards.map((savedCreditCard) => (
<PaymentOption <PaymentOption
type={savedCreditCard.cardType as PaymentMethodEnum}
key={savedCreditCard.id} key={savedCreditCard.id}
value={savedCreditCard.id as PaymentMethodEnum} value={savedCreditCard.id}
label={ label={
PAYMENT_METHOD_TITLES[ PAYMENT_METHOD_TITLES[
savedCreditCard.cardType as PaymentMethodEnum savedCreditCard.cardType as PaymentMethodEnum
@@ -86,6 +87,7 @@ export function ConfirmBookingPaymentOptions({
</> </>
) : null} ) : null}
<PaymentOption <PaymentOption
type={PaymentMethodEnum.card}
value={PaymentMethodEnum.card} value={PaymentMethodEnum.card}
label={intl.formatMessage({ label={intl.formatMessage({
id: "common.creditCard", id: "common.creditCard",
@@ -96,6 +98,7 @@ export function ConfirmBookingPaymentOptions({
{!hasMixedRates {!hasMixedRates
? availablePaymentOptions.map((paymentMethod) => ( ? availablePaymentOptions.map((paymentMethod) => (
<PaymentOption <PaymentOption
type={paymentMethod}
key={paymentMethod} key={paymentMethod}
value={paymentMethod} value={paymentMethod}
label={PAYMENT_METHOD_TITLES[paymentMethod]} label={PAYMENT_METHOD_TITLES[paymentMethod]}

View File

@@ -9,14 +9,16 @@ import { PaymentMethodIcon } from '../../Payment/PaymentMethodIcon'
import { Typography } from '../../Typography' import { Typography } from '../../Typography'
export type PaymentOptionProps = { export type PaymentOptionProps = {
value: PaymentMethodEnum value: string
label: string label: string
type: PaymentMethodEnum
cardNumber?: string cardNumber?: string
hideRadioButton?: boolean hideRadioButton?: boolean
} }
export function PaymentOption({ export function PaymentOption({
value, value,
label, label,
type,
cardNumber, cardNumber,
hideRadioButton = false, hideRadioButton = false,
}: PaymentOptionProps) { }: PaymentOptionProps) {
@@ -40,16 +42,15 @@ export function PaymentOption({
<Label>{label}</Label> <Label>{label}</Label>
</Typography> </Typography>
</div> </div>
{cardNumber ? ( <div className={styles.cardContainer}>
<> {cardNumber && (
<Typography variant={'Body/Supporting text (caption)/smRegular'}> <Typography variant={'Body/Supporting text (caption)/smRegular'}>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */} {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span> {cardNumber}</span> <span> {cardNumber}</span>
</Typography> </Typography>
</> )}
) : ( <PaymentMethodIcon paymentMethod={type} alt={label} />
<PaymentMethodIcon paymentMethod={value} alt={label} /> </div>
)}
</> </>
)} )}
</Radio> </Radio>

View File

@@ -22,15 +22,21 @@ export const Default: Story = {
onChange: fn(), onChange: fn(),
children: ( children: (
<> <>
<PaymentOption label="Visa" value={PaymentMethodEnum.visa} /> <PaymentOption
label="Visa"
value={PaymentMethodEnum.visa}
type={PaymentMethodEnum.visa}
/>
<PaymentOption <PaymentOption
label="American Express" label="American Express"
value={PaymentMethodEnum.americanExpress} value={PaymentMethodEnum.americanExpress}
type={PaymentMethodEnum.americanExpress}
/> />
<PaymentOption <PaymentOption
label="MasterCard" label="MasterCard"
value={PaymentMethodEnum.masterCard} value={PaymentMethodEnum.masterCard}
cardNumber="1234" cardNumber="1234"
type={PaymentMethodEnum.masterCard}
/> />
</> </>
), ),

View File

@@ -11,6 +11,12 @@
cursor: pointer; cursor: pointer;
} }
.cardContainer {
display: flex;
gap: var(--Space-x15);
align-items: center;
}
.paymentOption.focused { .paymentOption.focused {
outline: 2px solid var(--UI-Input-Controls-Border-Focus); outline: 2px solid var(--UI-Input-Controls-Border-Focus);
outline-offset: 2px; outline-offset: 2px;

View File

@@ -9,7 +9,7 @@ import styles from './selectPaymentMethod.module.css'
import { import {
PAYMENT_METHOD_TITLES, PAYMENT_METHOD_TITLES,
type PaymentMethodEnum, PaymentMethodEnum,
} from '@scandic-hotels/common/constants/paymentMethod' } from '@scandic-hotels/common/constants/paymentMethod'
type PaymentMethod = { type PaymentMethod = {
@@ -79,8 +79,9 @@ export function SelectPaymentMethod({
return ( return (
<PaymentOption <PaymentOption
type={paymentMethods.cardType}
key={paymentMethods.id} key={paymentMethods.id}
value={paymentMethods.id as PaymentMethodEnum} value={paymentMethods.id}
label={label} label={label}
cardNumber={paymentMethods.truncatedNumber} cardNumber={paymentMethods.truncatedNumber}
/> />
@@ -90,7 +91,8 @@ export function SelectPaymentMethod({
<span>{otherCardLabel}</span> <span>{otherCardLabel}</span>
</Typography> </Typography>
<PaymentOption <PaymentOption
value={PAYMENT_METHOD_TITLES.card as PaymentMethodEnum} value={PaymentMethodEnum.card}
type={PaymentMethodEnum.card}
label={intl.formatMessage({ label={intl.formatMessage({
id: 'common.creditCard', id: 'common.creditCard',
defaultMessage: 'Credit card', defaultMessage: 'Credit card',