UNPKG

@shopgate/engage

Version:
294 lines (283 loc) • 10.5 kB
import React, { useState, useCallback, useRef } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { css } from 'glamor'; import { RippleButton, SurroundPortals } from '@shopgate/engage/components'; import { hasNewServices } from '@shopgate/engage/core/helpers'; import { i18n } from '@shopgate/engage/core'; import { getFavoritesLists, isInitialLoading, getHasMultipleFavoritesListsSupport } from '@shopgate/pwa-common-commerce/favorites/selectors'; import addFavoritesList from '@shopgate/pwa-common-commerce/favorites/actions/addFavoritesList'; import updateFavoritesList from '@shopgate/pwa-common-commerce/favorites/actions/updateFavoritesList'; import removeFavoritesList from '@shopgate/pwa-common-commerce/favorites/actions/removeFavoritesList'; import { removeFavorites } from '@shopgate/pwa-common-commerce/favorites/actions/toggleFavorites'; import addProductsToCart from '@shopgate/pwa-common-commerce/cart/actions/addProductsToCart'; import { FulfillmentSheet, MULTI_LINE_RESERVE, STAGE_SELECT_STORE } from '@shopgate/engage/locations'; import { openSheet } from '@shopgate/engage/locations/providers/FulfillmentProvider'; import { getWishlistMode } from '@shopgate/engage/core/selectors/shopSettings'; import { WISHLIST_MODE_PERSIST_ON_ADD } from '@shopgate/engage/core/constants/shopSettings'; import { getPreferredLocation, getPreferredFulfillmentMethod, getUserSearch } from '@shopgate/engage/locations/selectors'; import { responsiveMediaQuery } from '@shopgate/engage/styles'; import { makeGetEnabledFulfillmentMethods } from '@shopgate/engage/core/config'; import { fetchProductLocations } from '@shopgate/engage/locations/actions'; import List from "../List"; import ListsModal from "./ListsModal"; import CommentDialog from "../CommentDialog"; import ItemFulfillmentMethod from "../ItemFulfillmentMethod"; import { FAVORITES_LIST_ADD_BUTTON, FAVORITES_LIST } from "../../constants/Portals"; /** * @param {Object} state State * @param {Object} props Props * @returns {Object} */ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const makeMapStateToProps = () => { const getFulfillmentMethods = makeGetEnabledFulfillmentMethods(); return (state, props) => ({ isInitializing: isInitialLoading(state), lists: getFavoritesLists(state), preferredLocation: getPreferredLocation(state, props), preferredFulfillmentMethod: getPreferredFulfillmentMethod(state, props), shopFulfillmentMethods: getFulfillmentMethods(state, props), wishlistMode: getWishlistMode(state), userSearch: getUserSearch(state), hasMultipleFavoritesListsSupport: getHasMultipleFavoritesListsSupport(state) }); }; /** * @param {Object} dispatch Dispatch * @returns {Object} */ const mapDispatchToProps = dispatch => ({ addList: name => dispatch(addFavoritesList(name)), updateList: (id, name) => dispatch(updateFavoritesList(id, name)), removeList: id => dispatch(removeFavoritesList(id)), removeItem: (listId, productId) => dispatch(removeFavorites(productId, false, listId)), addToCart: (...params) => dispatch(addProductsToCart.apply(void 0, params)), fetchLocations: (productId, params) => dispatch(fetchProductLocations(productId, params)) }); const styles = { root: css({ [responsiveMediaQuery('>=md', { webOnly: true })]: { margin: 8 } }), addButton: css({ width: 'calc(100% - 32px)', margin: 16, backgroundColor: 'var(--color-primary)', borderRadius: 5, [responsiveMediaQuery('>=md', { webOnly: true })]: { width: 240, float: 'right' } }).toString() }; /** * @param {Object} props Props * @returns {Object} */ const FavoriteLists = ({ addList, updateList, removeList, removeItem, addToCart, preferredFulfillmentMethod, preferredLocation, wishlistMode, lists, isInitializing, shopFulfillmentMethods, userSearch, fetchLocations, hasMultipleFavoritesListsSupport }) => { // Add to cart state. const promiseRef = useRef(null); const [activeProductId, setActiveProductId] = useState(null); const [fulfillmentMethods, setFulfillmentMethods] = useState([]); const [fulfillmentMethod, setFulfillmentMethod] = useState(null); const [foMethodChooser, setFOMethodChooser] = useState(false); const handleAddToCartWithMethod = useCallback(method => { // Hide modal if visible. setFOMethodChooser(false); // Handle cancellation. if (!method) { return; } setFulfillmentMethod(method); // Direct ship. if (method === 'directShip') { addToCart([{ productId: activeProductId, quantity: 1 }]); promiseRef.current.resolve(); return; } // Open the sheet fetchLocations(activeProductId, userSearch); setTimeout(() => { openSheet({ callback: state => { if (state) { return promiseRef.current?.resolve(); } return promiseRef.current?.reject(); }, fulfillmentPath: MULTI_LINE_RESERVE, stage: STAGE_SELECT_STORE }); }, 10); }, [activeProductId, addToCart, fetchLocations, userSearch]); const handleAddToCart = useCallback((listId, product, quantity = 1) => { // Create promise to inform add to cart button when ready. const promise = new Promise((resolve, reject) => { promiseRef.current = { resolve: () => { // Remove item from wishlist after adding to cart. if (wishlistMode !== WISHLIST_MODE_PERSIST_ON_ADD) { removeItem(listId, product.id); } resolve(); }, reject }; }); if (!hasNewServices()) { addToCart([{ productId: product.id, quantity }]); promiseRef.current.resolve(); return promise; } // Get location. let activeLocation = null; if (preferredLocation) { activeLocation = preferredLocation; } // Get fulfillment method that is both active for location and product. let activeFulfillmentMethod = preferredFulfillmentMethod || fulfillmentMethod; const availableFulfillmentMethods = shopFulfillmentMethods?.filter(s => product.fulfillmentMethods.indexOf(s) !== -1) || []; if (activeLocation && !activeFulfillmentMethod && availableFulfillmentMethods.length === 1) { [activeFulfillmentMethod] = availableFulfillmentMethods; } // If all options are already configured immediately add it to the cart. if (activeFulfillmentMethod && activeLocation) { addToCart([{ productId: product.id, quantity, fulfillment: { method: activeFulfillmentMethod, location: { code: activeLocation.code, name: activeLocation.name || '' } } }]); promiseRef.current.resolve(); return promise; } // Location not set but FO method is set. setActiveProductId(product.id); if (activeFulfillmentMethod && !activeLocation) { handleAddToCartWithMethod(activeFulfillmentMethod); return promise; } if (!activeFulfillmentMethod && !activeFulfillmentMethod) { // Long path is required <- fo method and location unset. setFulfillmentMethods(shopFulfillmentMethods); setFOMethodChooser(true); return promise; } // Short path is required <- fo method is unset. setFulfillmentMethods(availableFulfillmentMethods); setFOMethodChooser(true); return promise; }, [addToCart, fulfillmentMethod, handleAddToCartWithMethod, preferredFulfillmentMethod, preferredLocation, removeItem, shopFulfillmentMethods, wishlistMode]); const handleMethodClose = useCallback(() => { setFOMethodChooser(false); promiseRef.current.reject(); }, []); // Modal for renaming and adding. const [modalOpen, setModalOpen] = useState(false); const [modalType, setModalType] = useState(null); const [modalTarget, setModalTarget] = useState(null); const openAddModal = useCallback(() => { setModalOpen(true); setModalType('add_list'); setModalTarget(null); }, []); const openRenameModal = useCallback(code => { setModalOpen(true); setModalType('rename_list'); setModalTarget(code); }, []); const closeModal = useCallback(() => { setModalOpen(false); setModalType(null); setModalTarget(null); }, []); const confirmModal = useCallback(name => { if (modalType === 'add_list') { addList(name); } else if (modalType === 'rename_list') { updateList(modalTarget, name); } closeModal(); }, [addList, closeModal, modalTarget, modalType, updateList]); if (isInitializing) { return null; } return /*#__PURE__*/_jsxs("div", { className: styles.root, children: [lists.map(list => /*#__PURE__*/_jsx(SurroundPortals, { portalName: FAVORITES_LIST, portalProps: list, children: /*#__PURE__*/_jsx(List, { id: list.id, name: list.name, rename: openRenameModal, remove: () => removeList(list.id), removeItem: productId => removeItem(list.id, productId), addToCart: (product, quantity) => handleAddToCart(list.id, product, quantity), hasMultipleFavoritesListsSupport: hasMultipleFavoritesListsSupport }) }, list.id)), modalOpen ? /*#__PURE__*/_jsx(ListsModal, { type: modalType, onDismiss: closeModal, onConfirm: confirmModal }) : null, activeProductId ? /*#__PURE__*/_jsx(FulfillmentSheet, { productId: activeProductId, fulfillmentMethod: fulfillmentMethod }) : null, /*#__PURE__*/_jsx(CommentDialog, {}), /*#__PURE__*/_jsx(ItemFulfillmentMethod, { isOpen: foMethodChooser, methods: fulfillmentMethods, onSelect: handleAddToCartWithMethod, onClose: handleMethodClose }), /*#__PURE__*/_jsx(SurroundPortals, { portalName: FAVORITES_LIST_ADD_BUTTON, children: hasMultipleFavoritesListsSupport ? /*#__PURE__*/_jsx(RippleButton, { type: "primary", className: styles.addButton, onClick: openAddModal, disabled: false, children: i18n.text('favorites.add_list') }) : null })] }); }; FavoriteLists.defaultProps = { lists: [], userSearch: {}, preferredFulfillmentMethod: null, shopFulfillmentMethods: [], preferredLocation: null, isInitializing: true }; export default connect(makeMapStateToProps, mapDispatchToProps)(FavoriteLists);