UNPKG

@shopgate/engage

Version:
219 lines (213 loc) • 6.55 kB
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import { css } from 'glamor'; import classNames from 'classnames'; import CryptoJs from 'crypto-js'; import sortBy from 'lodash/sortBy'; import uniqBy from 'lodash/uniqBy'; import { themeConfig } from '@shopgate/pwa-common/helpers/config'; import { i18n } from '@shopgate/engage/core'; import { RadioGroupV2 as RadioGroup, RadioCard, MessageBar } from '@shopgate/engage/components'; import { useCheckoutContext } from '@shopgate/engage/checkout/hooks/common'; import ShippingMethod from "./ShippingMethod"; import connect from "./connector"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const { variables } = themeConfig; const styles = { root: css({ padding: `0 ${variables.gap.big}px ${variables.gap.xbig}px` }).toString(), headline: css({ fontSize: '1.25rem', fontWeight: 'normal', padding: `0 ${variables.gap.small}px 0 0`, margin: `0 0 ${variables.gap.small}px 0`, color: 'var(--color-text-high-emphasis)', textTransform: 'none' }).toString(), container: css({ border: '1px solid #eaeaea', ' li:nth-child(2n)': { background: 'var(--color-background-accent)' } }).toString(), containerSingle: css({ padding: variables.gap.small }).toString(), card: css({ display: 'flex', alignItems: 'center' }).toString(), errorMessage: css({ margin: 0 }).toString(), iOSCard: css({ width: '100%', overflow: 'hidden', marginBottom: variables.gap.big }).toString() }; /** * Hashes a shipping method * @param {Object} method A shipping method * @returns {string} */ const hashShippingMethod = method => { if (!method) { return null; } const { code, serviceLevel: { code: serviceLevelCode, carrier: { code: carrierCode } } } = method; return CryptoJs.MD5(`${code} ${serviceLevelCode} ${carrierCode}`).toString(); }; /** * Custom replacement for the wrapper component of the RadioCard * @param {Object} props The component props * @returns {JSX} */ const CardComponent = ({ children }) => /*#__PURE__*/_jsx("li", { className: styles.card, children: children }); CardComponent.defaultProps = { children: null }; /** * The shipping methods component. * @returns {JSX} */ const ShippingMethods = ({ orderHasDirectShipItems }) => { const { shippingAddress, updateShippingMethod, isLoading, order } = useCheckoutContext(); const { selectedShippingMethod = null, availableShippingMethods = [] } = shippingAddress?.orderSegment || {}; const [selectedHash, setSelectedHash] = useState(hashShippingMethod(selectedShippingMethod)); useEffect(() => { // Update the selected hash when the selected shipping method updates setSelectedHash(hashShippingMethod(selectedShippingMethod)); }, [selectedShippingMethod]); /** * The component actually doesn't render shipping methods, but service levels. To improve * data handling, we transform the original data structure to the shape which is required for * the update request. */ const shippingMethods = useMemo(() => { // Flat map all service levels of all shipping methods and aggregate with parent (method) data. const unsortedLevels = availableShippingMethods.flatMap(method => { const { serviceLevels, ...methodData } = method; return serviceLevels.map(serviceLevel => { const entry = { ...methodData, serviceLevel }; const hash = hashShippingMethod(entry); return { ...entry, hash }; }); }); // Remove duplicated shipping levels that originated from different shipping methods. const dedupedLevels = uniqBy(unsortedLevels, ({ serviceLevel }) => `${serviceLevel.code}#${serviceLevel.carrier?.code}`); // Show cheapest service level first followed by alphabetic name. return sortBy(dedupedLevels, ['serviceLevel.cost', 'serviceLevel.name']); }, [availableShippingMethods]); const onChange = useCallback(async (event, value) => { // Determine the selected shipping method by its hash const shippingMethod = shippingMethods.find(({ hash }) => value === hash); if (!shippingMethod) { return; } // Update the local state setSelectedHash(shippingMethod?.hash); // Perform the update request await updateShippingMethod({ code: shippingMethod?.code, serviceLevel: { code: shippingMethod?.serviceLevel?.code, carrier: { code: shippingMethod?.serviceLevel?.carrier?.code } } }); }, [shippingMethods, updateShippingMethod]); if (order?.status !== 'new' || !orderHasDirectShipItems) { return null; } if (shippingMethods.length === 0) { return /*#__PURE__*/_jsxs("div", { className: styles.root, children: [/*#__PURE__*/_jsx("h3", { className: styles.headline, children: i18n.text('checkout.shippingMethod.title') }), /*#__PURE__*/_jsx(MessageBar, { messages: [{ type: 'error', message: i18n.text(`checkout.shippingMethod.errors.${!shippingAddress ? 'noShippingAddress' : 'invalidShippingAddress'}`) }], classNames: { container: styles.errorMessage }, showIcons: true })] }); } return /*#__PURE__*/_jsxs("div", { className: styles.root, children: [/*#__PURE__*/_jsx("h3", { className: styles.headline, children: i18n.text('checkout.shippingMethod.title') }), shippingMethods.length === 1 ? /*#__PURE__*/_jsx("div", { className: classNames(styles.container, styles.containerSingle), children: /*#__PURE__*/_jsx(ShippingMethod, { shippingMethod: shippingMethods[0] }) }) : /*#__PURE__*/_jsx(RadioGroup, { name: "shipping-methods", value: selectedHash, onChange: onChange, component: "ul", classes: { root: styles.container }, disabled: isLoading, children: shippingMethods.map(shippingMethod => /*#__PURE__*/_jsx(RadioCard, { renderCard: CardComponent, value: shippingMethod.hash, children: /*#__PURE__*/_jsx(ShippingMethod, { shippingMethod: shippingMethod }) }, shippingMethod.hash)) })] }); }; ShippingMethods.defaultProps = { orderHasDirectShipItems: false }; export default connect(ShippingMethods);