@salesforce/retail-react-app 1.0.0-preview.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +7 -0
- package/.eslintrc.js +25 -0
- package/.prettierignore +4 -0
- package/.prettierrc.yaml +7 -0
- package/CHANGELOG.md +173 -0
- package/LICENSE +14 -0
- package/README.md +48 -0
- package/app/assets/svg/account.svg +3 -0
- package/app/assets/svg/alert.svg +3 -0
- package/app/assets/svg/basket.svg +3 -0
- package/app/assets/svg/brand-logo.svg +10 -0
- package/app/assets/svg/cc-amex.svg +7 -0
- package/app/assets/svg/cc-cvv.svg +8 -0
- package/app/assets/svg/cc-discover.svg +14 -0
- package/app/assets/svg/cc-mastercard.svg +8 -0
- package/app/assets/svg/cc-visa.svg +11 -0
- package/app/assets/svg/check-circle.svg +3 -0
- package/app/assets/svg/check.svg +3 -0
- package/app/assets/svg/chevron-down.svg +3 -0
- package/app/assets/svg/chevron-left.svg +3 -0
- package/app/assets/svg/chevron-right.svg +3 -0
- package/app/assets/svg/chevron-up.svg +3 -0
- package/app/assets/svg/close.svg +3 -0
- package/app/assets/svg/dashboard.svg +4 -0
- package/app/assets/svg/figma-logo.svg +14 -0
- package/app/assets/svg/file.svg +3 -0
- package/app/assets/svg/filter.svg +3 -0
- package/app/assets/svg/flag-ca.svg +5 -0
- package/app/assets/svg/flag-cn.svg +19 -0
- package/app/assets/svg/flag-fr.svg +19 -0
- package/app/assets/svg/flag-gb.svg +16 -0
- package/app/assets/svg/flag-it.svg +29 -0
- package/app/assets/svg/flag-jp.svg +10 -0
- package/app/assets/svg/flag-us.svg +7 -0
- package/app/assets/svg/github-logo.svg +40 -0
- package/app/assets/svg/hamburger.svg +8 -0
- package/app/assets/svg/heart-solid.svg +7 -0
- package/app/assets/svg/heart.svg +3 -0
- package/app/assets/svg/info.svg +3 -0
- package/app/assets/svg/like.svg +4 -0
- package/app/assets/svg/location.svg +3 -0
- package/app/assets/svg/lock.svg +3 -0
- package/app/assets/svg/paypal.svg +19 -0
- package/app/assets/svg/plug.svg +3 -0
- package/app/assets/svg/plus.svg +3 -0
- package/app/assets/svg/receipt.svg +3 -0
- package/app/assets/svg/search.svg +8 -0
- package/app/assets/svg/signout.svg +3 -0
- package/app/assets/svg/social-facebook.svg +3 -0
- package/app/assets/svg/social-instagram.svg +3 -0
- package/app/assets/svg/social-pinterest.svg +4 -0
- package/app/assets/svg/social-twitter.svg +3 -0
- package/app/assets/svg/social-youtube.svg +3 -0
- package/app/assets/svg/user.svg +3 -0
- package/app/assets/svg/visibility-off.svg +5 -0
- package/app/assets/svg/visibility.svg +3 -0
- package/app/components/_app/index.jsx +401 -0
- package/app/components/_app/index.test.js +85 -0
- package/app/components/_app/partials/above-header.jsx +10 -0
- package/app/components/_app-config/index.jsx +125 -0
- package/app/components/_app-config/index.test.js +77 -0
- package/app/components/_error/index.jsx +142 -0
- package/app/components/_error/index.test.js +25 -0
- package/app/components/action-card/index.jsx +75 -0
- package/app/components/address-display/index.jsx +30 -0
- package/app/components/basic-tile/index.jsx +65 -0
- package/app/components/basic-tile/index.test.js +23 -0
- package/app/components/breadcrumb/index.jsx +67 -0
- package/app/components/breadcrumb/index.test.js +30 -0
- package/app/components/confirmation-modal/index.jsx +111 -0
- package/app/components/confirmation-modal/index.test.js +98 -0
- package/app/components/drawer-menu/index.jsx +405 -0
- package/app/components/drawer-menu/index.test.js +33 -0
- package/app/components/dynamic-image/index.jsx +56 -0
- package/app/components/field/index.jsx +161 -0
- package/app/components/footer/index.jsx +269 -0
- package/app/components/footer/index.test.js +22 -0
- package/app/components/forms/address-fields.jsx +49 -0
- package/app/components/forms/credit-card-fields.jsx +149 -0
- package/app/components/forms/form-action-buttons.jsx +55 -0
- package/app/components/forms/login-fields.jsx +31 -0
- package/app/components/forms/password-requirements.jsx +99 -0
- package/app/components/forms/post-checkout-registration-fields.jsx +43 -0
- package/app/components/forms/profile-fields.jsx +36 -0
- package/app/components/forms/promo-code-fields.jsx +43 -0
- package/app/components/forms/registration-fields.jsx +42 -0
- package/app/components/forms/reset-password-fields.jsx +31 -0
- package/app/components/forms/state-province-options.jsx +75 -0
- package/app/components/forms/update-password-fields.jsx +49 -0
- package/app/components/forms/useAddressFields.jsx +196 -0
- package/app/components/forms/useCreditCardFields.jsx +146 -0
- package/app/components/forms/useLoginFields.jsx +52 -0
- package/app/components/forms/useProfileFields.jsx +95 -0
- package/app/components/forms/usePromoCodeFields.jsx +39 -0
- package/app/components/forms/useRegistrationFields.jsx +136 -0
- package/app/components/forms/useResetPasswordFields.jsx +40 -0
- package/app/components/forms/useUpdatePasswordFields.jsx +89 -0
- package/app/components/header/index.jsx +290 -0
- package/app/components/header/index.test.js +217 -0
- package/app/components/hero/index.jsx +84 -0
- package/app/components/hero/index.test.js +40 -0
- package/app/components/icons/index.jsx +158 -0
- package/app/components/icons/index.test.js +20 -0
- package/app/components/image-gallery/index.jsx +176 -0
- package/app/components/image-gallery/index.test.js +485 -0
- package/app/components/item-variant/index.jsx +33 -0
- package/app/components/item-variant/item-attributes.jsx +107 -0
- package/app/components/item-variant/item-image.jsx +73 -0
- package/app/components/item-variant/item-name.jsx +28 -0
- package/app/components/item-variant/item-price.jsx +117 -0
- package/app/components/link/index.jsx +32 -0
- package/app/components/link/index.test.js +72 -0
- package/app/components/links-list/index.jsx +89 -0
- package/app/components/links-list/index.test.js +62 -0
- package/app/components/list-menu/index.jsx +280 -0
- package/app/components/list-menu/index.test.js +44 -0
- package/app/components/loading-spinner/index.jsx +46 -0
- package/app/components/locale-selector/index.jsx +124 -0
- package/app/components/locale-selector/index.test.js +37 -0
- package/app/components/locale-text/index.jsx +97 -0
- package/app/components/locale-text/index.test.js +36 -0
- package/app/components/login/index.jsx +96 -0
- package/app/components/nested-accordion/index.jsx +185 -0
- package/app/components/nested-accordion/index.test.js +98 -0
- package/app/components/offline-banner/index.jsx +40 -0
- package/app/components/offline-banner/index.test.js +15 -0
- package/app/components/offline-boundary/index.jsx +104 -0
- package/app/components/offline-boundary/index.test.js +123 -0
- package/app/components/order-summary/index.jsx +331 -0
- package/app/components/page-action-placeholder/index.jsx +50 -0
- package/app/components/pagination/index.jsx +134 -0
- package/app/components/pagination/index.test.js +25 -0
- package/app/components/product-item/index.jsx +146 -0
- package/app/components/product-item/index.test.js +38 -0
- package/app/components/product-scroller/index.jsx +172 -0
- package/app/components/product-scroller/index.test.js +98 -0
- package/app/components/product-tile/index.jsx +195 -0
- package/app/components/product-tile/index.test.js +96 -0
- package/app/components/product-view/index.jsx +538 -0
- package/app/components/product-view/index.test.js +224 -0
- package/app/components/product-view-modal/index.jsx +48 -0
- package/app/components/product-view-modal/index.test.js +72 -0
- package/app/components/promo-code/index.jsx +162 -0
- package/app/components/promo-popover/index.jsx +83 -0
- package/app/components/quantity-picker/index.jsx +58 -0
- package/app/components/radio-card/index.jsx +75 -0
- package/app/components/recommended-products/index.jsx +227 -0
- package/app/components/register/index.jsx +114 -0
- package/app/components/reset-password/index.jsx +87 -0
- package/app/components/responsive/index.jsx +29 -0
- package/app/components/scroll-to-top/index.jsx +24 -0
- package/app/components/scroll-to-top/index.test.js +46 -0
- package/app/components/search/index.jsx +279 -0
- package/app/components/search/index.test.js +127 -0
- package/app/components/search/partials/recent-searches.jsx +76 -0
- package/app/components/search/partials/search-suggestions.jsx +45 -0
- package/app/components/search/partials/suggestions.jsx +43 -0
- package/app/components/section/index.jsx +68 -0
- package/app/components/seo/index.jsx +33 -0
- package/app/components/social-icons/index.jsx +101 -0
- package/app/components/social-icons/index.test.js +30 -0
- package/app/components/swatch-group/index.jsx +77 -0
- package/app/components/swatch-group/index.test.js +136 -0
- package/app/components/swatch-group/swatch.jsx +94 -0
- package/app/components/toggle-card/index.jsx +97 -0
- package/app/components/with-registration/index.jsx +58 -0
- package/app/components/with-registration/index.test.js +85 -0
- package/app/constants.js +109 -0
- package/app/contexts/index.js +92 -0
- package/app/hooks/einstein-mock-data.js +916 -0
- package/app/hooks/index.js +17 -0
- package/app/hooks/use-add-to-cart-modal.js +344 -0
- package/app/hooks/use-add-to-cart-modal.test.js +625 -0
- package/app/hooks/use-auth-modal.js +337 -0
- package/app/hooks/use-auth-modal.test.js +365 -0
- package/app/hooks/use-currency.js +20 -0
- package/app/hooks/use-currency.test.js +41 -0
- package/app/hooks/use-current-basket.js +39 -0
- package/app/hooks/use-current-customer.js +29 -0
- package/app/hooks/use-derived-product.js +77 -0
- package/app/hooks/use-derived-product.test.js +69 -0
- package/app/hooks/use-einstein.js +512 -0
- package/app/hooks/use-einstein.test.js +224 -0
- package/app/hooks/use-intersection-observer.js +64 -0
- package/app/hooks/use-limit-urls.js +31 -0
- package/app/hooks/use-limit-urls.test.js +40 -0
- package/app/hooks/use-multi-site.js +36 -0
- package/app/hooks/use-multi-site.test.js +53 -0
- package/app/hooks/use-navigation.js +37 -0
- package/app/hooks/use-navigation.test.js +109 -0
- package/app/hooks/use-page-urls.js +35 -0
- package/app/hooks/use-page-urls.test.js +39 -0
- package/app/hooks/use-pdp-search-params.js +16 -0
- package/app/hooks/use-pdp-search-params.test.js +52 -0
- package/app/hooks/use-previous.js +17 -0
- package/app/hooks/use-product-view-modal.js +93 -0
- package/app/hooks/use-product-view-modal.test.js +172 -0
- package/app/hooks/use-search-params.js +96 -0
- package/app/hooks/use-search-params.test.js +91 -0
- package/app/hooks/use-sort-urls.js +33 -0
- package/app/hooks/use-sort-urls.test.js +42 -0
- package/app/hooks/use-toast.js +68 -0
- package/app/hooks/use-toast.test.js +58 -0
- package/app/hooks/use-variant.js +32 -0
- package/app/hooks/use-variant.test.js +81 -0
- package/app/hooks/use-variation-attributes.js +138 -0
- package/app/hooks/use-variation-attributes.test.js +119 -0
- package/app/hooks/use-variation-params.js +31 -0
- package/app/hooks/use-variation-params.test.js +73 -0
- package/app/hooks/use-wish-list.js +42 -0
- package/app/main.jsx +14 -0
- package/app/mocks/basket-with-suit.js +146 -0
- package/app/mocks/empty-basket.js +39 -0
- package/app/mocks/mock-data.js +5632 -0
- package/app/mocks/product-set-winter-lookM.js +1224 -0
- package/app/mocks/searchResults.js +144 -0
- package/app/mocks/variant-750518699578M.js +434 -0
- package/app/page-designer/README.md +102 -0
- package/app/page-designer/assets/image-tile/index.jsx +51 -0
- package/app/page-designer/assets/image-tile/index.test.js +30 -0
- package/app/page-designer/assets/image-with-text/index.jsx +140 -0
- package/app/page-designer/assets/image-with-text/index.test.js +38 -0
- package/app/page-designer/assets/index.js +9 -0
- package/app/page-designer/index.js +10 -0
- package/app/page-designer/layouts/carousel/index.jsx +222 -0
- package/app/page-designer/layouts/carousel/index.test.js +43 -0
- package/app/page-designer/layouts/index.js +14 -0
- package/app/page-designer/layouts/mobileGrid1r1c/index.jsx +36 -0
- package/app/page-designer/layouts/mobileGrid1r1c/index.test.js +35 -0
- package/app/page-designer/layouts/mobileGrid2r1c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid2r1c/index.test.js +47 -0
- package/app/page-designer/layouts/mobileGrid2r2c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid2r2c/index.test.js +71 -0
- package/app/page-designer/layouts/mobileGrid2r3c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid2r3c/index.test.js +95 -0
- package/app/page-designer/layouts/mobileGrid3r1c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid3r1c/index.test.js +59 -0
- package/app/page-designer/layouts/mobileGrid3r2c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid3r2c/index.test.js +95 -0
- package/app/page-designer/utils.js +14 -0
- package/app/pages/account/addresses.jsx +382 -0
- package/app/pages/account/addresses.test.js +120 -0
- package/app/pages/account/constant.js +57 -0
- package/app/pages/account/index.jsx +237 -0
- package/app/pages/account/index.test.js +188 -0
- package/app/pages/account/order-detail.jsx +397 -0
- package/app/pages/account/order-history.jsx +264 -0
- package/app/pages/account/orders.jsx +30 -0
- package/app/pages/account/orders.test.js +95 -0
- package/app/pages/account/profile.jsx +357 -0
- package/app/pages/account/wishlist/index.jsx +195 -0
- package/app/pages/account/wishlist/index.mock.js +1481 -0
- package/app/pages/account/wishlist/index.test.js +56 -0
- package/app/pages/account/wishlist/partials/wishlist-primary-action.jsx +170 -0
- package/app/pages/account/wishlist/partials/wishlist-primary-action.mock.js +1623 -0
- package/app/pages/account/wishlist/partials/wishlist-primary-action.test.js +99 -0
- package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx +120 -0
- package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js +391 -0
- package/app/pages/cart/index.jsx +476 -0
- package/app/pages/cart/index.test.js +481 -0
- package/app/pages/cart/partials/cart-cta.jsx +46 -0
- package/app/pages/cart/partials/cart-secondary-button-group.jsx +135 -0
- package/app/pages/cart/partials/cart-secondary-button-group.test.js +103 -0
- package/app/pages/cart/partials/cart-skeleton.jsx +93 -0
- package/app/pages/cart/partials/cart-title.jsx +27 -0
- package/app/pages/cart/partials/empty-cart.jsx +86 -0
- package/app/pages/checkout/confirmation.jsx +541 -0
- package/app/pages/checkout/confirmation.mock.js +450 -0
- package/app/pages/checkout/confirmation.test.js +114 -0
- package/app/pages/checkout/index.jsx +169 -0
- package/app/pages/checkout/index.test.js +582 -0
- package/app/pages/checkout/partials/cc-radio-group.jsx +122 -0
- package/app/pages/checkout/partials/checkout-footer.jsx +140 -0
- package/app/pages/checkout/partials/checkout-footer.test.js +16 -0
- package/app/pages/checkout/partials/checkout-header.jsx +54 -0
- package/app/pages/checkout/partials/checkout-header.test.js +16 -0
- package/app/pages/checkout/partials/checkout-skeleton.jsx +52 -0
- package/app/pages/checkout/partials/contact-info.jsx +251 -0
- package/app/pages/checkout/partials/contact-info.test.js +43 -0
- package/app/pages/checkout/partials/payment-form.jsx +97 -0
- package/app/pages/checkout/partials/payment.jsx +276 -0
- package/app/pages/checkout/partials/shipping-address-selection.jsx +377 -0
- package/app/pages/checkout/partials/shipping-address.jsx +132 -0
- package/app/pages/checkout/partials/shipping-options.jsx +232 -0
- package/app/pages/checkout/util/checkout-context.js +94 -0
- package/app/pages/home/data.js +134 -0
- package/app/pages/home/index.jsx +301 -0
- package/app/pages/home/index.test.js +23 -0
- package/app/pages/login/index.jsx +123 -0
- package/app/pages/login/index.test.js +229 -0
- package/app/pages/login-redirect/index.jsx +23 -0
- package/app/pages/login-redirect/index.test.js +16 -0
- package/app/pages/page-not-found/index.jsx +90 -0
- package/app/pages/page-not-found/index.test.js +31 -0
- package/app/pages/product-detail/index.jsx +394 -0
- package/app/pages/product-detail/index.mock.js +197 -0
- package/app/pages/product-detail/index.test.js +162 -0
- package/app/pages/product-detail/partials/information-accordion.jsx +121 -0
- package/app/pages/product-list/index.jsx +735 -0
- package/app/pages/product-list/index.test.js +180 -0
- package/app/pages/product-list/partials/above-page-header.jsx +10 -0
- package/app/pages/product-list/partials/checkbox-refinements.jsx +41 -0
- package/app/pages/product-list/partials/checkbox-refinements.test.js +53 -0
- package/app/pages/product-list/partials/color-refinements.jsx +88 -0
- package/app/pages/product-list/partials/empty-results.jsx +118 -0
- package/app/pages/product-list/partials/link-refinements.jsx +38 -0
- package/app/pages/product-list/partials/page-header.jsx +42 -0
- package/app/pages/product-list/partials/radio-refinements.jsx +60 -0
- package/app/pages/product-list/partials/refinements.jsx +144 -0
- package/app/pages/product-list/partials/selected-refinements.jsx +100 -0
- package/app/pages/product-list/partials/size-refinements.jsx +55 -0
- package/app/pages/registration/index.jsx +87 -0
- package/app/pages/registration/index.test.jsx +132 -0
- package/app/pages/reset-password/index.jsx +112 -0
- package/app/pages/reset-password/index.test.jsx +141 -0
- package/app/request-processor.js +118 -0
- package/app/request-processor.test.js +23 -0
- package/app/routes.jsx +111 -0
- package/app/routes.test.js +13 -0
- package/app/ssr.js +70 -0
- package/app/static/ico/favicon.ico +0 -0
- package/app/static/img/global/app-icon-192.png +0 -0
- package/app/static/img/global/app-icon-512.png +0 -0
- package/app/static/img/global/apple-touch-icon.png +0 -0
- package/app/static/img/hero.png +0 -0
- package/app/static/manifest.json +19 -0
- package/app/static/robots.txt +2 -0
- package/app/theme/components/base/accordion.js +21 -0
- package/app/theme/components/base/alert.js +17 -0
- package/app/theme/components/base/badge.js +25 -0
- package/app/theme/components/base/button.js +77 -0
- package/app/theme/components/base/checkbox.js +30 -0
- package/app/theme/components/base/container.js +17 -0
- package/app/theme/components/base/drawer.js +26 -0
- package/app/theme/components/base/formLabel.js +13 -0
- package/app/theme/components/base/icon.js +13 -0
- package/app/theme/components/base/input.js +44 -0
- package/app/theme/components/base/modal.js +11 -0
- package/app/theme/components/base/popover.js +61 -0
- package/app/theme/components/base/radio.js +33 -0
- package/app/theme/components/base/select.js +15 -0
- package/app/theme/components/base/skeleton.js +12 -0
- package/app/theme/components/base/tooltip.js +19 -0
- package/app/theme/components/project/_app.js +25 -0
- package/app/theme/components/project/breadcrumb.js +25 -0
- package/app/theme/components/project/checkout-footer.js +35 -0
- package/app/theme/components/project/drawer-menu.js +66 -0
- package/app/theme/components/project/footer.js +84 -0
- package/app/theme/components/project/header.js +84 -0
- package/app/theme/components/project/image-gallery.js +59 -0
- package/app/theme/components/project/links-list.js +43 -0
- package/app/theme/components/project/list-menu.js +91 -0
- package/app/theme/components/project/locale-selector.js +42 -0
- package/app/theme/components/project/nested-accordion.js +26 -0
- package/app/theme/components/project/offline-banner.js +25 -0
- package/app/theme/components/project/pagination.js +22 -0
- package/app/theme/components/project/product-tile.js +32 -0
- package/app/theme/components/project/social-icons.js +52 -0
- package/app/theme/components/project/swatch-group.js +115 -0
- package/app/theme/foundations/colors.js +170 -0
- package/app/theme/foundations/gradients.js +9 -0
- package/app/theme/foundations/layerStyles.js +41 -0
- package/app/theme/foundations/shadows.js +9 -0
- package/app/theme/foundations/sizes.js +18 -0
- package/app/theme/foundations/space.js +9 -0
- package/app/theme/foundations/styles.js +21 -0
- package/app/theme/index.js +104 -0
- package/app/utils/cc-utils.js +112 -0
- package/app/utils/cc-utils.test.js +41 -0
- package/app/utils/image-groups-utils.js +62 -0
- package/app/utils/image-groups-utils.test.js +65 -0
- package/app/utils/locale.js +78 -0
- package/app/utils/locale.test.js +112 -0
- package/app/utils/password-utils.js +21 -0
- package/app/utils/phone-utils.js +22 -0
- package/app/utils/phone-utils.test.js +15 -0
- package/app/utils/product-utils.js +35 -0
- package/app/utils/product-utils.test.js +51 -0
- package/app/utils/responsive-image.js +198 -0
- package/app/utils/responsive-image.test.js +170 -0
- package/app/utils/routes-utils.js +111 -0
- package/app/utils/routes-utils.test.js +291 -0
- package/app/utils/site-utils.js +222 -0
- package/app/utils/site-utils.test.js +376 -0
- package/app/utils/test-utils.js +257 -0
- package/app/utils/url.js +291 -0
- package/app/utils/url.test.js +421 -0
- package/app/utils/utils.js +201 -0
- package/app/utils/utils.test.js +182 -0
- package/babel.config.js +7 -0
- package/cache-hash-config.json +8 -0
- package/config/default.js +64 -0
- package/config/mocks/default.js +131 -0
- package/config/sites.js +78 -0
- package/jest-setup.js +191 -0
- package/jest.config.js +50 -0
- package/jsconfig.json +13 -0
- package/package.json +105 -0
- package/scripts/extract-default-messages.js +92 -0
- package/tests/lighthouserc.js +37 -0
- package/translations/README.md +127 -0
- package/translations/compiled/de-DE.json +3212 -0
- package/translations/compiled/en-GB.json +3212 -0
- package/translations/compiled/en-US.json +3212 -0
- package/translations/compiled/en-XA.json +6948 -0
- package/translations/compiled/es-MX.json +3216 -0
- package/translations/compiled/fr-FR.json +3216 -0
- package/translations/compiled/it-IT.json +3188 -0
- package/translations/compiled/ja-JP.json +3200 -0
- package/translations/compiled/ko-KR.json +3180 -0
- package/translations/compiled/pt-BR.json +3220 -0
- package/translations/compiled/zh-CN.json +3212 -0
- package/translations/compiled/zh-TW.json +3208 -0
- package/translations/de-DE.json +1417 -0
- package/translations/en-GB.json +1417 -0
- package/translations/en-US.json +1417 -0
- package/translations/es-MX.json +1417 -0
- package/translations/fr-FR.json +1417 -0
- package/translations/it-IT.json +1417 -0
- package/translations/ja-JP.json +1417 -0
- package/translations/ko-KR.json +1417 -0
- package/translations/pt-BR.json +1417 -0
- package/translations/zh-CN.json +1417 -0
- package/translations/zh-TW.json +1417 -0
- package/worker/main.js +36 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import {useMemo, useState} from 'react'
|
|
8
|
+
import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
|
|
9
|
+
import {useCommerceApi, useAccessToken, useUsid, useEncUserId} from '@salesforce/commerce-sdk-react'
|
|
10
|
+
import {keysToCamel} from '@salesforce/retail-react-app/app/utils/utils'
|
|
11
|
+
|
|
12
|
+
export class EinsteinAPI {
|
|
13
|
+
constructor({host, einsteinId, userId, cookieId, siteId, isProduction}) {
|
|
14
|
+
this.userId = userId
|
|
15
|
+
this.cookieId = cookieId
|
|
16
|
+
this.siteId = siteId
|
|
17
|
+
this.isProduction = isProduction
|
|
18
|
+
this.host = host
|
|
19
|
+
this.einsteinId = einsteinId
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Given a POJO append the correct user and cookie identifier values using the current auth state.
|
|
24
|
+
*
|
|
25
|
+
* @param {object} params
|
|
26
|
+
* @returns {object} The decorated body object.
|
|
27
|
+
*/
|
|
28
|
+
_buildBody(params) {
|
|
29
|
+
const instanceType_prd = 'prd'
|
|
30
|
+
const instanceType_sbx = 'sbx'
|
|
31
|
+
|
|
32
|
+
const body = {...params}
|
|
33
|
+
|
|
34
|
+
// If we have an encrypted user id (authenticaed users only) use it as the `userId` otherwise
|
|
35
|
+
// we won't send a `userId` param for guest users.
|
|
36
|
+
if (this.userId) {
|
|
37
|
+
body.userId = this.userId
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Append the `usid` as the `cookieId` value if present. (It should always be present as long
|
|
41
|
+
// as the user is initilized)
|
|
42
|
+
if (this.cookieId) {
|
|
43
|
+
body.cookieId = this.cookieId
|
|
44
|
+
} else {
|
|
45
|
+
console.warn('Missing `cookieId`. For optimal results this value must be defined.')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// The first part of the siteId is the realm
|
|
49
|
+
if (this.siteId) {
|
|
50
|
+
body.realm = this.siteId.split('-')[0]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (this.isProduction) {
|
|
54
|
+
body.instanceType = instanceType_prd
|
|
55
|
+
} else {
|
|
56
|
+
body.instanceType = instanceType_sbx
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return body
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Given a product or item source, returns the product data that Einstein requires
|
|
64
|
+
*/
|
|
65
|
+
_constructEinsteinProduct(product) {
|
|
66
|
+
if (product.type && (product.type.master || product.type.variant)) {
|
|
67
|
+
// handle variants for PDP / viewProduct
|
|
68
|
+
// Assumes product is a Product object from SCAPI Shopper-Products:
|
|
69
|
+
// https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-products?meta=type%3AProduct
|
|
70
|
+
return {
|
|
71
|
+
id: product.master.masterId,
|
|
72
|
+
sku: product.id,
|
|
73
|
+
altId: '',
|
|
74
|
+
altIdType: ''
|
|
75
|
+
}
|
|
76
|
+
} else if (
|
|
77
|
+
product.productType &&
|
|
78
|
+
(product.productType.master ||
|
|
79
|
+
product.productType.variant ||
|
|
80
|
+
product.productType.set ||
|
|
81
|
+
product.productType.bundle ||
|
|
82
|
+
product.productType.variationGroup ||
|
|
83
|
+
product.productType.item)
|
|
84
|
+
) {
|
|
85
|
+
// handle variants & sets for PLP / viewCategory & viewSearch
|
|
86
|
+
// Assumes product is a ProductSearchHit from SCAPI Shopper-Search:
|
|
87
|
+
// https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-search?meta=type%3AProductSearchHit
|
|
88
|
+
return {
|
|
89
|
+
id: product.productId,
|
|
90
|
+
sku: product.productId, //TODO: Should we switch this to product.representedProduct.id once we allow non-master products in search results?
|
|
91
|
+
altId: '',
|
|
92
|
+
altIdType: ''
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
// handles non-variants
|
|
96
|
+
return {
|
|
97
|
+
id: product.id,
|
|
98
|
+
sku: '',
|
|
99
|
+
altId: '',
|
|
100
|
+
altIdType: ''
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Given a cart item, returns the data that Einstein requires
|
|
107
|
+
*
|
|
108
|
+
* Assumes item is a ProductItemfrom SCAPI Shopper-Baskets:
|
|
109
|
+
* https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=type%3AProductItem
|
|
110
|
+
*/
|
|
111
|
+
_constructEinsteinItem(item) {
|
|
112
|
+
return {
|
|
113
|
+
id: item.productId,
|
|
114
|
+
sku: '',
|
|
115
|
+
price: item.price,
|
|
116
|
+
quantity: item.quantity
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async einsteinFetch(endpoint, method, body) {
|
|
121
|
+
const headers = {
|
|
122
|
+
'Content-Type': 'application/json',
|
|
123
|
+
'x-cq-client-id': this.einsteinId
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Include `userId` and `cookieId` parameters.
|
|
127
|
+
if (body) {
|
|
128
|
+
body = this._buildBody(body)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let response
|
|
132
|
+
try {
|
|
133
|
+
response = await fetch(`${this.host}/v3${endpoint}`, {
|
|
134
|
+
method: method,
|
|
135
|
+
headers: headers,
|
|
136
|
+
...(body && {
|
|
137
|
+
body: JSON.stringify(body)
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
} catch {
|
|
141
|
+
console.warn('Einstein request failed')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!response?.ok) {
|
|
145
|
+
return {}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const responseJson = await response.json()
|
|
149
|
+
|
|
150
|
+
return keysToCamel(responseJson)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Tells the Einstein engine when a user views a product.
|
|
155
|
+
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
|
|
156
|
+
**/
|
|
157
|
+
async sendViewProduct(product, args) {
|
|
158
|
+
const endpoint = `/activities/${this.siteId}/viewProduct`
|
|
159
|
+
const method = 'POST'
|
|
160
|
+
const body = {
|
|
161
|
+
product: this._constructEinsteinProduct(product),
|
|
162
|
+
...args
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Tells the Einstein engine when a user views search results.
|
|
170
|
+
**/
|
|
171
|
+
async sendViewSearch(searchText, searchResults, args) {
|
|
172
|
+
const endpoint = `/activities/${this.siteId}/viewSearch`
|
|
173
|
+
const method = 'POST'
|
|
174
|
+
|
|
175
|
+
const products = searchResults?.hits?.map((product) =>
|
|
176
|
+
this._constructEinsteinProduct(product)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
const body = {
|
|
180
|
+
searchText,
|
|
181
|
+
products,
|
|
182
|
+
showProducts: true, // Needed by Reports and Dashboards to differentiate searches with results vs no results
|
|
183
|
+
...args
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Tells the Einstein engine when a user clicks on a search result.
|
|
191
|
+
**/
|
|
192
|
+
async sendClickSearch(searchText, product, args) {
|
|
193
|
+
const endpoint = `/activities/${this.siteId}/clickSearch`
|
|
194
|
+
const method = 'POST'
|
|
195
|
+
const body = {
|
|
196
|
+
searchText,
|
|
197
|
+
product: this._constructEinsteinProduct(product),
|
|
198
|
+
...args
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Tells the Einstein engine when a user views a category.
|
|
206
|
+
**/
|
|
207
|
+
async sendViewCategory(category, searchResults, args) {
|
|
208
|
+
const endpoint = `/activities/${this.siteId}/viewCategory`
|
|
209
|
+
const method = 'POST'
|
|
210
|
+
|
|
211
|
+
const products = searchResults?.hits?.map((product) =>
|
|
212
|
+
this._constructEinsteinProduct(product)
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
const body = {
|
|
216
|
+
category: {
|
|
217
|
+
id: category.id
|
|
218
|
+
},
|
|
219
|
+
products,
|
|
220
|
+
showProducts: true, // Needed by Reports and Dashboards to differentiate searches with results vs no results
|
|
221
|
+
...args
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Tells the Einstein engine when a user clicks a product from the category page.
|
|
229
|
+
* Not meant to be used when the user clicks a category from the nav bar.
|
|
230
|
+
**/
|
|
231
|
+
async sendClickCategory(category, product, args) {
|
|
232
|
+
const endpoint = `/activities/${this.siteId}/clickCategory`
|
|
233
|
+
const method = 'POST'
|
|
234
|
+
const body = {
|
|
235
|
+
category: {
|
|
236
|
+
id: category.id
|
|
237
|
+
},
|
|
238
|
+
product: this._constructEinsteinProduct(product),
|
|
239
|
+
...args
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Tells the Einstein engine when a user views a set of recommendations
|
|
247
|
+
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
|
|
248
|
+
**/
|
|
249
|
+
async sendViewReco(recommenderDetails, products, args) {
|
|
250
|
+
const endpoint = `/activities/${this.siteId}/viewReco`
|
|
251
|
+
const method = 'POST'
|
|
252
|
+
const {__recoUUID, recommenderName} = recommenderDetails
|
|
253
|
+
const body = {
|
|
254
|
+
recommenderName,
|
|
255
|
+
__recoUUID,
|
|
256
|
+
products: products,
|
|
257
|
+
...args
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Tells the Einstein engine when a user clicks on a recommendation
|
|
265
|
+
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
|
|
266
|
+
**/
|
|
267
|
+
async sendClickReco(recommenderDetails, product, args) {
|
|
268
|
+
const endpoint = `/activities/${this.siteId}/clickReco`
|
|
269
|
+
const method = 'POST'
|
|
270
|
+
const {__recoUUID, recommenderName} = recommenderDetails
|
|
271
|
+
const body = {
|
|
272
|
+
recommenderName,
|
|
273
|
+
__recoUUID,
|
|
274
|
+
product: this._constructEinsteinProduct(product),
|
|
275
|
+
...args
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Tells the Einstein engine when a user views a page.
|
|
283
|
+
* Use this only for pages where another activity does not fit. (ie. on the PDP, use viewProduct rather than this)
|
|
284
|
+
**/
|
|
285
|
+
async sendViewPage(path, args) {
|
|
286
|
+
const endpoint = `/activities/${this.siteId}/viewPage`
|
|
287
|
+
const method = 'POST'
|
|
288
|
+
const body = {
|
|
289
|
+
currentLocation: path,
|
|
290
|
+
...args
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Tells the Einstein engine when a user starts the checkout process.
|
|
298
|
+
**/
|
|
299
|
+
async sendBeginCheckout(basket, args) {
|
|
300
|
+
const endpoint = `/activities/${this.siteId}/beginCheckout`
|
|
301
|
+
const method = 'POST'
|
|
302
|
+
const products = basket.productItems.map((item) => this._constructEinsteinItem(item))
|
|
303
|
+
const subTotal = basket.productSubTotal
|
|
304
|
+
const body = {
|
|
305
|
+
products: products,
|
|
306
|
+
amount: subTotal,
|
|
307
|
+
...args
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Tells the Einstein engine when a user reaches the given step during checkout.
|
|
315
|
+
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
|
|
316
|
+
**/
|
|
317
|
+
async sendCheckoutStep(stepName, stepNumber, basket, args) {
|
|
318
|
+
const endpoint = `/activities/${this.siteId}/checkoutStep`
|
|
319
|
+
const method = 'POST'
|
|
320
|
+
const body = {
|
|
321
|
+
stepName,
|
|
322
|
+
stepNumber,
|
|
323
|
+
basketId: basket.basketId,
|
|
324
|
+
...args
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Tells the Einstein engine when a user adds an item to their cart.
|
|
332
|
+
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
|
|
333
|
+
**/
|
|
334
|
+
async sendAddToCart(items, args) {
|
|
335
|
+
const endpoint = `/activities/${this.siteId}/addToCart`
|
|
336
|
+
const method = 'POST'
|
|
337
|
+
const body = {
|
|
338
|
+
products: items.map((item) => this._constructEinsteinItem(item)),
|
|
339
|
+
...args
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Get a list of recommenders that can be used in recommendation requests.
|
|
347
|
+
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
|
|
348
|
+
**/
|
|
349
|
+
async getRecommenders() {
|
|
350
|
+
const endpoint = `/personalization/recommenders/${this.siteId}`
|
|
351
|
+
const method = 'GET'
|
|
352
|
+
const body = null
|
|
353
|
+
|
|
354
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Get a set of recommendations
|
|
359
|
+
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
|
|
360
|
+
**/
|
|
361
|
+
async getRecommendations(recommenderName, products, args) {
|
|
362
|
+
const endpoint = `/personalization/recs/${this.siteId}/${recommenderName}`
|
|
363
|
+
const method = 'POST'
|
|
364
|
+
const body = {
|
|
365
|
+
products: products?.map((product) => this._constructEinsteinProduct(product)),
|
|
366
|
+
...args
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get a set of recommendations for a zone
|
|
374
|
+
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
|
|
375
|
+
**/
|
|
376
|
+
async getZoneRecommendations(zoneName, products, args) {
|
|
377
|
+
const endpoint = `/personalization/${this.siteId}/zones/${zoneName}/recs`
|
|
378
|
+
const method = 'POST'
|
|
379
|
+
|
|
380
|
+
const body = {
|
|
381
|
+
products: products?.map((product) => this._constructEinsteinProduct(product)),
|
|
382
|
+
...args
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return this.einsteinFetch(endpoint, method, body)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const useEinstein = () => {
|
|
390
|
+
const api = useCommerceApi()
|
|
391
|
+
const {getTokenWhenReady} = useAccessToken()
|
|
392
|
+
const {
|
|
393
|
+
app: {einsteinAPI: config}
|
|
394
|
+
} = getConfig()
|
|
395
|
+
const {host, einsteinId, siteId, isProduction} = config
|
|
396
|
+
const usid = useUsid()
|
|
397
|
+
const encUserId = useEncUserId()
|
|
398
|
+
|
|
399
|
+
const einstein = useMemo(
|
|
400
|
+
() =>
|
|
401
|
+
new EinsteinAPI({
|
|
402
|
+
host,
|
|
403
|
+
einsteinId,
|
|
404
|
+
userId: encUserId,
|
|
405
|
+
cookieId: usid,
|
|
406
|
+
siteId,
|
|
407
|
+
isProduction
|
|
408
|
+
}),
|
|
409
|
+
[host, einsteinId, encUserId, usid, siteId, isProduction]
|
|
410
|
+
)
|
|
411
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
412
|
+
const [recommendations, setRecommendations] = useState([])
|
|
413
|
+
|
|
414
|
+
const fetchRecProductDetails = async (reco) => {
|
|
415
|
+
const ids = reco.recs?.map((rec) => rec.id)
|
|
416
|
+
if (ids?.length > 0) {
|
|
417
|
+
const token = await getTokenWhenReady()
|
|
418
|
+
// Fetch the product details for the recommendations
|
|
419
|
+
const products = await api.shopperProducts.getProducts({
|
|
420
|
+
parameters: {ids: ids.join(',')},
|
|
421
|
+
headers: {
|
|
422
|
+
Authorization: `Bearer ${token}`
|
|
423
|
+
}
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
// Merge the product detail into the recommendations response
|
|
427
|
+
return {
|
|
428
|
+
...reco,
|
|
429
|
+
recs: reco.recs.map((rec) => {
|
|
430
|
+
const product = products?.data?.find((product) => product.id === rec.id)
|
|
431
|
+
return {
|
|
432
|
+
...rec,
|
|
433
|
+
...product,
|
|
434
|
+
productId: rec.id,
|
|
435
|
+
image: {disBaseLink: rec.imageUrl, alt: rec.productName}
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return reco
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
isLoading,
|
|
445
|
+
|
|
446
|
+
recommendations,
|
|
447
|
+
|
|
448
|
+
async sendViewProduct(...args) {
|
|
449
|
+
return einstein.sendViewProduct(...args)
|
|
450
|
+
},
|
|
451
|
+
async sendViewSearch(...args) {
|
|
452
|
+
return einstein.sendViewSearch(...args)
|
|
453
|
+
},
|
|
454
|
+
async sendClickSearch(...args) {
|
|
455
|
+
return einstein.sendClickSearch(...args)
|
|
456
|
+
},
|
|
457
|
+
async sendViewCategory(...args) {
|
|
458
|
+
return einstein.sendViewCategory(...args)
|
|
459
|
+
},
|
|
460
|
+
async sendClickCategory(...args) {
|
|
461
|
+
return einstein.sendClickCategory(...args)
|
|
462
|
+
},
|
|
463
|
+
async sendViewPage(...args) {
|
|
464
|
+
return einstein.sendViewPage(...args)
|
|
465
|
+
},
|
|
466
|
+
async sendBeginCheckout(...args) {
|
|
467
|
+
return einstein.sendBeginCheckout(...args)
|
|
468
|
+
},
|
|
469
|
+
async sendCheckoutStep(...args) {
|
|
470
|
+
return einstein.sendCheckoutStep(...args)
|
|
471
|
+
},
|
|
472
|
+
async sendViewReco(...args) {
|
|
473
|
+
return einstein.sendViewReco(...args)
|
|
474
|
+
},
|
|
475
|
+
async sendClickReco(...args) {
|
|
476
|
+
return einstein.sendClickReco(...args)
|
|
477
|
+
},
|
|
478
|
+
async sendAddToCart(...args) {
|
|
479
|
+
return einstein.sendAddToCart(...args)
|
|
480
|
+
},
|
|
481
|
+
async getRecommenders(...args) {
|
|
482
|
+
return einstein.getRecommenders(...args)
|
|
483
|
+
},
|
|
484
|
+
async getRecommendations(recommenderName, products, args) {
|
|
485
|
+
setIsLoading(true)
|
|
486
|
+
try {
|
|
487
|
+
const reco = await einstein.getRecommendations(recommenderName, products, args)
|
|
488
|
+
reco.recommenderName = recommenderName
|
|
489
|
+
const recommendations = await fetchRecProductDetails(reco)
|
|
490
|
+
setRecommendations(recommendations)
|
|
491
|
+
} catch (err) {
|
|
492
|
+
console.error(err)
|
|
493
|
+
} finally {
|
|
494
|
+
setIsLoading(false)
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
async getZoneRecommendations(zoneName, products, args) {
|
|
498
|
+
setIsLoading(true)
|
|
499
|
+
try {
|
|
500
|
+
const reco = await einstein.getZoneRecommendations(zoneName, products, args)
|
|
501
|
+
const recommendations = await fetchRecProductDetails(reco)
|
|
502
|
+
setRecommendations(recommendations)
|
|
503
|
+
} catch (err) {
|
|
504
|
+
console.error(err)
|
|
505
|
+
} finally {
|
|
506
|
+
setIsLoading(false)
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export default useEinstein
|