Feat/SW-1996 tracking gla my stay * feat(SW-1996): tracking gla my stay * feat(SW-1996): update gla tracking * feat(SW-1996): update gla tracking * feat(SW-1996): fix comment * feat(SW-1996): fix camelCase Approved-by: Niclas Edenvin Approved-by: Erik Tiekstra
210 lines
7.6 KiB
TypeScript
210 lines
7.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 { 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 } = 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,
|
||
data.paymentMethod,
|
||
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 {
|
||
toast.error(
|
||
intl.formatMessage({
|
||
id: "Something went wrong!",
|
||
})
|
||
)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<FormProvider {...methods}>
|
||
<ModalContentWithActions
|
||
title={intl.formatMessage({ id: "Guarantee late arrival" })}
|
||
onClose={handleCloseModal}
|
||
content={
|
||
<>
|
||
<Caption>
|
||
{intl.formatMessage({
|
||
id: "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({
|
||
id: "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({ id: "OTHER" })}
|
||
</Body>
|
||
</>
|
||
) : null}
|
||
<PaymentOption
|
||
name="paymentMethod"
|
||
value={PaymentMethodEnum.card}
|
||
label={intl.formatMessage({ id: "Credit card" })}
|
||
/>
|
||
<div className={styles.termsAndConditions}>
|
||
<Checkbox topAlign name={"termsAndConditions"}>
|
||
<Caption>
|
||
{intl.formatMessage(
|
||
{
|
||
id: "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"
|
||
href={bookingTermsAndConditions[lang]}
|
||
target="_blank"
|
||
>
|
||
{str}
|
||
</Link>
|
||
),
|
||
privacyPolicyLink: (str) => (
|
||
<Link
|
||
variant="underscored"
|
||
href={privacyPolicy[lang]}
|
||
target="_blank"
|
||
>
|
||
{str}
|
||
</Link>
|
||
),
|
||
}
|
||
)}
|
||
</Caption>
|
||
</Checkbox>
|
||
</div>
|
||
<div className={styles.guaranteeCost}>
|
||
<div className={styles.guaranteeCostText}>
|
||
<Caption type="bold">
|
||
{intl.formatMessage({ id: "Guarantee cost" })}
|
||
</Caption>
|
||
<Caption color="uiTextHighContrast">
|
||
{intl.formatMessage({
|
||
id: "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({ id: "Guarantee" }),
|
||
onClick: methods.handleSubmit(handleGuaranteeLateArrival),
|
||
intent: "primary",
|
||
}}
|
||
secondaryAction={{
|
||
label: intl.formatMessage({ id: "Back" }),
|
||
onClick: handleCloseView,
|
||
intent: "text",
|
||
}}
|
||
/>
|
||
</FormProvider>
|
||
)
|
||
}
|