@shopgate/engage
Version:
Shopgate's ENGAGE library.
256 lines (242 loc) • 11.3 kB
JavaScript
import React, { useMemo, useState, useEffect, useCallback } from 'react';
import { REGISTER_PATH } from '@shopgate/pwa-common/constants/RoutePaths';
import { LoadingProvider, i18n, useRoute, SHOP_SETTING_REGISTRATION_MODE_SIMPLE } from '@shopgate/engage/core';
import { useFormState } from '@shopgate/engage/core/hooks/useFormState';
import appConfig from '@shopgate/pwa-common/helpers/config';
import PropTypes from 'prop-types';
import { extractDefaultValues } from "../../account/helper/form";
import Context from "./RegistrationProvider.context";
import { generateBaseConstraints, generateBillingConstraints, generateShippingConstraints, generateExtraConstraints } from "./RegistrationProvider.constraints";
import connect from "./RegistrationProvider.connector";
import { MARKETING_OPT_IN_DEFAULT } from "../constants";
import { jsx as _jsx } from "react/jsx-runtime";
const initialBaseFormState = {
emailAddress: '',
password: '',
passwordConfirm: ''
};
const initialAddressFormState = {
firstName: '',
lastName: '',
company: '',
address1: '',
address2: '',
city: '',
country: '',
postalCode: '',
mobile: ''
};
const initialBillingFormState = {
...initialAddressFormState
};
const initialShippingFormState = {
...initialAddressFormState
};
const initialOptInFormState = {
marketingOptIn: MARKETING_OPT_IN_DEFAULT
};
/**
* Converts validation errors into errors for form builder.
* @param {Object} validationErrors The validation errors.
* @returns {Array}
*/
const convertValidationErrors = validationErrors => Object.keys(validationErrors).map(key => ({
path: key,
message: i18n.text(validationErrors[key])
}));
/**
* Registration Provider
* @param {React.ReactNode} children Child components.
* @param {Object} shopSettings Shop settings object.
* @param {Object} userLocation User location object.
* @param {Object} customerAttributes Customer attributes object.
* @param {boolean} isDataReady Indicates if data is ready.
* @param {string} registrationMode Registration mode.
* @param {boolean} [cartHasDirectShipItems=false] Indicates if the cart has direct ship items.
* @param {number} [numberOfAddressLines=null] Number of address lines.
* @param {Function} submitRegistration Function to submit registration.
* @param {Object} [formContainerRef=null] Reference to the form container.
* @returns {JSX.Element}
*/
const RegistrationProvider = ({
isDataReady,
cartHasDirectShipItems,
shopSettings,
userLocation,
customerAttributes,
numberOfAddressLines,
registrationMode,
submitRegistration,
children,
formContainerRef
}) => {
const [isLocked, setLocked] = useState(false);
const [isBaseFormSubmitted, setIsBaseFormSubmitted] = useState(false);
const [isBillingFormSubmitted, setIsBillingFormSubmitted] = useState(false);
const [isShippingFormSubmitted, setIsShippingFormSubmitted] = useState(false);
const [isExtraFormSubmitted, setIsExtraFormSubmitted] = useState(false);
const [baseFormRequestErrors, setBaseFormRequestErrors] = useState(null);
const [billingFormRequestErrors, setBillingFormRequestErrors] = useState(null);
const [shippingFormRequestErrors, setShippingFormRequestErrors] = useState(null);
const [extraFormRequestErrors, setExtraFormRequestErrors] = useState(null);
const [isShippingFormVisible, setIsShippingFormVisible] = useState(false);
const {
query
} = useRoute();
const isShippingAddressSelectionEnabled = useMemo(() => query?.checkout && cartHasDirectShipItems && registrationMode !== SHOP_SETTING_REGISTRATION_MODE_SIMPLE, [cartHasDirectShipItems, query, registrationMode]);
const isBillingAddressSelectionEnabled = useMemo(() => registrationMode !== SHOP_SETTING_REGISTRATION_MODE_SIMPLE, [registrationMode]);
// Determine values to prefill some form fields
const userCountry = useMemo(() => userLocation?.country || appConfig?.marketId || null, [userLocation]);
const userRegion = useMemo(() => userLocation?.region || null, [userLocation]);
const baseConstraints = useMemo(() => generateBaseConstraints({
registrationMode
}), [registrationMode]);
const billingConstraints = useMemo(() => generateBillingConstraints({
registrationMode
}), [registrationMode]);
const shippingConstraints = useMemo(() => generateShippingConstraints({
registrationMode
}), [registrationMode]);
const extraConstraints = useMemo(() => generateExtraConstraints(customerAttributes), [customerAttributes]);
// Default form states
const defaultBaseFormState = useMemo(() => ({
...initialBaseFormState
}), []);
const defaultBillingFormState = useMemo(() => ({
...initialBillingFormState,
country: userCountry,
region: userRegion
}), [userCountry, userRegion]);
const defaultShippingFormState = useMemo(() => ({
...initialShippingFormState,
country: userCountry,
region: userRegion
}), [userCountry, userRegion]);
const defaultExtraFormState = useMemo(() => ({
...initialOptInFormState,
...extractDefaultValues(customerAttributes)
}), [customerAttributes]);
// Form submit handlers
const handleBaseFormSubmit = useCallback(() => {
setIsBaseFormSubmitted(true);
}, [setIsBaseFormSubmitted]);
const handleBillingFormSubmit = useCallback(() => {
setIsBillingFormSubmitted(true);
}, [setIsBillingFormSubmitted]);
const handleShippingFormSubmit = useCallback(() => {
setIsShippingFormSubmitted(true);
}, [setIsShippingFormSubmitted]);
const handleExtraFormSubmit = useCallback(() => {
setIsExtraFormSubmitted(true);
}, []);
// Form states
const baseFormState = useFormState(defaultBaseFormState, handleBaseFormSubmit, baseConstraints, formContainerRef);
const billingFormState = useFormState(defaultBillingFormState, handleBillingFormSubmit, billingConstraints, formContainerRef);
const shippingFormState = useFormState(defaultShippingFormState, handleShippingFormSubmit, shippingConstraints, formContainerRef);
const extraFormState = useFormState(defaultExtraFormState, handleExtraFormSubmit, extraConstraints, formContainerRef);
// Central submit handler
const handleSubmit = useCallback(() => {
baseFormState.handleSubmit(new Event('submit'));
billingFormState.handleSubmit(new Event('submit'));
shippingFormState.handleSubmit(new Event('submit'));
extraFormState.handleSubmit(new Event('submit'));
}, [baseFormState, billingFormState, extraFormState, shippingFormState]);
useEffect(() => {
// Break the process when the forms are not submitted yet
if (!isBaseFormSubmitted || !isBillingFormSubmitted || !isShippingFormSubmitted || !isExtraFormSubmitted) {
return;
}
// Break the process when one of the forms has validation errors from the constraints
if (!baseFormState.valid || !billingFormState.valid || isShippingFormVisible && !shippingFormState.valid || !extraFormState.valid) {
setIsBaseFormSubmitted(false);
setIsBillingFormSubmitted(false);
setIsShippingFormSubmitted(false);
setIsExtraFormSubmitted(false);
return;
}
/** Async wrapper for submit registration */
const fn = async () => {
setLocked(true);
const response = await submitRegistration({
baseFormData: baseFormState.values,
billingFormData: billingFormState.values,
additionalFormData: extraFormState.values,
...(isShippingFormVisible ? {
shippingFormData: shippingFormState.values
} : {})
});
const {
errors
} = response || {};
// Updated the request validation errors
setBaseFormRequestErrors(errors?.baseFormData || null);
setBillingFormRequestErrors(errors?.billingFormData || null);
setShippingFormRequestErrors(errors?.shippingFormData || null);
setExtraFormRequestErrors(errors?.extraFormData || null);
// Release forms for additional submits
setIsBaseFormSubmitted(false);
setIsBillingFormSubmitted(false);
setIsShippingFormSubmitted(false);
setIsExtraFormSubmitted(false);
setLocked(false);
};
fn();
/* eslint-disable react-hooks/exhaustive-deps */
}, [isBaseFormSubmitted, isBillingFormSubmitted, isShippingFormSubmitted, isExtraFormSubmitted, baseFormState.valid, billingFormState.valid, shippingFormState.valid, extraFormState.valid, isShippingFormVisible, submitRegistration]);
/* eslint-enable react-hooks/exhaustive-deps */
/* eslint-disable react-hooks/exhaustive-deps */
useEffect(() => {
baseFormState.scrollToError();
}, [baseFormRequestErrors, billingFormRequestErrors, shippingFormRequestErrors, baseFormState.scrollToError]);
/* eslint-enable react-hooks/exhaustive-deps */
/* eslint-disable react-hooks/exhaustive-deps */
useEffect(() => {
shippingFormState.setIgnoreErrors(!isShippingFormVisible);
}, [isShippingFormVisible]);
/* eslint-enable react-hooks/exhaustive-deps */
useEffect(() => {
if (isLocked) {
LoadingProvider.setLoading(REGISTER_PATH);
return;
}
LoadingProvider.unsetLoading(REGISTER_PATH);
}, [isLocked]);
const value = useMemo(() => ({
supportedCountries: shopSettings.supportedCountries || [],
countrySortOrder: shopSettings.countrySortOrder || [],
customerAttributes,
userLocation,
defaultBaseFormState,
defaultBillingFormState,
defaultShippingFormState,
defaultExtraFormState,
baseFormValidationErrors: convertValidationErrors(baseFormState.validationErrors || baseFormRequestErrors || {}),
billingFormValidationErrors: convertValidationErrors(billingFormState.validationErrors || billingFormRequestErrors || {}),
shippingFormValidationErrors: convertValidationErrors(shippingFormState.validationErrors || shippingFormRequestErrors || {}),
extraFormValidationErrors: convertValidationErrors(extraFormState.validationErrors || extraFormRequestErrors || {}),
handleSubmit,
updateBaseForm: baseFormState.setValues,
updateBillingForm: billingFormState.setValues,
updateShippingForm: shippingFormState.setValues,
updateExtraForm: extraFormState.setValues,
isShippingAddressSelectionEnabled,
isBillingAddressSelectionEnabled,
isShippingFormVisible,
setIsShippingFormVisible,
numberOfAddressLines,
registrationMode
}), [shopSettings.supportedCountries, shopSettings.countrySortOrder, customerAttributes, userLocation, defaultBaseFormState, defaultBillingFormState, defaultShippingFormState, baseFormState.validationErrors, baseFormState.setValues, baseFormRequestErrors, billingFormState.validationErrors, billingFormState.setValues, billingFormRequestErrors, shippingFormState.validationErrors, shippingFormState.setValues, shippingFormRequestErrors, handleSubmit, defaultExtraFormState, extraFormState.setValues, extraFormState.validationErrors, extraFormRequestErrors, isShippingAddressSelectionEnabled, isBillingAddressSelectionEnabled, isShippingFormVisible, setIsShippingFormVisible, numberOfAddressLines, registrationMode]);
if (!isDataReady) {
return null;
}
return /*#__PURE__*/_jsx(Context.Provider, {
value: value,
children: children
});
};
RegistrationProvider.defaultProps = {
formContainerRef: null,
cartHasDirectShipItems: false,
numberOfAddressLines: null
};
export default connect(RegistrationProvider);