Files
web/apps/scandic-web/components/HotelReservation/MyStay/GuaranteeLateArrival/index.tsx
Michael Zetterberg 35862d03f0 feat(SW-706): fix the build
The pinned version of @formatjs/swc-plugin-experimental has a bug in it that
doesn't allow it to parse ’. But we can't upgrade to the new version due to
incompatibilities between SWC and Next. We can upgrade once we move to Next 15.
2025-04-14 11:30:07 +00:00

236 lines
8.6 KiB
TypeScript

"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useRouter } from "next/navigation"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { PaymentMethodEnum } from "@/constants/booking"
import {
bookingTermsAndConditions,
privacyPolicy,
} from "@/constants/currentWebHrefs"
import { guaranteeCallback } from "@/constants/routes/hotelReservation"
import { env } from "@/env/client"
import { useManageStayStore } from "@/stores/my-stay/manageStayStore"
import { useMyStayRoomDetailsStore } from "@/stores/my-stay/myStayRoomDetailsStore"
import LoadingSpinner from "@/components/LoadingSpinner"
import { ModalContentWithActions } from "@/components/Modal/ModalContentWithActions"
import Divider from "@/components/TempDesignSystem/Divider"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import { toast } from "@/components/TempDesignSystem/Toasts"
import { useGuaranteeBooking } from "@/hooks/booking/useGuaranteeBooking"
import useLang from "@/hooks/useLang"
import { formatPrice } from "@/utils/numberFormatting"
import { trackGlaSaveCardAttempt } from "@/utils/tracking/myStay"
import MySavedCards from "../../EnterDetails/Payment/MySavedCards"
import PaymentOption from "../../EnterDetails/Payment/PaymentOption"
import { type GuaranteeFormData, paymentSchema } from "./schema"
import styles from "./guaranteeLateArrival.module.css"
import type { CreditCard } from "@/types/user"
export interface GuaranteeLateArrivalProps {
savedCreditCards: CreditCard[] | null
refId: string
}
export default function GuaranteeLateArrival({
savedCreditCards,
refId,
}: GuaranteeLateArrivalProps) {
const intl = useIntl()
const lang = useLang()
const router = useRouter()
const bookedRoom = useMyStayRoomDetailsStore((state) => state.bookedRoom)
const {
actions: { handleCloseView, handleCloseModal },
} = useManageStayStore()
const methods = useForm<GuaranteeFormData>({
defaultValues: {
paymentMethod: savedCreditCards?.length
? savedCreditCards[0].id
: PaymentMethodEnum.card,
termsAndConditions: false,
},
mode: "all",
reValidateMode: "onChange",
resolver: zodResolver(paymentSchema),
})
const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang)}`
const { guaranteeBooking, isLoading, handleGuaranteeError } =
useGuaranteeBooking({
confirmationNumber: bookedRoom.confirmationNumber,
handleBookingCompleted: router.refresh,
})
if (isLoading) {
return (
<div className={styles.loading}>
<LoadingSpinner />
</div>
)
}
const handleGuaranteeLateArrival = (data: GuaranteeFormData) => {
const savedCreditCard = savedCreditCards?.find(
(card) => card.id === data.paymentMethod
)
trackGlaSaveCardAttempt(bookedRoom.hotelId, savedCreditCard, "yes")
if (bookedRoom.confirmationNumber) {
const card = savedCreditCard
? {
alias: savedCreditCard.alias,
expiryDate: savedCreditCard.expirationDate,
cardType: savedCreditCard.cardType,
}
: undefined
guaranteeBooking.mutate({
confirmationNumber: bookedRoom.confirmationNumber,
language: lang,
...(card !== undefined && { card }),
success: `${guaranteeRedirectUrl}?status=success&RefId=${encodeURIComponent(refId)}`,
error: `${guaranteeRedirectUrl}?status=error&RefId=${encodeURIComponent(refId)}`,
cancel: `${guaranteeRedirectUrl}?status=cancel&RefId=${encodeURIComponent(refId)}`,
})
} else {
handleGuaranteeError("No confirmation number")
toast.error(
intl.formatMessage({
defaultMessage: "Something went wrong!",
})
)
}
}
return (
<FormProvider {...methods}>
<ModalContentWithActions
title={intl.formatMessage({
defaultMessage: "Guarantee late arrival",
})}
onClose={handleCloseModal}
content={
<>
<Caption>
{intl.formatMessage({
defaultMessage:
"Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be reallocated after 18:00.",
})}
</Caption>
<Caption type="bold">
{intl.formatMessage({
defaultMessage:
"In case of no-show you will be charged for the first night.",
})}
</Caption>
{savedCreditCards?.length ? (
<>
<MySavedCards savedCreditCards={savedCreditCards} />
<Body color="uiTextHighContrast" textTransform="bold">
{intl.formatMessage({
defaultMessage: "OTHER",
})}
</Body>
</>
) : null}
<PaymentOption
name="paymentMethod"
value={PaymentMethodEnum.card}
label={intl.formatMessage({
defaultMessage: "Credit card",
})}
/>
<div className={styles.termsAndConditions}>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{intl.formatMessage(
{
defaultMessage:
"By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandic's Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.",
},
{
termsAndConditionsLink: (str) => (
<Link
variant="underscored"
color="peach80"
target="_blank"
href={bookingTermsAndConditions[lang]}
>
{str}
</Link>
),
privacyPolicyLink: (str) => (
<Link
variant="underscored"
color="peach80"
target="_blank"
href={privacyPolicy[lang]}
>
{str}
</Link>
),
}
)}
</p>
</Typography>
<Checkbox name="termsAndConditions">
<Typography variant="Body/Supporting text (caption)/smRegular">
<span>
{intl.formatMessage({
defaultMessage: "I accept the terms and conditions",
})}
</span>
</Typography>
</Checkbox>
</div>
<div className={styles.guaranteeCost}>
<div className={styles.guaranteeCostText}>
<Caption type="bold">
{intl.formatMessage({
defaultMessage: "Guarantee cost",
})}
</Caption>
<Caption color="uiTextHighContrast">
{intl.formatMessage({
defaultMessage:
"Your card will only be used for authorisation",
})}
</Caption>
</div>
<Divider variant="vertical" color="subtle" />
<Body textTransform="bold">
{formatPrice(intl, 0, bookedRoom.currencyCode)}
</Body>
</div>
</>
}
primaryAction={{
label: intl.formatMessage({
defaultMessage: "Guarantee",
}),
onClick: methods.handleSubmit(handleGuaranteeLateArrival),
intent: "primary",
}}
secondaryAction={{
label: intl.formatMessage({
defaultMessage: "Back",
}),
onClick: handleCloseView,
intent: "text",
}}
/>
</FormProvider>
)
}