@evenicanpm/storefront-core 1.0.5 → 1.1.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.
Files changed (307) hide show
  1. package/.storybook/main.ts +37 -0
  2. package/.storybook/preview.ts +14 -0
  3. package/README.md +17 -2
  4. package/__mocks__/next-font.js +4 -0
  5. package/package.json +19 -13
  6. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +265 -5
  7. package/src/api-manager/schemas/product.schema.ts +2 -2
  8. package/src/api-manager/services/cart/mutations/add-discount-code.ts +11 -0
  9. package/src/api-manager/services/cart/mutations/checkout.ts +1 -1
  10. package/src/api-manager/services/create-query.ts +11 -4
  11. package/src/api-manager/services/product/queries/get-product-availability.ts +1 -2
  12. package/src/api-manager/services/user/mutations/add-to-wishlist.ts +2 -2
  13. package/src/api-manager/services/user/mutations/copy-cart-to-wishlist.ts +2 -1
  14. package/src/api-manager/services/user/mutations/remove-from-wishlist.ts +4 -4
  15. package/src/api-manager/services/user/mutations/update-wishlist.ts +2 -1
  16. package/src/api-manager/services/user/queries/get-wishlist-details.ts +2 -2
  17. package/src/cms/blocks/components/product-carousel/index.tsx +6 -3
  18. package/src/cms/blocks/components/product-section-fullwidth/index.tsx +2 -2
  19. package/src/cms/blocks/components/shared/product-grid.tsx +5 -2
  20. package/src/cms/interfaces/navigation.ts +9 -9
  21. package/src/components/Blocks/Components/CategoryCarousel/index.tsx +3 -3
  22. package/src/components/Blocks/Components/HeroCarousel/index.tsx +2 -2
  23. package/src/components/Blocks/Components/ProductCarousel/index.tsx +5 -5
  24. package/src/components/Blocks/Components/ProductSectionFullWidth/index.tsx +6 -6
  25. package/src/components/Blocks/Components/Services/service-card.tsx +4 -1
  26. package/src/components/Blocks/Components/shared/featured-product-card.tsx +2 -2
  27. package/src/components/Blocks/Components/shared/product-category-item.tsx +3 -3
  28. package/src/components/Blocks/Components/shared/product-grid.tsx +2 -2
  29. package/src/components/Blocks/Components/shared/top-categories-card.tsx +1 -1
  30. package/src/components/Blocks/Components/shared/top-rating-product-card.tsx +6 -3
  31. package/src/components/Footer/sections/FooterAppStore.tsx +1 -1
  32. package/src/components/Footer/sections/FooterContact.tsx +1 -1
  33. package/src/components/Footer/sections/FooterLogo.tsx +2 -2
  34. package/src/components/Footer/sections/FooterSocialLinks.tsx +1 -1
  35. package/src/components/_components/MiniCartClient.tsx +2 -2
  36. package/src/components/_components/accordion/accordion-header.tsx +1 -1
  37. package/src/components/_components/carousel-cards/carousel-card-1/carousel-card-1.tsx +2 -2
  38. package/src/components/_components/carousel-cards/carousel-card-2/carousel-card-2.tsx +9 -4
  39. package/src/components/_components/carousel-cards/carousel-card-3/carousel-card-3.tsx +6 -3
  40. package/src/components/_components/carousel-cards/carousel-card-4/carousel-card-4.tsx +6 -1
  41. package/src/components/_components/categories/category-list/category-list.tsx +3 -3
  42. package/src/components/_components/categories/mega-menu/banner.tsx +7 -3
  43. package/src/components/_components/categories/mega-menu/column-list.tsx +3 -3
  44. package/src/components/_components/categories/mega-menu/mega-menu-1.tsx +2 -2
  45. package/src/components/_components/category-cards/category-card-1/category-card-1.tsx +2 -2
  46. package/src/components/_components/countdown/count-box.tsx +4 -1
  47. package/src/components/_components/countdown/count-down.tsx +1 -1
  48. package/src/components/_components/data-table/table-header.tsx +1 -1
  49. package/src/components/_components/footer/components/app-store.tsx +1 -1
  50. package/src/components/_components/footer/components/logo.tsx +2 -2
  51. package/src/components/_components/footer/components/social-links.tsx +1 -1
  52. package/src/components/_components/footer/footer-1.tsx +1 -1
  53. package/src/components/_components/footer/footer-2.tsx +2 -2
  54. package/src/components/_components/footer/footer-3.tsx +2 -2
  55. package/src/components/_components/footer/footer-4.tsx +3 -3
  56. package/src/components/_components/header/components/categories-menu.tsx +3 -3
  57. package/src/components/_components/header/components/mobile-header.tsx +9 -6
  58. package/src/components/_components/header/components/user.tsx +5 -5
  59. package/src/components/_components/header/header.tsx +2 -2
  60. package/src/components/_components/header/sticky-header.tsx +3 -3
  61. package/src/components/_components/mini-cart/components/cart-item.tsx +5 -2
  62. package/src/components/_components/mini-cart/components/empty-view.tsx +2 -2
  63. package/src/components/_components/mini-cart/components/top-header.tsx +5 -2
  64. package/src/components/_components/mini-cart/mini-cart.tsx +3 -3
  65. package/src/components/_components/mobile-navigation/mobile-navigation-bar.tsx +5 -5
  66. package/src/components/_components/nav-link/nav-link-2.tsx +1 -1
  67. package/src/components/_components/nav-link/nav-link-3.tsx +1 -1
  68. package/src/components/_components/navbar/categories.tsx +3 -3
  69. package/src/components/_components/navbar/category-based-menu/category-based-menu.tsx +1 -1
  70. package/src/components/_components/navbar/category-based-menu/components/child-categories.tsx +2 -2
  71. package/src/components/_components/navbar/mega-menu/category-list.tsx +1 -1
  72. package/src/components/_components/navbar/mega-menu/mega-menu.tsx +1 -1
  73. package/src/components/_components/navbar/mobile-menu/mobile-menu.tsx +2 -2
  74. package/src/components/_components/navbar/mobile-menu/render-levels.tsx +3 -3
  75. package/src/components/_components/navbar/nav-list/nav-item-child.tsx +3 -3
  76. package/src/components/_components/navbar/nav-list/nav-list.tsx +4 -4
  77. package/src/components/_components/newsletter/newsletter.tsx +5 -1
  78. package/src/components/_components/newsletter/social-icons.tsx +1 -1
  79. package/src/components/_components/page-sidenav/grocery-side-nav/components/link-item.tsx +2 -2
  80. package/src/components/_components/page-sidenav/grocery-side-nav/components/list-item.tsx +2 -2
  81. package/src/components/_components/page-sidenav/grocery-side-nav/components/nav-accordion.tsx +2 -2
  82. package/src/components/_components/page-sidenav/grocery-side-nav/grocery-side-nav.tsx +3 -3
  83. package/src/components/_components/page-sidenav/health-beauty-side-nav/components/list-item.tsx +2 -2
  84. package/src/components/_components/page-sidenav/health-beauty-side-nav/components/nav-accordion.tsx +2 -2
  85. package/src/components/_components/page-sidenav/health-beauty-side-nav/components/render-child.tsx +2 -2
  86. package/src/components/_components/page-sidenav/health-beauty-side-nav/health-beauty-side-nav.tsx +5 -5
  87. package/src/components/_components/page-sidenav/side-navbar/components/list-item.tsx +1 -1
  88. package/src/components/_components/page-sidenav/side-navbar/components/nav-accordion.tsx +3 -3
  89. package/src/components/_components/page-sidenav/side-navbar/components/render-child.tsx +1 -1
  90. package/src/components/_components/page-sidenav/side-navbar/side-nav-bar.tsx +4 -4
  91. package/src/components/_components/page-sidenav/side-navbar-2/components/sidebar-accordion.tsx +1 -1
  92. package/src/components/_components/page-sidenav/side-navbar-2/side-navbar-2.tsx +2 -2
  93. package/src/components/_components/product-cards/product-card-1/components/quantity-buttons.tsx +2 -2
  94. package/src/components/_components/product-cards/product-card-1/product-card.tsx +2 -2
  95. package/src/components/_components/product-cards/product-card-10/product-card.tsx +9 -5
  96. package/src/components/_components/product-cards/product-card-11/product-card.tsx +6 -3
  97. package/src/components/_components/product-cards/product-card-12/product-card.tsx +4 -4
  98. package/src/components/_components/product-cards/product-card-13/product-card.tsx +6 -3
  99. package/src/components/_components/product-cards/product-card-14/product-card.tsx +7 -4
  100. package/src/components/_components/product-cards/product-card-15/product-card.tsx +6 -3
  101. package/src/components/_components/product-cards/product-card-16/components/quantity-buttons.tsx +2 -2
  102. package/src/components/_components/product-cards/product-card-16/product-card.tsx +5 -2
  103. package/src/components/_components/product-cards/product-card-2/components/product-price.tsx +2 -2
  104. package/src/components/_components/product-cards/product-card-2/product-card.tsx +4 -4
  105. package/src/components/_components/product-cards/product-card-3/components/hover-actions.tsx +1 -1
  106. package/src/components/_components/product-cards/product-card-3/product-card.tsx +2 -2
  107. package/src/components/_components/product-cards/product-card-4/components/hover-actions.tsx +1 -1
  108. package/src/components/_components/product-cards/product-card-4/product-card.tsx +2 -2
  109. package/src/components/_components/product-cards/product-card-5/components/hover-actions.tsx +1 -1
  110. package/src/components/_components/product-cards/product-card-5/product-card.tsx +2 -2
  111. package/src/components/_components/product-cards/product-card-6/components/hover-actions.tsx +1 -1
  112. package/src/components/_components/product-cards/product-card-6/product-card.tsx +3 -3
  113. package/src/components/_components/product-cards/product-card-7/components/quantity-buttons.tsx +2 -2
  114. package/src/components/_components/product-cards/product-card-7/product-card.tsx +5 -2
  115. package/src/components/_components/product-cards/product-card-8/product-card.tsx +9 -5
  116. package/src/components/_components/product-cards/product-card-9/components/add-to-cart.tsx +5 -2
  117. package/src/components/_components/product-cards/product-card-9/product-card.tsx +1 -1
  118. package/src/components/_components/product-cards/product-price.tsx +2 -2
  119. package/src/components/_components/product-cards/product-rating.tsx +2 -2
  120. package/src/components/_components/product-cards/product-title.tsx +1 -1
  121. package/src/components/_components/product-cards/use-product.ts +1 -1
  122. package/src/components/_components/product-dimensions/product-dimensions.tsx +1 -1
  123. package/src/components/_components/product-quantity-buttons/product-quantity-buttons.tsx +2 -2
  124. package/src/components/_components/products-view/products-grid-view.tsx +2 -2
  125. package/src/components/_components/products-view/products-list-view.tsx +2 -2
  126. package/src/components/_components/search-box/components/category-dropdown.tsx +1 -1
  127. package/src/components/_components/search-box/components/search-result.tsx +1 -1
  128. package/src/components/_components/search-box/hooks/use-search.ts +1 -1
  129. package/src/components/_components/search-box/search-input-category.tsx +3 -3
  130. package/src/components/_components/service-cards/service-card-1/service-card-1.tsx +5 -2
  131. package/src/components/_components/service-cards/service-card-2/service-card-2.tsx +5 -2
  132. package/src/components/_components/service-cards/service-card-3/service-card-3.tsx +4 -1
  133. package/src/components/_components/settings/setting.tsx +3 -3
  134. package/src/components/_components/side-nav/side-nav.tsx +1 -1
  135. package/src/components/_components/stories/story-content/story-content.tsx +1 -1
  136. package/src/components/_components/stories/story-item/story-item.tsx +1 -1
  137. package/src/components/_components/topbar/top-bar.tsx +6 -3
  138. package/src/components/_components/wishlist-modal/wishlist-modal-item.tsx +1 -1
  139. package/src/components-v2/accordion/accordion-header.stories.tsx +28 -0
  140. package/src/components-v2/categories/category-list/category-list.tsx +2 -2
  141. package/src/components-v2/categories/category-list/styles.ts +3 -2
  142. package/src/components-v2/categories/mega-menu/column-list.tsx +1 -1
  143. package/src/components-v2/categories/mega-menu/mega-menu-1.tsx +6 -3
  144. package/src/components-v2/categories/mega-menu/mega-menu-2.tsx +2 -2
  145. package/src/components-v2/header/__tests__/user.test.tsx +12 -12
  146. package/src/components-v2/header/components/user.tsx +12 -11
  147. package/src/components-v2/mini-cart/mini-cart.tsx +9 -14
  148. package/src/components-v2/mobile-navigation/mobile-navigation-bar.tsx +1 -1
  149. package/src/components-v2/navbar/mobile-menu/mobile-menu.tsx +2 -2
  150. package/src/components-v2/product-cards/favorite-button.tsx +52 -0
  151. package/src/components-v2/product-cards/product-card/product-card.tsx +87 -47
  152. package/src/components-v2/product-cards/product-card-plp/product-card.tsx +140 -51
  153. package/src/components-v2/product-cards/product-card-plp/styles/index.ts +17 -0
  154. package/src/components-v2/product-cards/product-card-plp-list/product-card.tsx +76 -42
  155. package/src/components-v2/product-cards/product-card-search/product-card.tsx +50 -21
  156. package/src/components-v2/product-cards/product-card-search/styles/index.ts +0 -10
  157. package/src/components-v2/product-dialog/compound/product-dialog.tsx +39 -15
  158. package/src/components-v2/product-quantity-buttons/compound/quantity-buttons-root.tsx +22 -7
  159. package/src/components-v2/product-quantity-variants/index.tsx +1 -1
  160. package/src/components-v2/products-view/compound/context.ts +46 -1
  161. package/src/components-v2/products-view/compound/products-grid-view.tsx +23 -14
  162. package/src/components-v2/products-view/compound/products-list-view.tsx +12 -3
  163. package/src/components-v2/search-bar/compound/context.ts +24 -3
  164. package/src/components-v2/search-bar/compound/results-item-lg.tsx +14 -13
  165. package/src/components-v2/search-bar/compound/results-item-sm.tsx +5 -5
  166. package/src/components-v2/search-bar/compound/results-suspense.tsx +3 -3
  167. package/src/components-v2/search-bar/compound/results.tsx +4 -4
  168. package/src/components-v2/search-bar/compound/textfield-adornment-category.tsx +1 -1
  169. package/src/components-v2/search-bar/compound/textfield.tsx +3 -3
  170. package/src/components-v2/search-bar/hooks/use-click-outside.ts +1 -1
  171. package/src/components-v2/wishlist-dialogs/add-to-wishlist/add-to-wishlist-button.tsx +2 -6
  172. package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-bottom-ui.tsx +6 -1
  173. package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +22 -15
  174. package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-list.tsx +8 -7
  175. package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/context.ts +8 -8
  176. package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-item.tsx +29 -19
  177. package/src/components-v2/wishlist-dialogs/add-to-wishlist/hooks/use-add-to-wishlist.ts +27 -77
  178. package/src/components-v2/wishlist-dialogs/add-to-wishlist/index.tsx +1 -9
  179. package/src/components-v2/wishlist-dialogs/create-wishlist/compound/create-wishlist-bottom-ui.tsx +7 -2
  180. package/src/components-v2/wishlist-dialogs/create-wishlist/compound/create-wishlist-dialog.tsx +20 -12
  181. package/src/components-v2/wishlist-dialogs/create-wishlist/compound/create-wishlist-form.tsx +25 -5
  182. package/src/hooks/use-cart.ts +11 -14
  183. package/src/hooks/use-favorite.ts +17 -0
  184. package/src/hooks/use-variants.ts +4 -3
  185. package/src/pages/account/addresses/address-item.tsx +1 -1
  186. package/src/pages/account/dashboard-header.tsx +3 -3
  187. package/src/pages/account/navigation.tsx +5 -2
  188. package/src/pages/account/no-records.tsx +1 -1
  189. package/src/pages/account/orders/order-progress.tsx +5 -2
  190. package/src/pages/account/orders/order-row.tsx +4 -1
  191. package/src/pages/account/orders/order-summary.tsx +6 -2
  192. package/src/pages/account/orders/ordered-products.tsx +9 -2
  193. package/src/pages/account/profile/user-info.tsx +5 -2
  194. package/src/pages/account/wishlist/create-new-list.tsx +1 -1
  195. package/src/pages/account/wishlist/wishlist-item.tsx +2 -2
  196. package/src/pages/checkout/checkout-alt-form/address-card.tsx +5 -2
  197. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +3 -3
  198. package/src/pages/checkout/checkout-alt-form/customer-information.tsx +1 -1
  199. package/src/pages/checkout/checkout-alt-form/delivery-address.tsx +5 -1
  200. package/src/pages/checkout/checkout-alt-form/delivery-options.tsx +6 -2
  201. package/src/pages/checkout/checkout-alt-form/edit-address-form.tsx +1 -1
  202. package/src/pages/checkout/checkout-alt-form/heading.tsx +2 -2
  203. package/src/pages/checkout/checkout-alt-form/new-address-form.tsx +9 -4
  204. package/src/pages/checkout/checkout-alt-form/payment-details.tsx +6 -3
  205. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +5 -2
  206. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +4 -2
  207. package/src/pages/checkout/checkout-alt-summary/list-item.tsx +2 -2
  208. package/src/pages/confirmation/address.tsx +4 -1
  209. package/src/pages/confirmation/confirmation-summary.tsx +6 -3
  210. package/src/pages/confirmation/ordered-products.tsx +9 -2
  211. package/src/pages/product-details/available-shops.tsx +5 -2
  212. package/src/pages/product-details/bopis/find-in-store-modal.tsx +1 -1
  213. package/src/pages/product-details/bopis/pickup-option-select.tsx +1 -1
  214. package/src/pages/product-details/frequently-bought.tsx +9 -2
  215. package/src/pages/product-details/frequently-product-card.tsx +8 -5
  216. package/src/pages/product-details/product-comment.tsx +7 -2
  217. package/src/pages/product-details/product-description.tsx +1 -1
  218. package/src/pages/product-details/product-filter-card.tsx +11 -3
  219. package/src/pages/product-details/product-intro/product-intro-images.tsx +4 -1
  220. package/src/pages/product-details/product-intro/product-intro.tsx +8 -5
  221. package/src/pages/product-details/product-review.tsx +5 -2
  222. package/src/pages/product-details/related-products.tsx +2 -2
  223. package/src/pages/product-list/facet-group.tsx +1 -1
  224. package/src/pages/product-list/list-filter.tsx +5 -2
  225. package/src/pages/product-list/pagination.tsx +1 -1
  226. package/src/pages/product-list/quick-view-dialog-content.tsx +2 -2
  227. package/src/pages/product-list/selected-facets.tsx +1 -1
  228. package/src/pages/quickorder/order-upload.tsx +1 -1
  229. package/src/pages/quickorder/quick-order-form.tsx +6 -3
  230. package/src/pages/quickorder/quick-order-row.tsx +1 -1
  231. package/src/pages-v2/account/account-navigation.tsx +1 -1
  232. package/src/pages-v2/account/orders/order-summary.tsx +5 -2
  233. package/src/pages-v2/account/profile/user-info.tsx +6 -3
  234. package/src/pages-v2/account/wishlist/wishlist-item.tsx +1 -1
  235. package/src/pages-v2/cart/__tests__/checkout-form.test.tsx +1 -1
  236. package/src/pages-v2/cart/estimate-shipping.tsx +3 -0
  237. package/src/pages-v2/checkout/checkout-alt-form/checkout-step.tsx +4 -4
  238. package/src/pages-v2/checkout/checkout-alt-form/steps/address/address-card.tsx +1 -1
  239. package/src/pages-v2/checkout/checkout-alt-form/steps/address/delivery-address.tsx +12 -5
  240. package/src/pages-v2/checkout/checkout-alt-form/steps/address/new-address-form.tsx +27 -5
  241. package/src/pages-v2/checkout/checkout-alt-form/steps/address/styles.ts +2 -1
  242. package/src/pages-v2/checkout/checkout-alt-form/steps/customer-info/customer-information.tsx +27 -7
  243. package/src/pages-v2/checkout/checkout-alt-form/steps/shipping/delivery-options.tsx +8 -1
  244. package/src/pages-v2/confirmation/address.tsx +5 -1
  245. package/src/pages-v2/product-details/available-shops.tsx +4 -1
  246. package/src/pages-v2/product-details/bopis/find-in-store-button.tsx +1 -1
  247. package/src/pages-v2/product-details/bopis/find-in-store-modal.tsx +3 -5
  248. package/src/pages-v2/product-details/product-intro/compound/context.ts +69 -12
  249. package/src/pages-v2/product-details/product-intro/compound/product-info.tsx +73 -27
  250. package/src/pages-v2/product-details/product-intro/compound/product-intro-images.tsx +19 -6
  251. package/src/pages-v2/product-details/product-intro/utils.ts +2 -2
  252. package/src/pages-v2/product-details/product-tabs.tsx +19 -4
  253. package/src/pages-v2/product-details/related-products.tsx +3 -3
  254. package/src/pages-v2/quickorder/context.ts +2 -2
  255. package/src/pages-v2/quickorder/order-upload.tsx +6 -2
  256. package/src/pages-v2/quickorder/provider.tsx +24 -9
  257. package/src/pages-v2/quickorder/quick-order-actions.tsx +2 -2
  258. package/src/pages-v2/quickorder/quick-order-header.tsx +6 -3
  259. package/src/pages-v2/quickorder/quick-order-row.tsx +1 -1
  260. package/src/pages-v2/quickorder/quick-order-table.tsx +10 -4
  261. package/src/pages-v2/quickorder/quick-order-upload.tsx +2 -2
  262. package/src/stories/Button.stories.ts +54 -0
  263. package/src/stories/Button.tsx +35 -0
  264. package/src/stories/Configure.mdx +364 -0
  265. package/src/stories/Header.stories.ts +34 -0
  266. package/src/stories/Header.tsx +54 -0
  267. package/src/stories/Page.stories.ts +33 -0
  268. package/src/stories/Page.tsx +73 -0
  269. package/src/stories/assets/accessibility.png +0 -0
  270. package/src/stories/assets/accessibility.svg +1 -0
  271. package/src/stories/assets/addon-library.png +0 -0
  272. package/src/stories/assets/assets.png +0 -0
  273. package/src/stories/assets/avif-test-image.avif +0 -0
  274. package/src/stories/assets/context.png +0 -0
  275. package/src/stories/assets/discord.svg +1 -0
  276. package/src/stories/assets/docs.png +0 -0
  277. package/src/stories/assets/figma-plugin.png +0 -0
  278. package/src/stories/assets/github.svg +1 -0
  279. package/src/stories/assets/share.png +0 -0
  280. package/src/stories/assets/styling.png +0 -0
  281. package/src/stories/assets/testing.png +0 -0
  282. package/src/stories/assets/theming.png +0 -0
  283. package/src/stories/assets/tutorials.svg +1 -0
  284. package/src/stories/assets/youtube.svg +1 -0
  285. package/src/stories/button.css +30 -0
  286. package/src/stories/header.css +32 -0
  287. package/src/stories/page.css +68 -0
  288. package/src/theme/build-mui-theme.ts +104 -0
  289. package/src/theme/components.ts +163 -0
  290. package/src/theme/emotion-cache.tsx +101 -0
  291. package/src/theme/theme-colors.ts +205 -0
  292. package/src/theme/theme-options-sample.ts +69 -0
  293. package/src/theme/theme-options.ts +172 -0
  294. package/src/theme/theme-provider.tsx +44 -0
  295. package/src/theme/typography.ts +22 -0
  296. package/src/theme/utils.ts +17 -0
  297. package/tsconfig.json +3 -1
  298. package/src/api-manager/lib/cart-cookie-handler.ts +0 -14
  299. package/src/components-v2/product-cards/product-card/components/hover-actions.tsx +0 -37
  300. package/src/components-v2/product-cards/product-card-plp/components/quantity-buttons.tsx +0 -54
  301. package/src/components-v2/product-cards/product-card-plp-list/components/add-to-cart.tsx +0 -63
  302. package/src/components-v2/product-cards/product-card-plp-list/components/favorite-button.tsx +0 -27
  303. package/src/components-v2/product-cards/use-product.ts +0 -35
  304. package/src/components-v2/products-view/products-grid-view.tsx +0 -65
  305. package/src/components-v2/products-view/products-list-view.tsx +0 -66
  306. package/src/components-v2/wishlist-dialogs/create-wishlist/compound/context.ts +0 -2
  307. /package/src/components-v2/product-cards/{product-card/components/quantity-buttons.tsx → quantity-buttons.tsx} +0 -0
