"use client" import { zodResolver } from "@hookform/resolvers/zod" import { useRouter } from "next/navigation" import { useCallback, useEffect, useState } from "react" import { FormProvider, useForm } from "react-hook-form" import { useIntl } from "react-intl" import { BookingStatusEnum, PaymentMethodEnum } from "@/constants/booking" import { bookingTermsAndConditions, privacyPolicy, } from "@/constants/currentWebHrefs" import { guaranteeCallback } from "@/constants/routes/hotelReservation" import { env } from "@/env/client" import { trpc } from "@/lib/trpc/client" 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 { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus" import useLang from "@/hooks/useLang" import { formatPrice } from "@/utils/numberFormatting" 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 { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" import type { CreditCard } from "@/types/user" const maxRetries = 15 const retryInterval = 2000 export interface GuaranteeLateArrivalProps { booking: BookingConfirmation["booking"] handleCloseModal: () => void handleBackToManageStay: () => void savedCreditCards: CreditCard[] | null refId: string } export default function GuaranteeLateArrival({ booking, handleCloseModal, handleBackToManageStay, savedCreditCards, refId, }: GuaranteeLateArrivalProps) { const intl = useIntl() const lang = useLang() const router = useRouter() const methods = useForm({ defaultValues: { paymentMethod: savedCreditCards?.length ? savedCreditCards[0].id : PaymentMethodEnum.card, termsAndConditions: false, }, mode: "all", reValidateMode: "onChange", resolver: zodResolver(paymentSchema), }) const [isPollingForBookingStatus, setIsPollingForBookingStatus] = useState(false) const handlePaymentError = useCallback(() => { toast.error( intl.formatMessage({ id: "We had an issue guaranteeing your booking. Please try again.", }) ) }, [intl]) const guaranteeBooking = trpc.booking.guarantee.useMutation({ onSuccess: (result) => { if (result) { setIsPollingForBookingStatus(true) } else { handlePaymentError() } }, onError: () => { toast.error( intl.formatMessage({ id: "Something went wrong!", }) ) }, }) const bookingStatus = useHandleBookingStatus({ confirmationNumber: booking.confirmationNumber, expectedStatus: BookingStatusEnum.BookingCompleted, maxRetries, retryInterval, enabled: isPollingForBookingStatus, }) useEffect(() => { if (bookingStatus?.data?.paymentUrl) { router.push(bookingStatus.data.paymentUrl) } else if (bookingStatus.isTimeout) { handlePaymentError() } }, [bookingStatus, router, intl, handlePaymentError]) if ( guaranteeBooking.isPending || (isPollingForBookingStatus && !bookingStatus.data?.paymentUrl && !bookingStatus.isTimeout) ) { return (
) } const handleGuaranteeLateArrival = (data: GuaranteeFormData) => { const savedCreditCard = savedCreditCards?.find( (card) => card.id === data.paymentMethod ) const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang)}` if (booking.confirmationNumber) { const card = savedCreditCard ? { alias: savedCreditCard.alias, expiryDate: savedCreditCard.expirationDate, cardType: savedCreditCard.cardType, } : undefined guaranteeBooking.mutate({ confirmationNumber: booking.confirmationNumber, language: lang, ...(card !== undefined && { card }), success: `${guaranteeRedirectUrl}/success/${encodeURIComponent(refId)}`, error: `${guaranteeRedirectUrl}/error/${encodeURIComponent(refId)}`, cancel: `${guaranteeRedirectUrl}/cancel/${encodeURIComponent(refId)}`, }) } else { toast.error( intl.formatMessage({ id: "Confirmation number is missing!", }) ) } } return ( {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.", })} {intl.formatMessage({ id: "In case of no-show you will be charged for the first night.", })} {savedCreditCards?.length ? ( <> {intl.formatMessage({ id: "OTHER" })} ) : null}
{intl.formatMessage( { id: "By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general Terms & Conditions, and understand Scandic will process my personal data for this stay in accordance with Scandic’s Privacy Policy. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.", }, { termsAndConditionsLink: (str) => ( {str} ), privacyPolicyLink: (str) => ( {str} ), } )}
{intl.formatMessage({ id: "Guarantee cost" })} {intl.formatMessage({ id: "Your card will only be used for authorisation", })}
{formatPrice(intl, 0, booking.currencyCode)}
} primaryAction={{ label: intl.formatMessage({ id: "Guarantee" }), onClick: methods.handleSubmit(handleGuaranteeLateArrival), intent: "primary", }} secondaryAction={{ label: intl.formatMessage({ id: "Back" }), onClick: handleBackToManageStay, intent: "text", }} />
) }