feat(BOOK-469): Enter details design changes with guarantee/non-guarantee flow
Approved-by: Bianca Widstam
This commit is contained in:
@@ -1,102 +1,46 @@
|
||||
@keyframes overlay-fade {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes modal-anim {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.guarantee {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Space-x1);
|
||||
}
|
||||
|
||||
.checkboxContainer {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: var(--Space-x1);
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.infoButton {
|
||||
display: flex;
|
||||
gap: var(--Space-x05);
|
||||
justify-self: flex-end;
|
||||
outline: none;
|
||||
}
|
||||
.infoButton:focus-visible {
|
||||
outline: 2px auto -webkit-focus-ring-color;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
align-items: center;
|
||||
background-color: var(--Overlay-40);
|
||||
display: flex;
|
||||
inset: 0;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
z-index: var(--default-modal-overlay-z-index);
|
||||
|
||||
&[data-entering] {
|
||||
animation: overlay-fade 200ms;
|
||||
}
|
||||
|
||||
&[data-exiting] {
|
||||
animation: overlay-fade 150ms reverse ease-in;
|
||||
}
|
||||
}
|
||||
|
||||
.modal {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
border-radius: var(--Corner-radius-lg);
|
||||
box-shadow: 0px 4px 24px 0px rgba(38, 32, 30, 0.08);
|
||||
overflow: hidden;
|
||||
padding: var(--Space-x5) var(--Space-x3);
|
||||
width: min(90dvw, 560px);
|
||||
|
||||
&[data-entering] {
|
||||
animation: modal-anim 200ms;
|
||||
}
|
||||
|
||||
&[data-exiting] {
|
||||
animation: modal-anim 150ms reverse ease-in;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
gap: var(--Space-x2);
|
||||
background-color: var(--Surface-Secondary-Default);
|
||||
border-radius: var(--Corner-radius-lg);
|
||||
padding: var(--Space-x2);
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
.paymentRequired {
|
||||
display: flex;
|
||||
gap: var(--Space-x15);
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
margin-top: var(--Space-x15);
|
||||
outline: none;
|
||||
width: min(164px, 100%);
|
||||
.guaranteeQuestion {
|
||||
display: grid;
|
||||
gap: var(--Space-x1);
|
||||
justify-items: start;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.btnText {
|
||||
display: none;
|
||||
.textWrapper {
|
||||
display: grid;
|
||||
gap: var(--Space-x025);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.guaranteeInfoButton {
|
||||
flex-shrink: 0;
|
||||
margin-left: calc(var(--Space-x3) + var(--Space-x15)); /* Align with checkbox */
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.guaranteeQuestion {
|
||||
display: flex;
|
||||
gap: var(--Space-x15);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.guaranteeInfoButton {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
"use client"
|
||||
import { useLayoutEffect } from "react"
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
} from "react-aria-components"
|
||||
import { useWatch } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useSetOverflowVisibleOnRA from "@scandic-hotels/common/hooks/useSetOverflowVisibleOnRA"
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
||||
import { SelectPaymentMethod } from "@scandic-hotels/design-system/Form/SelectPaymentMethod"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { trackUpdatePaymentMethod } from "@scandic-hotels/tracking/payment"
|
||||
|
||||
import { GuaranteeInfo } from "../../Payment/GuaranteeInfo"
|
||||
import { ConfirmBookingPaymentOptions } from "../PaymentOptions"
|
||||
|
||||
import styles from "./guarantee.module.css"
|
||||
|
||||
@@ -23,114 +15,89 @@ import type { PaymentMethodEnum } from "@scandic-hotels/common/constants/payment
|
||||
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
||||
|
||||
interface GuaranteeProps {
|
||||
mustBeGuaranteed: boolean
|
||||
savedCreditCards: CreditCard[] | null
|
||||
otherPaymentOptions: PaymentMethodEnum[]
|
||||
}
|
||||
|
||||
export default function Guarantee({ savedCreditCards }: GuaranteeProps) {
|
||||
export function Guarantee({
|
||||
mustBeGuaranteed,
|
||||
savedCreditCards,
|
||||
otherPaymentOptions,
|
||||
}: GuaranteeProps) {
|
||||
const intl = useIntl()
|
||||
const guarantee = useWatch({ name: "guarantee" })
|
||||
|
||||
return (
|
||||
<div className={styles.guarantee}>
|
||||
<Checkbox name="guarantee">
|
||||
<div className={styles.checkboxContainer}>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.confirmBooking.guaranteeLabel",
|
||||
defaultMessage:
|
||||
"I may arrive later than 18:00 and want to guarantee my booking.",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
<DialogTrigger>
|
||||
<Button
|
||||
className={styles.infoButton}
|
||||
size="Small"
|
||||
typography="Body/Supporting text (caption)/smBold"
|
||||
variant="Text"
|
||||
>
|
||||
<MaterialIcon icon="info" size={20} color="CurrentColor" />
|
||||
<span className={styles.btnText}>
|
||||
{intl.formatMessage({
|
||||
id: "common.howItWorks",
|
||||
defaultMessage: "How it works",
|
||||
})}
|
||||
</span>
|
||||
</Button>
|
||||
<ModalOverlay className={styles.overlay} isDismissable>
|
||||
<Modal className={styles.modal}>
|
||||
<Dialog>
|
||||
{({ close }) => (
|
||||
<div className={styles.container}>
|
||||
<Typography variant="Title/Subtitle/lg">
|
||||
<h3>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.confirmBooking.guaranteeInfoModalTitle",
|
||||
defaultMessage: "Guarantee for late arrival",
|
||||
})}
|
||||
</h3>
|
||||
</Typography>
|
||||
<Typography variant="Body/Lead text">
|
||||
<p className={styles.text}>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.confirmBooking.guaranteeInfoModalDescription",
|
||||
defaultMessage:
|
||||
"When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.text}>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.confirmBooking.guaranteeInfoModalNoShowInfo",
|
||||
defaultMessage:
|
||||
"In case of a no-show, your credit card will be charged for the first night.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Button
|
||||
className={styles.closeButton}
|
||||
onPress={close}
|
||||
size="Small"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
variant="Secondary"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: "common.close",
|
||||
defaultMessage: "Close",
|
||||
})}
|
||||
</Button>
|
||||
<RestoreOverflow />
|
||||
</div>
|
||||
)}
|
||||
</Dialog>
|
||||
</Modal>
|
||||
</ModalOverlay>
|
||||
</DialogTrigger>
|
||||
</div>
|
||||
</Checkbox>
|
||||
{!mustBeGuaranteed ? (
|
||||
<>
|
||||
<div className={styles.guaranteeQuestion}>
|
||||
<Checkbox name="guarantee" topAlign className={styles.checkbox}>
|
||||
<div className={styles.textWrapper}>
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.guarantee.guaranteeLabel",
|
||||
defaultMessage: "Guarantee for late arrival",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.guarantee.guaranteeInfo",
|
||||
defaultMessage:
|
||||
"I may arrive later than 18:00 and want the hotel to hold my booking.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</Checkbox>
|
||||
<GuaranteeInfo buttonClassName={styles.guaranteeInfoButton} />
|
||||
</div>
|
||||
{guarantee ? <Divider color="Border/Divider/Default" /> : null}
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{guarantee && (
|
||||
<SelectPaymentMethod
|
||||
paymentMethods={(savedCreditCards ?? []).map((card) => ({
|
||||
...card,
|
||||
cardType: card.cardType as PaymentMethodEnum,
|
||||
}))}
|
||||
onChange={(method) => {
|
||||
trackUpdatePaymentMethod({ method })
|
||||
}}
|
||||
formName={"paymentMethod"}
|
||||
/>
|
||||
)}
|
||||
{mustBeGuaranteed || guarantee ? (
|
||||
<>
|
||||
<div className={styles.paymentRequired}>
|
||||
<MaterialIcon icon="credit_card" size={24} color="CurrentColor" />
|
||||
<div className={styles.textWrapper}>
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.guarantee.paymentCardRequiredLabel",
|
||||
defaultMessage:
|
||||
"Payment card required to hold your booking",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.guarantee.paymentCardRequiredInfo",
|
||||
defaultMessage:
|
||||
"Complete the booking and provide your payment card details in the next step.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{savedCreditCards && savedCreditCards.length ? (
|
||||
<>
|
||||
<Divider color="Border/Divider/Default" />
|
||||
<ConfirmBookingPaymentOptions
|
||||
savedCreditCards={savedCreditCards}
|
||||
hasMixedRates={false}
|
||||
otherPaymentOptions={otherPaymentOptions}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function RestoreOverflow() {
|
||||
const setOverflowVisible = useSetOverflowVisibleOnRA()
|
||||
useLayoutEffect(() => {
|
||||
setOverflowVisible(true)
|
||||
}, [setOverflowVisible])
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
"use client"
|
||||
|
||||
import { Label } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import {
|
||||
PAYMENT_METHOD_TITLES,
|
||||
PaymentMethodEnum,
|
||||
} from "@scandic-hotels/common/constants/paymentMethod"
|
||||
import { PaymentOption } from "@scandic-hotels/design-system/Form/PaymentOption"
|
||||
import { PaymentOptionsGroup } from "@scandic-hotels/design-system/Form/PaymentOptionsGroup"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useAvailablePaymentOptions } from "../../../../hooks/useAvailablePaymentOptions"
|
||||
import { useEnterDetailsStore } from "../../../../stores/enter-details"
|
||||
import MixedRatePaymentBreakdown from "../../Payment/MixedRatePaymentBreakdown"
|
||||
|
||||
import styles from "./paymentOptions.module.css"
|
||||
|
||||
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
||||
|
||||
interface ConfirmBookingPaymentOptionsProps {
|
||||
savedCreditCards: CreditCard[] | null
|
||||
hasMixedRates: boolean
|
||||
otherPaymentOptions: PaymentMethodEnum[]
|
||||
}
|
||||
|
||||
export function ConfirmBookingPaymentOptions({
|
||||
savedCreditCards,
|
||||
hasMixedRates,
|
||||
otherPaymentOptions,
|
||||
}: ConfirmBookingPaymentOptionsProps) {
|
||||
const intl = useIntl()
|
||||
const { rooms, currency } = useEnterDetailsStore((state) => ({
|
||||
rooms: state.rooms,
|
||||
currency: state.totalPrice.local.currency,
|
||||
}))
|
||||
const availablePaymentOptions =
|
||||
useAvailablePaymentOptions(otherPaymentOptions)
|
||||
|
||||
return (
|
||||
<>
|
||||
<PaymentOptionsGroup
|
||||
name="paymentMethod"
|
||||
className={styles.paymentOptions}
|
||||
>
|
||||
<Label className="sr-only">
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.payment.paymentMethods",
|
||||
defaultMessage: "Payment methods",
|
||||
})}
|
||||
</Label>
|
||||
|
||||
{savedCreditCards?.length ? (
|
||||
<>
|
||||
<Typography variant="Title/Overline/sm">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "payment.mySavedCards",
|
||||
defaultMessage: "My saved cards",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
|
||||
{savedCreditCards.map((savedCreditCard) => (
|
||||
<PaymentOption
|
||||
key={savedCreditCard.id}
|
||||
value={savedCreditCard.id as PaymentMethodEnum}
|
||||
label={
|
||||
PAYMENT_METHOD_TITLES[
|
||||
savedCreditCard.cardType as PaymentMethodEnum
|
||||
]
|
||||
}
|
||||
cardNumber={savedCreditCard.truncatedNumber}
|
||||
/>
|
||||
))}
|
||||
|
||||
<Typography variant="Title/Overline/sm">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.payment.otherPaymentMethods",
|
||||
defaultMessage: "Other payment methods",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</>
|
||||
) : null}
|
||||
<PaymentOption
|
||||
value={PaymentMethodEnum.card}
|
||||
label={intl.formatMessage({
|
||||
id: "common.creditCard",
|
||||
defaultMessage: "Credit card",
|
||||
})}
|
||||
/>
|
||||
|
||||
{!hasMixedRates
|
||||
? availablePaymentOptions.map((paymentMethod) => (
|
||||
<PaymentOption
|
||||
key={paymentMethod}
|
||||
value={paymentMethod}
|
||||
label={PAYMENT_METHOD_TITLES[paymentMethod]}
|
||||
/>
|
||||
))
|
||||
: null}
|
||||
</PaymentOptionsGroup>
|
||||
|
||||
{hasMixedRates ? (
|
||||
<MixedRatePaymentBreakdown rooms={rooms} currency={currency} />
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
.paymentOptions {
|
||||
display: grid;
|
||||
gap: var(--Space-x15);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
export default function SmsConfirmation() {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<Checkbox name="smsConfirmation">
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "booking.smsConfirmationLabel",
|
||||
defaultMessage:
|
||||
"I would like to get my booking confirmation via sms",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</Checkbox>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.confirmBooking {
|
||||
display: grid;
|
||||
gap: var(--Space-x3);
|
||||
}
|
||||
|
||||
.selections {
|
||||
background-color: var(--Surface-Secondary-Default);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
border-radius: var(--Corner-radius-lg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Space-x2);
|
||||
|
||||
@@ -2,67 +2,41 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import TermsAndConditions from "../Payment/TermsAndConditions"
|
||||
import Guarantee from "./Guarantee"
|
||||
import { Guarantee } from "./Guarantee"
|
||||
import { ConfirmBookingPaymentOptions } from "./PaymentOptions"
|
||||
import SmsConfirmation from "./SmsConfirmation"
|
||||
|
||||
import styles from "./confirm.module.css"
|
||||
|
||||
import type { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
|
||||
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
||||
|
||||
interface ConfirmBookingProps {
|
||||
savedCreditCards: CreditCard[] | null
|
||||
otherPaymentOptions: PaymentMethodEnum[]
|
||||
hasOnlyFlexRates: boolean
|
||||
hasMixedRates: boolean
|
||||
isRedemptionBooking: boolean
|
||||
bookingMustBeGuaranteed: boolean
|
||||
}
|
||||
|
||||
export default function ConfirmBooking({
|
||||
savedCreditCards,
|
||||
otherPaymentOptions,
|
||||
hasOnlyFlexRates,
|
||||
hasMixedRates,
|
||||
isRedemptionBooking,
|
||||
bookingMustBeGuaranteed,
|
||||
}: ConfirmBookingProps) {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.selections}>
|
||||
<Guarantee savedCreditCards={savedCreditCards} />
|
||||
<Divider color="Border/Divider/Default" />
|
||||
<Checkbox name="smsConfirmation">
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "booking.smsConfirmationLabel",
|
||||
defaultMessage:
|
||||
"I would like to get my booking confirmation via sms",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div className={styles.checkboxContainer}>
|
||||
<TermsAndConditions isFlexBookingTerms />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ConfirmBookingRedemption() {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.selections}>
|
||||
<Checkbox name="smsConfirmation">
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "booking.smsConfirmationLabel",
|
||||
defaultMessage:
|
||||
"I would like to get my booking confirmation via sms",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div className={styles.guaranteeContainer}>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
if (isRedemptionBooking) {
|
||||
return (
|
||||
<div className={styles.confirmBooking}>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.confirmBooking.redemptionGuaranteeInfo",
|
||||
@@ -71,10 +45,49 @@ export function ConfirmBookingRedemption() {
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={styles.checkboxContainer}>
|
||||
<TermsAndConditions isFlexBookingTerms />
|
||||
<SmsConfirmation />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (hasOnlyFlexRates) {
|
||||
return (
|
||||
<div className={styles.confirmBooking}>
|
||||
<Guarantee
|
||||
mustBeGuaranteed={bookingMustBeGuaranteed}
|
||||
savedCreditCards={savedCreditCards}
|
||||
otherPaymentOptions={otherPaymentOptions}
|
||||
/>
|
||||
<Divider color="Border/Divider/Subtle" />
|
||||
<TermsAndConditions isFlexBookingTerms />
|
||||
<SmsConfirmation />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.confirmBooking}>
|
||||
{hasMixedRates ? (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "enterDetails.payment.mixedRatesInfo",
|
||||
defaultMessage:
|
||||
"As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
|
||||
<ConfirmBookingPaymentOptions
|
||||
savedCreditCards={savedCreditCards}
|
||||
hasMixedRates={hasMixedRates}
|
||||
otherPaymentOptions={otherPaymentOptions}
|
||||
/>
|
||||
|
||||
<TermsAndConditions isFlexBookingTerms={hasOnlyFlexRates} />
|
||||
<SmsConfirmation />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user