2021-07-22 11:03:00 +00:00
/ * *
* External dependencies
* /
import { __ } from '@wordpress/i18n' ;
import classnames from 'classnames' ;
import { createInterpolateElement , useEffect } from '@wordpress/element' ;
2022-04-08 12:11:50 +00:00
import { useStoreCart } from '@woocommerce/base-context/hooks' ;
2021-07-22 11:03:00 +00:00
import {
CheckoutProvider ,
2022-05-25 21:00:47 +00:00
SnackbarNoticesContainer ,
2021-07-22 11:03:00 +00:00
} from '@woocommerce/base-context' ;
2022-05-25 21:00:47 +00:00
import { StoreNoticesContainer } from '@woocommerce/base-context/providers' ;
2021-07-22 11:03:00 +00:00
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary' ;
import { SidebarLayout } from '@woocommerce/base-components/sidebar-layout' ;
import { CURRENT_USER_IS_ADMIN , getSetting } from '@woocommerce/settings' ;
import { SlotFillProvider } from '@woocommerce/blocks-checkout' ;
import withScrollToTop from '@woocommerce/base-hocs/with-scroll-to-top' ;
2022-07-01 23:06:25 +00:00
import { useDispatch , useSelect } from '@wordpress/data' ;
import {
CHECKOUT_STORE_KEY ,
VALIDATION_STORE_KEY ,
} from '@woocommerce/block-data' ;
2021-07-22 11:03:00 +00:00
/ * *
* Internal dependencies
* /
import './styles/style.scss' ;
import EmptyCart from './empty-cart' ;
import CheckoutOrderError from './checkout-order-error' ;
import { LOGIN_TO_CHECKOUT_URL , isLoginRequired , reloadPage } from './utils' ;
import type { Attributes } from './types' ;
import { CheckoutBlockContext } from './context' ;
2022-04-08 12:11:50 +00:00
import { hasNoticesOfType } from '../../utils/notices' ;
import { StoreNoticesProvider } from '../../base/context/providers' ;
2021-07-22 11:03:00 +00:00
const LoginPrompt = ( ) = > {
return (
< >
{ __ (
'You must be logged in to checkout. ' ,
'woo-gutenberg-products-block'
) }
< a href = { LOGIN_TO_CHECKOUT_URL } >
{ __ (
'Click here to log in.' ,
'woo-gutenberg-products-block'
) }
< / a >
< / >
) ;
} ;
const Checkout = ( {
attributes ,
children ,
} : {
attributes : Attributes ;
children : React.ReactChildren ;
} ) : JSX . Element = > {
2022-06-10 16:33:15 +00:00
const { hasOrder , customerId } = useSelect ( ( select ) = > {
const store = select ( CHECKOUT_STORE_KEY ) ;
return {
hasOrder : store.hasOrder ( ) ,
customerId : store.getCustomerId ( ) ,
} ;
} ) ;
2021-07-22 11:03:00 +00:00
const { cartItems , cartIsLoading } = useStoreCart ( ) ;
const {
allowCreateAccount ,
showCompanyField ,
requireCompanyField ,
showApartmentField ,
showPhoneField ,
requirePhoneField ,
} = attributes ;
if ( ! cartIsLoading && cartItems . length === 0 ) {
return < EmptyCart / > ;
}
if ( ! hasOrder ) {
return < CheckoutOrderError / > ;
}
if (
isLoginRequired ( customerId ) &&
allowCreateAccount &&
getSetting ( 'checkoutAllowsSignup' , false )
) {
< LoginPrompt / > ;
}
return (
< CheckoutBlockContext.Provider
value = { {
allowCreateAccount ,
showCompanyField ,
requireCompanyField ,
showApartmentField ,
showPhoneField ,
requirePhoneField ,
} }
>
{ children }
< / CheckoutBlockContext.Provider >
) ;
} ;
const ScrollOnError = ( {
scrollToTop ,
} : {
scrollToTop : ( props : Record < string , unknown > ) = > void ;
} ) : null = > {
2022-06-10 16:33:15 +00:00
const { hasError : checkoutHasError , isIdle : checkoutIsIdle } = useSelect (
( select ) = > {
const store = select ( CHECKOUT_STORE_KEY ) ;
return {
isIdle : store.isIdle ( ) ,
hasError : store.hasError ( ) ,
} ;
}
) ;
2022-07-01 23:06:25 +00:00
const { hasValidationErrors } = useSelect ( ( select ) = > {
const store = select ( VALIDATION_STORE_KEY ) ;
return {
hasValidationErrors : store.hasValidationErrors ( ) ,
} ;
} ) ;
const { showAllValidationErrors } = useDispatch ( VALIDATION_STORE_KEY ) ;
2021-07-22 11:03:00 +00:00
const hasErrorsToDisplay =
checkoutIsIdle &&
checkoutHasError &&
2022-04-08 12:11:50 +00:00
( hasValidationErrors || hasNoticesOfType ( 'wc/checkout' , 'default' ) ) ;
2021-07-22 11:03:00 +00:00
useEffect ( ( ) = > {
2021-08-02 09:19:07 +00:00
let scrollToTopTimeout : number ;
2021-07-22 11:03:00 +00:00
if ( hasErrorsToDisplay ) {
showAllValidationErrors ( ) ;
2021-07-26 14:05:09 +00:00
// Scroll after a short timeout to allow a re-render. This will allow focusableSelector to match updated components.
2021-08-02 09:19:07 +00:00
scrollToTopTimeout = window . setTimeout ( ( ) = > {
2021-07-26 14:05:09 +00:00
scrollToTop ( {
focusableSelector : 'input:invalid, .has-error input' ,
} ) ;
} , 50 ) ;
2021-07-22 11:03:00 +00:00
}
2021-08-02 09:19:07 +00:00
return ( ) = > {
clearTimeout ( scrollToTopTimeout ) ;
} ;
2021-07-22 11:03:00 +00:00
} , [ hasErrorsToDisplay , scrollToTop , showAllValidationErrors ] ) ;
return null ;
} ;
const Block = ( {
attributes ,
children ,
scrollToTop ,
} : {
attributes : Attributes ;
children : React.ReactChildren ;
scrollToTop : ( props : Record < string , unknown > ) = > void ;
2022-04-08 12:11:50 +00:00
} ) : JSX . Element = > {
return (
< BlockErrorBoundary
header = { __ (
2022-09-12 08:39:26 +00:00
'Something went wrong. Please contact us for assistance.' ,
2021-07-22 11:03:00 +00:00
'woo-gutenberg-products-block'
2022-04-08 12:11:50 +00:00
) }
text = { createInterpolateElement (
__ (
'The checkout has encountered an unexpected error. <button>Try reloading the page</button>. If the error persists, please get in touch with us so we can assist.' ,
'woo-gutenberg-products-block'
2021-07-22 11:03:00 +00:00
) ,
2022-04-08 12:11:50 +00:00
{
button : (
< button
className = "wc-block-link-button"
onClick = { reloadPage }
/ >
) ,
}
) }
showErrorMessage = { CURRENT_USER_IS_ADMIN }
>
2022-05-25 21:00:47 +00:00
< SnackbarNoticesContainer context = "wc/checkout" / >
< StoreNoticesProvider >
< StoreNoticesContainer context = "wc/checkout" / >
2022-07-01 23:06:25 +00:00
{ /* SlotFillProvider need to be defined before CheckoutProvider so fills have the SlotFill context ready when they mount. */ }
< SlotFillProvider >
< CheckoutProvider >
< SidebarLayout
className = { classnames ( 'wc-block-checkout' , {
'has-dark-controls' : attributes . hasDarkControls ,
} ) }
>
< Checkout attributes = { attributes } >
{ children }
< / Checkout >
< ScrollOnError scrollToTop = { scrollToTop } / >
< / SidebarLayout >
< / CheckoutProvider >
< / SlotFillProvider >
2022-05-25 21:00:47 +00:00
< / StoreNoticesProvider >
2022-04-08 12:11:50 +00:00
< / BlockErrorBoundary >
) ;
} ;
2021-07-22 11:03:00 +00:00
export default withScrollToTop ( Block ) ;