UNPKG

@shopgate/engage

Version:
204 lines (197 loc) • 8.23 kB
import React, { useState, useEffect, useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; import { SurroundPortals } from "../../../components"; import { DIRECT_SHIP, ROPIS, BOPIS, STAGE_SELECT_STORE, PRODUCT_FULFILLMENT_SELECTOR } from "../../constants"; import { FulfillmentSheet } from "../FulfillmentSheet"; import { FulfillmentSelectorContext } from "./FulfillmentSelector.context"; import { FulfillmentSelectorHeader } from "./FulfillmentSelectorHeader"; import { FulfillmentSelectorItem } from "./FulfillmentSelectorItem"; import { FulfillmentSelectorDirectShip } from "./FulfillmentSelectorDirectShip"; import { FulfillmentSelectorBOPIS } from "./FulfillmentSelectorBOPIS"; import { FulfillmentSelectorROPIS } from "./FulfillmentSelectorROPIS"; import { FulfillmentSelectorAddToCart } from "./FulfillmentSelectorAddToCart"; import { FulfillmentSelectorLocation } from "./FulfillmentSelectorLocation"; import { container } from "./FulfillmentSelector.style"; import connect from "./FulfillmentSelector.connector"; /* eslint-disable max-len */ /** @typedef {import('./FulfillmentSelector.types').FulfillmentSelectorContextProps} FulfillmentSelectorContextProps */ /** @typedef {import('./FulfillmentSelector.types').OwnProps} OwnProps */ /** @typedef {import('./FulfillmentSelector.types').StateProps} StateProps */ /** @typedef {import('./FulfillmentSelector.types').DispatchProps} DispatchProps */ /** @typedef {import('../../locations.types').UserLocationFulfillmentMethod} UserLocationFulfillmentMethod */ /* eslint-enable max-len */ /** * @typedef {OwnProps & StateProps & DispatchProps} Props */ /** * Renders the fulfillment selector radio button group. * @param {Props} props The component props. * @returns {JSX.Element|null} */ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const FulfillmentSelector = props => { const { productId, merchantSettings, shopFulfillmentMethods, productFulfillmentMethods, locationFulfillmentMethods, useLocationFulfillmentMethods, preferredLocation, inventory, conditioner, isDirectShipEnabled, isROPISEnabled, isBOPISEnabled, storeFulfillmentMethod, userFulfillmentMethod, fulfillmentPaths, isOrderable, isReady } = props; const [selection, setSelection] = useState(userFulfillmentMethod); const [selectedLocation, setSelectedLocation] = useState(preferredLocation); const [isOpen, setIsOpen] = useState(false); const usedLocation = selectedLocation || preferredLocation; // Initialize fo selection once it is available. useEffect(() => { if (!selection && userFulfillmentMethod) { setSelection(userFulfillmentMethod); } }, [selection, userFulfillmentMethod]); // Initialize location selection once it is available. useEffect(() => { if (!selectedLocation && preferredLocation) { setSelectedLocation(preferredLocation); } }, [preferredLocation, selectedLocation]); /** @type {UserLocationFulfillmentMethod} */ const fulfillmentMethodFallback = useMemo(() => { if (!shopFulfillmentMethods) { return null; } const states = [isDirectShipEnabled, isBOPISEnabled, isROPISEnabled]; return [DIRECT_SHIP, BOPIS, ROPIS].find((method, index) => states[index] && shopFulfillmentMethods.includes(method)) || null; }, [isBOPISEnabled, isDirectShipEnabled, isROPISEnabled, shopFulfillmentMethods]); /** * Determine and set a default fulfillment method when none was set before. */ useEffect(() => { if (!shopFulfillmentMethods) { return; } if (userFulfillmentMethod && shopFulfillmentMethods.includes(userFulfillmentMethod)) { return; } if (fulfillmentMethodFallback !== userFulfillmentMethod) { setSelection(fulfillmentMethodFallback); storeFulfillmentMethod(fulfillmentMethodFallback); } }, [fulfillmentMethodFallback, isBOPISEnabled, isDirectShipEnabled, isROPISEnabled, shopFulfillmentMethods, storeFulfillmentMethod, userFulfillmentMethod]); useEffect(() => { if (preferredLocation?.code !== selectedLocation?.code) { setSelectedLocation(preferredLocation); } }, [preferredLocation, selectedLocation, shopFulfillmentMethods]); const handleClose = useCallback(newLocationData => { setIsOpen(false); if (!newLocationData && (!usedLocation || !usedLocation.code)) { setSelection(fulfillmentMethodFallback); return; } if (newLocationData) { setSelectedLocation(newLocationData.location); } }, [fulfillmentMethodFallback, usedLocation]); const handleChange = useCallback((method, changeOnly = false) => { conditioner.without('fulfillment-inventory').check().then(passed => { if (!passed) { return; } setSelection(method); storeFulfillmentMethod(method); const isValidRopeSelection = [ROPIS, BOPIS].includes(method) && !!preferredLocation && !!preferredLocation.code; if (!changeOnly && (isOpen || isValidRopeSelection)) { return; } if (method === DIRECT_SHIP) { setSelectedLocation(null); return; } setIsOpen(true); FulfillmentSheet.open({ stage: STAGE_SELECT_STORE, callback: handleClose, changeOnly }); }); }, [conditioner, handleClose, isOpen, preferredLocation, storeFulfillmentMethod]); const shopHasRopeMethods = useMemo(() => { if (!Array.isArray(shopFulfillmentMethods)) { return false; } return shopFulfillmentMethods.filter(method => method !== DIRECT_SHIP).length > 0; }, [shopFulfillmentMethods]); const context = /** @type {FulfillmentSelectorContextProps} */useMemo(() => ({ selection, selectedLocation, inventory, preferredLocation, isDirectShipEnabled, isBOPISEnabled, isROPISEnabled, isReady, productId, handleChange, conditioner, merchantSettings, fulfillmentPaths, userFulfillmentMethod, isOrderable, shopFulfillmentMethods, productFulfillmentMethods, locationFulfillmentMethods, useLocationFulfillmentMethods }), [conditioner, fulfillmentPaths, handleChange, inventory, isBOPISEnabled, isDirectShipEnabled, isOrderable, isROPISEnabled, isReady, locationFulfillmentMethods, merchantSettings, preferredLocation, productFulfillmentMethods, productId, selectedLocation, selection, shopFulfillmentMethods, useLocationFulfillmentMethods, userFulfillmentMethod]); if (!shopHasRopeMethods) { return null; } return /*#__PURE__*/_jsxs(FulfillmentSelectorContext.Provider, { value: context, children: [/*#__PURE__*/_jsx(SurroundPortals, { portalName: PRODUCT_FULFILLMENT_SELECTOR, children: /*#__PURE__*/_jsxs("div", { className: container, role: "radiogroup", tabIndex: "0", children: [/*#__PURE__*/_jsx(FulfillmentSelectorHeader, {}), shopFulfillmentMethods.includes(DIRECT_SHIP) && /*#__PURE__*/_jsx(FulfillmentSelectorItem, { name: DIRECT_SHIP, onChange: handleChange, disabled: !isReady || !isDirectShipEnabled, children: /*#__PURE__*/_jsx(FulfillmentSelectorDirectShip, {}) }), shopFulfillmentMethods.includes(BOPIS) && /*#__PURE__*/_jsx(FulfillmentSelectorItem, { name: BOPIS, onChange: handleChange, disabled: !isReady || !isBOPISEnabled, children: /*#__PURE__*/_jsx(FulfillmentSelectorBOPIS, {}) }), shopFulfillmentMethods.includes(ROPIS) && /*#__PURE__*/_jsx(FulfillmentSelectorItem, { name: ROPIS, onChange: handleChange, disabled: !isReady || !isROPISEnabled, children: /*#__PURE__*/_jsx(FulfillmentSelectorROPIS, {}) })] }) }), /*#__PURE__*/_jsx(FulfillmentSelectorLocation, {}), /*#__PURE__*/_jsx(FulfillmentSelectorAddToCart, {})] }); }; FulfillmentSelector.defaultProps = { merchantSettings: null, shopFulfillmentMethods: null, productFulfillmentMethods: null, locationFulfillmentMethods: null, useLocationFulfillmentMethods: false, preferredLocation: null, inventory: null, userFulfillmentMethod: null }; export default connect(/*#__PURE__*/React.memo(FulfillmentSelector));