import { useBookingContext } from '../../../providers/BookingFormProvider/BookingFormProvider' import { PaymentSelector } from '../../../components/PaymentSelector/PaymentSelector' import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState, } from 'react' import { FormNotice } from '../../../components/FormNotice/FormNotice' import { __ } from '@wordpress/i18n' import './Steps.scss' import { Button } from '../../../components/Button/Button' import { useDispatch, useSelect } from '@wordpress/data' import { store, store_name } from '../../../../store/frontend' import sslPaymentLogo from '../../../../../public/images/logos/ssl-encryption.svg' import protectedPrivacyLogo from '../../../../../public/images/logos/privacy-protected.svg' import { CustomScroll } from 'react-custom-scroll' import classNames from 'classnames' import { useWording } from '../../../hooks/useWording' import { stripeMethods } from '../PaymentHandler/payments/Stripe/StripeMethods' const PaymentStepStripeSection = lazy(() => import( /* webpackMode: "eager" */ './PaymentStepStripeSection/PaymentStepStripeSection' ).then( (module) => ({ default: module.PaymentStepStripeSection }) ) ) const scrollToElementInContainer = (element: HTMLElement | null) => { if (!element) return let container = element.parentElement while (container) { const style = window.getComputedStyle(container) if ( style.overflowY === 'auto' || style.overflowY === 'scroll' || container.classList.contains('wbk_step__scroll-wrapper') ) { const containerRect = container.getBoundingClientRect() const elementRect = element.getBoundingClientRect() const relativeTop = elementRect.top - containerRect.top container.scrollTo({ top: container.scrollTop + relativeTop, behavior: 'smooth', }) return } container = container.parentElement } } export const PaymentStep = () => { const { fetchBookingAmounts, setLoading, setBookingAmounts } = useDispatch(store_name) const { formData, setFormData, paymentMethods, amountData, preset, disableCustomScroll } = useBookingContext() const couponInputRef = useRef(null) const stripeWrapperRef = useRef(null) const [applied, setApplied] = useState(false) const [showFeedback, setShowFeedback] = useState(false) const [paymentElementLoading, setPaymentElementLoading] = useState(false) const [showStripeError, setShowStripeError] = useState(false) const handleLoadingChange = useCallback((loading: boolean) => { setPaymentElementLoading(loading) if (!loading) { setTimeout(() => { scrollToElementInContainer(stripeWrapperRef.current) }, 100) } }, []) const handleSelectMethod = useCallback( (method: string) => { setFormData('payment_method', method) setShowStripeError(false) setPaymentElementLoading(false) }, [setFormData] ) const isCouponLoading = useSelect( // @ts-ignore (select) => select(store).getLoading().applyingCoupon, [] ) const isBookingAmountsLoading = useSelect( // @ts-ignore (select) => select(store).getLoading().bookingAmounts, [] ) const applyCoupon = useCallback(async () => { const coupon = couponInputRef.current?.value || '' setLoading('applyingCoupon', true) const updatedFormData = { ...formData, coupon, } if ( !coupon || !updatedFormData.places || Object.keys(updatedFormData.places).length === 0 ) { setLoading('applyingCoupon', false) return } await fetchBookingAmounts({ ...updatedFormData, generate_stripe_intent: true, }) setApplied(true) setShowFeedback(true) setLoading('applyingCoupon', false) }, [formData, fetchBookingAmounts]) // fetch amount data on stripe payment selection useEffect(() => { if (stripeMethods.includes(formData.payment_method)) { fetchBookingAmounts({ ...formData, generate_stripe_intent: true, }) } }, [formData.payment_method, fetchBookingAmounts]) // Reset feedback when coupon changes useEffect(() => { setApplied(false) setShowFeedback(false) }, [formData.coupon]) useEffect(() => { setShowStripeError(false) if ( stripeMethods.includes(formData.payment_method) && amountData?.stripe_details?.error ) { setShowStripeError(true) setPaymentElementLoading(false) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [amountData?.stripe_details?.error]) const couponApplied = applied && amountData.discount > 0 const couponFailed = applied && showFeedback && amountData.discount === 0 const wording = useWording() const paymentMessage = useMemo(() => { switch (formData.payment_method) { case 'bank': return wording?.bank_transfer_message || '' case 'arrival': return wording?.pay_on_arrival_message || '' case 'paypal': return wording?.paypal_message || '' case 'woocommerce': return wording?.woocommerce_message || '' default: return '' } }, [wording, formData.payment_method]) const content = (
{paymentMethods.length > 1 && amountData?.to_pay_total > 0 && ( )} {['bank', 'arrival', 'woocommerce', 'paypal'].includes( formData.payment_method ) && paymentMessage.length > 0 && ( )} {(Number(amountData?.left_to_pay) > 0 || !!formData.pay_full_amount) && ( )}
{preset?.settings.coupons_enabled && ( <>
{ setFormData('coupon', e.target.value) }} />
{couponApplied && !isCouponLoading && (
{wording?.coupon_applied_success || __( 'Coupon applied successfully!', 'webba-booking-lite' )}
)} {couponFailed && !isCouponLoading && (
{wording?.coupon_failed || __( 'Coupon did not apply. Please check your code.', 'webba-booking-lite' )}
)} )} {showStripeError && amountData?.stripe_details?.error && (
{amountData.stripe_details.error}
)} {stripeMethods.includes(formData?.payment_method) && amountData?.stripe_details?.client_secret && amountData?.to_pay_total > 0 && ( )}
{wording?.ssl {
) return ( disableCustomScroll ? (
{content}
) : ( {content} ) ) }