@@ -29,7 +29,7 @@ import AddressCard from "@evenicanpm/storefront-core/src/pages-v2/checkout/check
29
29
  import { CheckoutStepComponentProps } from "@evenicanpm/storefront-core/src/pages-v2/checkout/checkout-alt-form/checkout-step";
30
30
  import { AddressDropDownHandler } from "@evenicanpm/storefront-core/src/pages-v2/checkout/checkout-alt-form/steps/address/styles";
31
31
 
32
- type Translator = (key: string, values?: Record<string, unknown>) => string;
32
+ type Translator = ReturnType<typeof useTranslations>;
33
33
 
34
34
  type DeliveryAddressCompound = React.FC<DeliveryAddressProps> & {
35
35
  Modal: React.FC;
@@ -261,10 +261,14 @@ const DeliveryAddress: DeliveryAddressCompound = ({
261
261
  setModalOpen(true);
262
262
  };
263
263
  const openEdit = (addr: Address) => {
264
- setEditAddress(addr);
264
+ setEditAddress({ ...addr });
265
265
  setModalOpen(true);
266
266
  };
267
- const closeModal = () => setModalOpen(false);
267
+
268
+ const closeModal = () => {
269
+ setModalOpen(false);
270
+ setEditAddress(undefined);
271
+ };
268
272
 
269
273
  // context value
270
274
  const ctx: DeliveryAddressContextValue = {
@@ -322,6 +326,7 @@ DeliveryAddress.Modal = () => {
322
326
 
323
327
  return (
324
328
  <NewAddressForm
329
+ key={editAddress?.Id ?? "new"}
325
330
  countries={countries ?? []}
326
331
  open={modalOpen}
327
332
  isGuest={isGuest}
@@ -336,7 +341,8 @@ DeliveryAddress.Modal = () => {
336
341
  DeliveryAddress.Dropdown = () => {
337
342
  const ctx = useContext(DeliveryAddressCtx);
338
343
  if (!ctx) return null;
339
- const { showDropDown, title, addressList, pickAddress } = ctx;
344
+ const { showDropDown, title, addressList, pickAddress, handleSaveAddress } =
345
+ ctx;
340
346
 
341
347
  if (!showDropDown) return null;
342
348
 
@@ -355,9 +361,10 @@ DeliveryAddress.Dropdown = () => {
355
361
  addressList.map((value, index) => (
356
362
  <MenuItem
357
363
  key={index}
358
- onClick={() => {
364
+ onClick={async () => {
359
365
  pickAddress(value.RecordId as number);
360
366
  onClose();
367
+ await handleSaveAddress(value);
361
368
  }}
362
369
  >
363
370
  {`${value.Name}, ${value.FullAddress}`}
@@ -73,10 +73,12 @@ const NewAddressForm = ({
73
73
  const [selectedCountry, setSelectedCountry] =
74
74
  React.useState<CountryRegionInfo | null>(defaultCountry);
75
75
 
76
- const { data: states } = getStates.useData({
76
+ const { data } = getStates.useData({
77
77
  countryId: selectedCountry?.CountryRegionId,
78
78
  });
79
79
 
80
+ const states = data ?? [];
81
+
80
82
  const validationSchema = z.object({
81
83
  name: z
82
84
  .string()
@@ -145,6 +147,20 @@ const NewAddressForm = ({
145
147
  }
146
148
  }, [address, setValue, reset]);
147
149
 
150
+ // sync selectedCountry whenever address changes, not only on initial mount, so that state field populates.
151
+ React.useEffect(() => {
152
+ if (address) {
153
+ const sel =
154
+ countries.find(
155
+ (c) => c.CountryRegionId === address.ThreeLetterISORegionName,
156
+ ) ?? null;
157
+ setSelectedCountry(sel);
158
+ if (sel) {
159
+ setValue("country", sel.CountryRegionId ?? "");
160
+ }
161
+ }
162
+ }, [address, countries, setValue]);
163
+
148
164
  const onFormSubmit = (data: AddressFormInput) => {
149
165
  const tempAddress: Address = {
150
166
  Name: data.name,
@@ -165,7 +181,7 @@ const NewAddressForm = ({
165
181
  form,
166
182
  countries,
167
183
  states: states
168
- ? states.map((s) => ({
184
+ ? states?.map((s) => ({
169
185
  StateName: s.StateName ?? "",
170
186
  StateId: s.StateId ?? "",
171
187
  }))
@@ -216,7 +232,13 @@ const NewAddressForm = ({
216
232
  };
217
233
 
218
234
  // Generic text field
219
- NewAddressForm.Field = ({ name, label }) => {
235
+ NewAddressForm.Field = ({
236
+ name,
237
+ label,
238
+ }: {
239
+ name: keyof AddressFormInput;
240
+ label: string;
241
+ }) => {
220
242
  const ctx = React.useContext(FormContext);
221
243
  if (!ctx) return null;
222
244
 
@@ -260,8 +282,8 @@ NewAddressForm.CountrySelect = () => {
260
282
  }}
261
283
  >
262
284
  <MenuItem value="">Select country</MenuItem>
263
- {countries.map((c) => (
264
- <MenuItem key={c.CountryRegionId} value={c.CountryRegionId}>
285
+ {countries.map((c, index) => (
286
+ <MenuItem key={`${c.CountryRegionId}-${index}`} value={c.CountryRegionId}>
265
287
  {c.ShortName}
266
288
  </MenuItem>
267
289
  ))}
@@ -1,7 +1,8 @@
1
1
  import Box from "@mui/material/Box";
2
2
  import styled from "@mui/material/styles/styled";
3
+ import { Theme } from "@mui/material/styles";
3
4
 
4
- const dropDownHandlerStyles = (theme) => ({
5
+ const dropDownHandlerStyles = (theme: Theme) => ({
5
6
  width: 260,
6
7
  minWidth: 260,
7
8
  height: "100%",
@@ -9,11 +9,20 @@ import { Paragraph } from "@evenicanpm/storefront-core/src/components-v2/Typogra
9
9
  import * as yup from "yup";
10
10
  import { useForm } from "react-hook-form";
11
11
  import { yupResolver } from "@hookform/resolvers/yup";
12
+ import type { SubmitHandler, UseFormReturn } from "react-hook-form";
13
+ import getUser from "@/api-manager/services/user/queries/get-user";
14
+
15
+ const vSchema = yup.object({
16
+ email: yup.string().email().required(),
17
+ });
18
+
19
+ type CustomerFormValues = yup.InferType<typeof vSchema>;
20
+
12
21
  interface CustomerCtx extends CheckoutStepComponentProps {
13
22
  email: string | null;
14
23
  cartId: string;
15
- form: ReturnType<typeof useForm<{ email?: string }>>;
16
- handleSave: (formData: { email: string }) => void;
24
+ form: UseFormReturn<CustomerFormValues, undefined, CustomerFormValues>;
25
+ handleSave: SubmitHandler<CustomerFormValues>;
17
26
  }
18
27
  const CustomerContext = createContext<CustomerCtx | null>(null);
19
28
 
@@ -22,13 +31,24 @@ const CustomerInformation = (
22
31
  ) => {
23
32
  const {
24
33
  stepViewState,
25
- cart: { Id: cartId, ReceiptEmail: email },
34
+ cart: { Id: cartId, ReceiptEmail: cartEmail },
26
35
  stepSubmissionState,
27
36
  setStepSubmissionState,
28
37
  handleStepCompletion,
29
38
  children,
30
39
  } = props;
31
40
 
41
+ const { data: user, isLoading: userLoading } = getUser.useData();
42
+ const isGuest = !userLoading && !user?.RecordId;
43
+
44
+ const resolveEmail = () => {
45
+ if (isGuest) return cartEmail;
46
+ // In case of authenticated user, pick user email when no email in cart
47
+ return cartEmail || user?.Email || "";
48
+ };
49
+
50
+ const email = resolveEmail();
51
+
32
52
  const { mutateAsync: updateCart } = useUpdateCart();
33
53
  const t = useTranslations("Checkout.CustomerInformation");
34
54
 
@@ -36,7 +56,7 @@ const CustomerInformation = (
36
56
  email: yup.string().email(t("emailInvalid")).required(t("emailRequired")),
37
57
  });
38
58
 
39
- const form = useForm({
59
+ const form = useForm<CustomerFormValues, undefined, CustomerFormValues>({
40
60
  defaultValues: { email: email || "" },
41
61
  resolver: yupResolver(validationSchema),
42
62
  });
@@ -47,8 +67,8 @@ const CustomerInformation = (
47
67
  setValue("email", email || "");
48
68
  }, [email, setValue]);
49
69
 
50
- const handleSave = React.useCallback(
51
- (formData: { email: string }) => {
70
+ const handleSave: SubmitHandler<CustomerFormValues> = React.useCallback(
71
+ (formData) => {
52
72
  updateCart({
53
73
  cart: { ReceiptEmail: formData.email, Id: cartId },
54
74
  })
@@ -79,7 +99,7 @@ const CustomerInformation = (
79
99
 
80
100
  const ctx: CustomerCtx = {
81
101
  ...props,
82
- email,
102
+ email: email ?? null,
83
103
  cartId,
84
104
  form,
85
105
  handleSave,
@@ -115,7 +115,14 @@ const DeliveryOptions: DeliveryOptionsCompound = (props) => {
115
115
  React.useEffect(() => {
116
116
  if (currentShippingAddress) {
117
117
  const { Id, CartLines } = cart;
118
- const cartLineIds = CartLines.map((cl) => cl.LineId).filter(Boolean);
118
+ // Filter out undefined values and assert string[]
119
+ const cartLineIds: string[] = CartLines.map((cl) => cl.LineId).filter(
120
+ (id): id is string => Boolean(id),
121
+ );
122
+ if (!Id || !cartLineIds?.length) {
123
+ setDeliveryOptions([]);
124
+ return;
125
+ }
119
126
  getDeliveryOptions({
120
127
  cartId: Id,
121
128
  cartLineIds,
@@ -33,7 +33,11 @@ OrderAddress.Heading = ({ children }: { children?: ReactNode }) => {
33
33
  );
34
34
  };
35
35
 
36
- OrderAddress.Field = ({ property }) => {
36
+ OrderAddress.Field = ({
37
+ property,
38
+ }: {
39
+ property: Exclude<keyof Address, "ExtensionProperties">;
40
+ }) => {
37
41
  const ctx = useContext(AddressContext);
38
42
  if (!ctx) return null;
39
43
  const address = ctx;
@@ -6,7 +6,10 @@ import Card from "@mui/material/Card";
6
6
  import Grid from "@mui/material/Grid2";
7
7
  import Avatar from "@mui/material/Avatar";
8
8
  // GLOBAL CUSTOM COMPONENTS
9
- import { H3, H4 } from "@evenicanpm/storefront-core/src/components-v2/Typography";
9
+ import {
10
+ H3,
11
+ H4,
12
+ } from "@evenicanpm/storefront-core/src/components-v2/Typography";
10
13
  import FlexRowCenter from "@evenicanpm/storefront-core/src/components-v2/flex-box/flex-row-center";
11
14
 
12
15
  export default function AvailableShops() {
@@ -5,7 +5,7 @@ import { AddToCartBopisInput } from "@evenicanpm/storefront-core/src/hooks/use-c
5
5
  import { useTranslations } from "next-intl";
6
6
 
7
7
  type Props = {
8
- productId: number;
8
+ productId?: number;
9
9
  allDimensionsSelected: boolean;
10
10
  handleAddToCart: (bopisInput: AddToCartBopisInput) => void;
11
11
  setDimensionsError: (state: boolean) => void;
@@ -22,7 +22,6 @@ import { geolocate } from "@/lib/map-helper";
22
22
  import getStores from "@/api-manager/services/organization/queries/get-stores";
23
23
  import { AddToCartBopisInput } from "@evenicanpm/storefront-core/src/hooks/use-cart";
24
24
  import getProductAvailability from "@/api-manager/services/product/queries/get-product-availability";
25
- import { InventorySearchCriteriaInput } from "@/api-manager/schemas/product.schema";
26
25
  import { OrgUnitLocationReturnType } from "@/api-manager/schemas/organization.schema";
27
26
  import { useTranslations } from "next-intl";
28
27
  import getChannelConfiguration from "@/api-manager/services/organization/queries/get-channel-configuration";
@@ -31,7 +30,7 @@ type Props = {
31
30
  open: boolean;
32
31
  handleClose: () => void;
33
32
  handleAddToCart: (bopisInput: AddToCartBopisInput) => void;
34
- productId: number;
33
+ productId?: number;
35
34
  allDimensionsSelected: boolean;
36
35
  };
37
36
 
@@ -87,12 +86,11 @@ const FindInStoreModal = ({
87
86
  searchArea,
88
87
  });
89
88
 
90
- const inventoryInput: InventorySearchCriteriaInput = React.useMemo(
89
+ const inventoryInput = React.useMemo(
91
90
  () => ({
92
- allDimensionsSelected,
93
91
  searchCriteria: {
94
92
  defaultWarehouseOnly: false,
95
- productIds: [productId],
93
+ productIds: [productId || 0],
96
94
  searchArea,
97
95
  },
98
96
  }),
@@ -1,26 +1,31 @@
1
- import { createContext } from "react";
1
+ import { createContext, Dispatch, SetStateAction, useContext } from "react";
2
2
  import { ProductDetails } from "@/api-manager/schemas/product.schema";
3
3
  import { SelectedDimensions } from "@evenicanpm/storefront-core/src/hooks/use-variants";
4
- import { ProductWarehouseInventoryInformation } from "@msdyn365-commerce/retail-proxy";
4
+ import {
5
+ ProductWarehouseInventoryInformation,
6
+ ProductDimensionValueInventoryAvailability,
7
+ } from "@msdyn365-commerce/retail-proxy";
5
8
 
6
9
  export interface ProductIntroContext {
7
10
  /** The current master product of the PDP */
8
11
  masterProduct: ProductDetails;
9
12
  /** The currently selected variant that is deduced from dimensions */
10
- selectedVariant: ProductDetails;
13
+ selectedVariant: ProductDetails | undefined;
11
14
  /** Is the data for variant loading? */
12
15
  variantPending: boolean;
13
16
  /** State that tracks which dimensions are currently selected */
14
17
  selectDimensions: SelectedDimensions;
15
18
  /** Handler for when dimensions are selected **/
16
- handleChangeVariant: () => void;
19
+ handleChangeVariant: (
20
+ dimensionValue: ProductDimensionValueInventoryAvailability,
21
+ ) => void;
17
22
  /** Calculated value are all dimensions selected? **/
18
23
  allDimensionsSelected: boolean;
19
24
  }
20
25
 
21
26
  export interface ProductImagesContext {
22
27
  /** Currently selected index from images array of product details */
23
- selectedImageIndex?: number;
28
+ selectedImageIndex: number;
24
29
  /** The default image to show on PDP */
25
30
  primaryImageUrl: string;
26
31
  /* Handler when thumbnails are clicked */
@@ -32,18 +37,70 @@ export interface ProductImagesContext {
32
37
  }
33
38
 
34
39
  export interface ProductInfoContext {
35
- /** Currently selected index from images array of product details */
36
- isOOS: boolean;
40
+ /** Does the product have variants? If not we do not verify dimensions before adding to cart etc. **/
41
+ hasVariants: boolean;
37
42
  /** **/
38
43
  inventoryPending: boolean;
39
44
  /** */
40
- shippingInventoryData: ProductWarehouseInventoryInformation;
45
+ shippingInventoryData: ProductWarehouseInventoryInformation | undefined;
41
46
  /** */
42
47
  showDimensionError: boolean;
43
48
  /** */
44
- setShowDimensionError: () => void;
49
+ setShowDimensionError: Dispatch<SetStateAction<boolean>>;
45
50
  }
46
51
 
47
- export const ProductImagesContext = createContext(null);
48
- export const ProductIntroContext = createContext(null);
49
- export const ProductInfoContext = createContext(null);
52
+ export const ProductImagesContext = createContext<ProductImagesContext | null>(
53
+ null,
54
+ );
55
+ export const ProductIntroContext = createContext<ProductIntroContext | null>(
56
+ null,
57
+ );
58
+ export const ProductInfoContext = createContext<ProductInfoContext | null>(
59
+ null,
60
+ );
61
+
62
+ /**
63
+ * Hook to safely access ProductImagesContext
64
+ * @throws {Error} When used outside of ProductImagesProvider
65
+ */
66
+ export const useProductImages = () => {
67
+ const context = useContext(ProductImagesContext);
68
+
69
+ if (context === null) {
70
+ throw new Error(
71
+ "useProductImages must be used within a ProductImagesProvider",
72
+ );
73
+ }
74
+
75
+ return context;
76
+ };
77
+
78
+ /**
79
+ * Hook to safely access ProductIntroContext
80
+ * @throws {Error} When used outside of ProductIntroProvider
81
+ */
82
+ export const useProductIntro = () => {
83
+ const context = useContext(ProductIntroContext);
84
+
85
+ if (context === null) {
86
+ throw new Error(
87
+ "useProductIntro must be used within a ProductIntroProvider",
88
+ );
89
+ }
90
+
91
+ return context;
92
+ };
93
+
94
+ /**
95
+ * Hook to safely access ProductInfoContext
96
+ * @throws {Error} When used outside of ProductInfoProvider
97
+ */
98
+ export const useProductInfo = () => {
99
+ const context = useContext(ProductInfoContext);
100
+
101
+ if (context === null) {
102
+ throw new Error("useProductInfo must be used within a ProductInfoProvider");
103
+ }
104
+
105
+ return context;
106
+ };
@@ -29,7 +29,8 @@ import useCart, {
29
29
  import ProductDimensions from "@evenicanpm/storefront-core/src/components-v2/product-dimensions";
30
30
  import FindInStoreButton from "@evenicanpm/storefront-core/src/pages-v2/product-details/bopis/find-in-store-button";
31
31
  import {
32
- ProductIntroContext,
32
+ useProductInfo,
33
+ useProductIntro,
33
34
  ProductInfoContext,
34
35
  } from "@evenicanpm/storefront-core/src/pages-v2/product-details/product-intro/compound/context";
35
36
 
@@ -48,19 +49,27 @@ export interface Props {
48
49
  */
49
50
  const ProductInfo = ({ children }: Props) => {
50
51
  const [showDimensionError, setShowDimensionError] = useState(false);
51
- const { selectedVariant } = useContext(ProductIntroContext);
52
+ const { masterProduct, selectedVariant } = useProductIntro();
52
53
 
53
- const shippingInventorySearchCriteria = selectedVariant
54
- ? buildInventorySearchCriteria(selectedVariant?.RecordId)
55
- : null;
54
+ const hasVariants = masterProduct?.Dimensions?.length !== 0;
55
+
56
+ const id = hasVariants ? selectedVariant?.RecordId : masterProduct?.RecordId;
57
+
58
+ const shippingInventorySearchCriteria =
59
+ !hasVariants || selectedVariant
60
+ ? buildInventorySearchCriteria(id as number)
61
+ : null;
56
62
 
57
63
  // Query disabled if the input is null
58
64
  const { data: shippingInventoryData, isLoading: inventoryPending } =
59
- getProductAvailability.useData(shippingInventorySearchCriteria);
65
+ getProductAvailability.useData(shippingInventorySearchCriteria, {
66
+ enabled: !!shippingInventorySearchCriteria,
67
+ });
60
68
 
61
69
  return (
62
70
  <ProductInfoContext.Provider
63
71
  value={{
72
+ hasVariants,
64
73
  inventoryPending,
65
74
  shippingInventoryData,
66
75
  showDimensionError,
@@ -78,7 +87,12 @@ const ProductInfo = ({ children }: Props) => {
78
87
  * @category Sub-Component
79
88
  */
80
89
  ProductInfo.Name = () => {
81
- const { masterProduct } = useContext(ProductIntroContext);
90
+ const IntroCtx = useProductIntro();
91
+ if (!IntroCtx)
92
+ throw new Error(
93
+ "ProductInfo.Inventory must be wrapped in ProductInfoContext and a ProductIntroContext",
94
+ );
95
+ const { masterProduct } = IntroCtx;
82
96
  return <H1 mb={1}>{masterProduct.Name}</H1>;
83
97
  };
84
98
 
@@ -86,7 +100,12 @@ ProductInfo.Name = () => {
86
100
  * @category Sub-Component
87
101
  */
88
102
  ProductInfo.Sku = () => {
89
- const { masterProduct, selectedVariant } = useContext(ProductIntroContext);
103
+ const IntroCtx = useProductIntro();
104
+ if (!IntroCtx)
105
+ throw new Error(
106
+ "ProductInfo.Inventory must be wrapped in ProductInfoContext and a ProductIntroContext",
107
+ );
108
+ const { masterProduct, selectedVariant } = IntroCtx;
90
109
  return (
91
110
  <FlexBox alignItems="center" mb={1}>
92
111
  <H6>SKU: {selectedVariant?.ItemId || masterProduct.ItemId}</H6>
@@ -111,9 +130,19 @@ ProductInfo.Sku = () => {
111
130
  * @category Sub-Component
112
131
  */
113
132
  ProductInfo.Dimensions = () => {
114
- const { masterProduct, selectDimensions, handleChangeVariant } =
115
- useContext(ProductIntroContext);
116
- const { showDimensionError } = useContext(ProductInfoContext);
133
+ const InfoCtx = useContext(ProductInfoContext);
134
+ const IntroCtx = useProductIntro();
135
+ if (!InfoCtx || !IntroCtx)
136
+ throw new Error(
137
+ "ProductInfo.Inventory must be wrapped in ProductInfoContext and a ProductIntroContext",
138
+ );
139
+ if (!IntroCtx)
140
+ throw new Error(
141
+ "ProductInfo.Inventory must be wrapped in ProductInfoContext and a ProductIntroContext",
142
+ );
143
+
144
+ const { masterProduct, selectDimensions, handleChangeVariant } = IntroCtx;
145
+ const { showDimensionError } = InfoCtx;
117
146
  return (
118
147
  <ProductDimensions
119
148
  dimensionGroups={masterProduct.DimensionValues}
@@ -125,7 +154,12 @@ ProductInfo.Dimensions = () => {
125
154
  };
126
155
 
127
156
  ProductInfo.Price = () => {
128
- const { masterProduct, selectedVariant } = useContext(ProductIntroContext);
157
+ const IntroCtx = useProductIntro();
158
+ if (!IntroCtx)
159
+ throw new Error(
160
+ "ProductInfo.Inventory must be wrapped in ProductInfoContext and a ProductIntroContext",
161
+ );
162
+ const { masterProduct, selectedVariant } = IntroCtx;
129
163
  const { formatCurrency } = useCurrencyFormatter();
130
164
 
131
165
  const { data: productPrices, isPending: pricesPending } =
@@ -139,7 +173,7 @@ ProductInfo.Price = () => {
139
173
  {pricesPending || !productPrices ? (
140
174
  <CircularProgress size={20} />
141
175
  ) : (
142
- formatCurrency(productPrices?.[0]?.CustomerContextualPrice)
176
+ formatCurrency(productPrices?.[0]?.CustomerContextualPrice || 0)
143
177
  )}
144
178
  </H2>
145
179
  <Box color="inherit">{}</Box>
@@ -151,11 +185,14 @@ ProductInfo.Price = () => {
151
185
  * @category Sub-Component
152
186
  */
153
187
  ProductInfo.Inventory = () => {
154
- const { variantPending, allDimensionsSelected } =
155
- useContext(ProductIntroContext);
156
- const { shippingInventoryData, inventoryPending } =
157
- useContext(ProductInfoContext);
158
-
188
+ const InfoCtx = useContext(ProductInfoContext);
189
+ const IntroCtx = useProductIntro();
190
+ if (!InfoCtx || !IntroCtx)
191
+ throw new Error(
192
+ "ProductInfo.Inventory must be wrapped in ProductInfoContext and a ProductIntroContext",
193
+ );
194
+ const { variantPending, allDimensionsSelected } = IntroCtx;
195
+ const { shippingInventoryData, inventoryPending } = InfoCtx;
159
196
  if (!allDimensionsSelected)
160
197
  return (
161
198
  <FlexBox alignItems="center" gap={1}>
@@ -186,14 +223,21 @@ ProductInfo.Inventory = () => {
186
223
  };
187
224
 
188
225
  /**
226
+ * These are grouped together because they have similar functionality and callbacks
227
+ * addToCart Etc
189
228
  * @category Sub-Component
190
229
  */
191
230
  ProductInfo.Buttons = () => {
192
231
  const { masterProduct, selectedVariant, allDimensionsSelected } =
193
- useContext(ProductIntroContext);
194
- const { setShowDimensionError, shippingInventoryData } =
195
- useContext(ProductInfoContext);
196
- const { handleAddToCart } = useCart(selectedVariant?.RecordId);
232
+ useProductIntro();
233
+ const { setShowDimensionError, shippingInventoryData, hasVariants } =
234
+ useProductInfo();
235
+
236
+ const productId = hasVariants
237
+ ? selectedVariant?.RecordId
238
+ : masterProduct?.RecordId;
239
+
240
+ const { handleAddToCart } = useCart(productId);
197
241
 
198
242
  /**
199
243
  * A wrapper around the add to cart handler that
@@ -208,24 +252,26 @@ ProductInfo.Buttons = () => {
208
252
  handleAddToCart(bopisInput);
209
253
  };
210
254
 
211
- const isOOS = allDimensionsSelected && checkProductOOS(shippingInventoryData);
255
+ const isOOS = hasVariants
256
+ ? allDimensionsSelected && checkProductOOS(shippingInventoryData)
257
+ : checkProductOOS(shippingInventoryData);
212
258
 
213
259
  return (
214
260
  <FlexBox gap={2}>
215
261
  <QuantityButtons
216
262
  customAddToCart={onAddToCart}
217
263
  isOOS={isOOS}
218
- productId={selectedVariant?.RecordId}
264
+ productId={productId}
219
265
  />
220
266
  <FindInStoreButton
221
267
  setDimensionsError={setShowDimensionError}
222
- productId={selectedVariant?.RecordId || masterProduct.RecordId}
223
- handleAddToCart={() => handleAddToCart(selectedVariant?.RecordId)}
268
+ productId={productId}
269
+ handleAddToCart={handleAddToCart}
224
270
  allDimensionsSelected={allDimensionsSelected}
225
271
  />
226
272
  <AddToWishlistButton
227
273
  path="Account.Wishlists.btnAddToWishlist"
228
- product={masterProduct}
274
+ productId={masterProduct?.RecordId}
229
275
  />
230
276
  </FlexBox>
231
277
  );
@@ -23,7 +23,10 @@ type Props = {
23
23
  export default function ProductImages({ children }: Props) {
24
24
  const [selectedImageIndex, setSelectedImageIndex] = useState<number>(0);
25
25
 
26
- const { selectedVariant, masterProduct } = useContext(ProductIntroContext);
26
+ const ctx = useContext(ProductIntroContext);
27
+ if (!ctx)
28
+ throw new Error("ProductImages must be used inside a ProductIntroContext");
29
+ const { selectedVariant, masterProduct } = ctx;
27
30
  const { Name } = masterProduct;
28
31
 
29
32
  /**
@@ -38,7 +41,7 @@ export default function ProductImages({ children }: Props) {
38
41
  : [];
39
42
 
40
43
  const primaryImageUrl =
41
- selectedVariant?.primaryImageUrl || masterProduct?.primaryImageUrl;
44
+ selectedVariant?.PrimaryImageUrl || masterProduct?.PrimaryImageUrl || "";
42
45
 
43
46
  // HANDLE SELECT IMAGE
44
47
  const handleImageClick = (ind: number) => () => {
@@ -67,8 +70,13 @@ export default function ProductImages({ children }: Props) {
67
70
  * @category Sub-Component
68
71
  */
69
72
  ProductImages.Thumbnails = () => {
70
- const { images, handleImageClick, selectedImageIndex } =
71
- useContext(ProductImagesContext);
73
+ const ctx = useContext(ProductImagesContext);
74
+ if (!ctx)
75
+ throw new Error(
76
+ "ProductImages.Main must be used inside a product images context",
77
+ );
78
+
79
+ const { images, handleImageClick, selectedImageIndex } = ctx;
72
80
 
73
81
  return (
74
82
  <>
@@ -104,8 +112,13 @@ ProductImages.Thumbnails = () => {
104
112
  * @category Sub-Component
105
113
  */
106
114
  ProductImages.Main = () => {
107
- const { name, images, primaryImageUrl, selectedImageIndex } =
108
- useContext(ProductImagesContext);
115
+ const ctx = useContext(ProductImagesContext);
116
+ if (!ctx)
117
+ throw new Error(
118
+ "ProductImages.Main must be used inside a product images context",
119
+ );
120
+ const { name, images, primaryImageUrl, selectedImageIndex } = ctx;
121
+
109
122
  return (
110
123
  <FlexBox borderRadius={3} overflow="hidden" justifyContent="center" mb={6}>
111
124
  <ImageWithFallback
@@ -1,7 +1,7 @@
1
1
  import { ProductWarehouseInventoryInformation } from "@msdyn365-commerce/retail-proxy";
2
2
 
3
3
  export const getInventoryLabel = (
4
- shippingInventoryData: ProductWarehouseInventoryInformation,
4
+ shippingInventoryData?: ProductWarehouseInventoryInformation,
5
5
  ): string | null => {
6
6
  const aggregate =
7
7
  shippingInventoryData?.AggregatedProductInventoryAvailabilities &&
@@ -14,7 +14,7 @@ export const getInventoryLabel = (
14
14
  };
15
15
 
16
16
  export const checkProductOOS = (
17
- shippingInventoryData: ProductWarehouseInventoryInformation,
17
+ shippingInventoryData?: ProductWarehouseInventoryInformation,
18
18
  ) => {
19
19
  //TODO: Incorporate global inventory enabled flag.
20
20
  const OOSCode = "OOS"; // Always means out of stock