@evenicanpm/storefront-core 2.3.1 → 2.4.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 (59) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +29 -9
  3. package/src/api-manager/datasources/d365/d365-address.datasource.ts +11 -0
  4. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +23 -1
  5. package/src/api-manager/datasources/d365/d365-order.datasource.ts +6 -1
  6. package/src/api-manager/index.ts +2 -1
  7. package/src/api-manager/schemas/product.schema.ts +1 -1
  8. package/src/auth/msal.ts +1 -4
  9. package/src/cms/blocks/block-manager.tsx +1 -1
  10. package/src/cms/endpoints.ts +7 -2
  11. package/src/components/BazaarMenu.tsx +1 -1
  12. package/src/components/Typography.tsx +4 -2
  13. package/src/components/carousel-cards/carousel-card-1/CarouselCard1.stories.tsx +1 -1
  14. package/src/components/categories/category-menu.tsx +1 -1
  15. package/src/components/countries-input.tsx +4 -0
  16. package/src/components/header/sticky-header.tsx +0 -1
  17. package/src/components/navbar/mobile-menu/mobile-menu.test.tsx +1 -1
  18. package/src/components/navbar/utils/transform-nav.test.ts +1 -1
  19. package/src/components/navbar/utils/transform-nav.ts +1 -1
  20. package/src/components/product-cards/product-card/product-card.tsx +5 -2
  21. package/src/components/product-cards/product-card/styles/index.ts +1 -5
  22. package/src/components/products-view/compound/products-grid-view.tsx +5 -1
  23. package/src/components/products-view/compound/products-list-view.tsx +5 -1
  24. package/src/global.d.ts +3 -0
  25. package/src/pages/account/addresses/address-form.tsx +20 -2
  26. package/src/pages/account/wishlist/wishlist-item.tsx +1 -2
  27. package/src/pages/cart/cart-item.tsx +1 -7
  28. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +14 -3
  29. package/src/pages/checkout/checkout-alt-form/steps/address/address-card.tsx +5 -3
  30. package/src/pages/checkout/checkout-alt-form/steps/address/new-address-form.tsx +1 -1
  31. package/src/pages/confirmation/ordered-products.tsx +3 -1
  32. package/src/pages/product-details/bopis/find-in-store-modal.tsx +4 -4
  33. package/src/pages/product-details/bopis/pickup-option-select.tsx +2 -2
  34. package/src/pages/product-details/bopis/search-header.tsx +2 -2
  35. package/src/pages/product-details/product-intro/compound/context.ts +8 -3
  36. package/src/pages/product-details/product-intro/compound/product-info.tsx +25 -19
  37. package/src/pages/product-list/product-list-view.tsx +2 -1
  38. package/src/providers/nav-provider/index.tsx +1 -1
  39. package/src/providers/nav-provider/utils/createLink.ts +1 -1
  40. package/tsconfig.json +1 -2
  41. package/__mocks__/countries.ts +0 -11
  42. package/__mocks__/create-mutation.ts +0 -68
  43. package/__mocks__/create-query.ts +0 -94
  44. package/__mocks__/data/categories.json +0 -795
  45. package/__mocks__/get-product-by-id.ts +0 -6
  46. package/__mocks__/next-auth-react.ts +0 -9
  47. package/__mocks__/next-font.js +0 -4
  48. package/__mocks__/next-headers.js +0 -13
  49. package/__mocks__/next-image.tsx +0 -18
  50. package/__mocks__/next-link.js +0 -19
  51. package/__mocks__/next-navigation.ts +0 -29
  52. package/__mocks__/product-dimensions.ts +0 -635
  53. package/__mocks__/products.ts +0 -533
  54. package/__mocks__/query-client.ts +0 -3
  55. package/__mocks__/wishlists.json +0 -408
  56. package/src/cms/blog.ts +0 -229
  57. package/src/cms/interfaces/navigation.ts +0 -52
  58. package/src/cms/interfaces/product-data.ts +0 -83
  59. package/src/cms/pages.ts +0 -149
@@ -11,6 +11,7 @@ import type { CountryRegionInfo } from "@msdyn365-commerce/retail-proxy";
11
11
  import {
12
12
  Box,
13
13
  Button,
14
+ CircularProgress,
14
15
  FormControl,
15
16
  InputLabel,
16
17
  MenuItem,
@@ -25,7 +26,7 @@ import { withZodSchema } from "formik-validator-zod";
25
26
  import _ from "lodash";
26
27
  import { useRouter } from "next/navigation";
27
28
  import { useLocale, useTranslations } from "next-intl";
28
- import React, { createContext, useContext } from "react";
29
+ import React, { createContext, useContext, useRef } from "react";
29
30
  import { z } from "zod";
30
31
 
31
32
  interface AddressFormValues {
@@ -71,6 +72,7 @@ const AddressForm = ({ address, children }: RootProps) => {
71
72
 
72
73
  const { mutateAsync: createAddress } = useCreateAddress();
73
74
  const { mutateAsync: updateAddress } = useUpdateAddress();
75
+ const isSubmittingRef = useRef(false);
74
76
 
75
77
  const FormSchema = z.object({
76
78
  Name: z.string().nonempty(t("Validation.required", { field: t("name") })),
@@ -99,6 +101,9 @@ const AddressForm = ({ address, children }: RootProps) => {
99
101
  };
100
102
 
101
103
  const handleSubmit = async (values: AddressFormValues) => {
104
+ if (isSubmittingRef.current) return;
105
+ isSubmittingRef.current = true;
106
+
102
107
  let newAddress: Address = {
103
108
  Name: values.Name,
104
109
  Street: values.Street,
@@ -128,6 +133,7 @@ const AddressForm = ({ address, children }: RootProps) => {
128
133
  severity: "error",
129
134
  });
130
135
  console.log(error);
136
+ isSubmittingRef.current = false;
131
137
  }
132
138
  };
133
139
 
@@ -291,8 +297,20 @@ AddressForm.Actions = ({ children }: { children?: React.ReactNode }) => {
291
297
 
292
298
  AddressForm.SubmitButton = () => {
293
299
  const t = useTranslations("Address");
300
+ const ctx = useContext(FormContext);
301
+ const isSubmitting = ctx?.formik.isSubmitting ?? false;
294
302
  return (
295
- <Button type="submit" variant="contained" color="primary">
303
+ <Button
304
+ type="submit"
305
+ variant="contained"
306
+ color="primary"
307
+ disabled={isSubmitting}
308
+ startIcon={
309
+ isSubmitting ? (
310
+ <CircularProgress size={20} color="inherit" />
311
+ ) : undefined
312
+ }
313
+ >
296
314
  {t("save")}
297
315
  </Button>
298
316
  );
@@ -74,11 +74,10 @@ const WishlistItem = ({ wishlist, children, sx }: RootProps) => {
74
74
  };
75
75
 
76
76
  WishlistItem.Name = () => {
77
+ const { wishlists: wishlistsRoute } = useCreateAccountRoutes();
77
78
  const ctx = useContext(WishlistItemContext);
78
79
  if (!ctx) return null;
79
80
  const { wishlist } = ctx;
80
- const { wishlists: wishlistsRoute } = useCreateAccountRoutes();
81
-
82
81
  return (
83
82
  <Paragraph ellipsis>
84
83
  <NavLink3
@@ -416,13 +416,6 @@ CartItemRoot.Comment = () => {
416
416
  </FlexBox>
417
417
  </>
418
418
  );
419
- } else if (isLoading) {
420
- content = (
421
- <CircularProgress
422
- size={30}
423
- sx={{ marginTop: "auto", marginLeft: "auto" }}
424
- />
425
- );
426
419
  } else {
427
420
  content = (
428
421
  <>
@@ -448,6 +441,7 @@ CartItemRoot.Comment = () => {
448
441
  <Button
449
442
  color="primary"
450
443
  variant="outlined"
444
+ disabled={isLoading}
451
445
  sx={{ width: 150, mt: "auto", ml: "auto" }}
452
446
  onClick={() => setIsWritingComment(true)}
453
447
  >
@@ -7,7 +7,7 @@ import { PageSlug } from "@evenicanpm/storefront-core/src/lib/page-slugs";
7
7
  import CheckoutStep from "@evenicanpm/storefront-core/src/pages/checkout/checkout-alt-form/checkout-step";
8
8
  import { useNotification } from "@evenicanpm/storefront-core/src/providers/notifications/use-notification";
9
9
  import type { CardPaymentAcceptResult } from "@msdyn365-commerce/retail-proxy";
10
- import { Box, Button } from "@mui/material";
10
+ import { Box, Button, CircularProgress } from "@mui/material";
11
11
  import Grid from "@mui/material/Grid2";
12
12
  import { useQueryClient } from "@tanstack/react-query";
13
13
  import { useRouter } from "next/navigation";
@@ -18,6 +18,7 @@ import React, {
18
18
  type ReactNode,
19
19
  useContext,
20
20
  useEffect,
21
+ useRef,
21
22
  useState,
22
23
  } from "react";
23
24
 
@@ -62,12 +63,15 @@ const CheckoutForm = ({ cart, children }: CheckoutFormProps) => {
62
63
  const { data: channelConfigurationData } = getChannelConfiguration.useData();
63
64
  const [isSubmitting, setIsSubmitting] = useState(false);
64
65
  const [totalSteps, setTotalSteps] = useState(0);
66
+ const isSubmittingRef = useRef(false);
65
67
 
66
68
  const cleanupCart = async (transactionId: string) => {
67
69
  router.push(`${PageSlug.Confirmation}?transactionId=${transactionId}`);
68
70
  };
69
71
 
70
72
  const placeOrder = async () => {
73
+ if (isSubmittingRef.current) return;
74
+ isSubmittingRef.current = true;
71
75
  setIsSubmitting(true);
72
76
  const cache = queryClient.getQueryCache();
73
77
 
@@ -98,6 +102,8 @@ const CheckoutForm = ({ cart, children }: CheckoutFormProps) => {
98
102
  .data as CardPaymentAcceptResult;
99
103
 
100
104
  if (!paymentAcceptResult?.TokenizedPaymentCard) {
105
+ isSubmittingRef.current = false;
106
+ setIsSubmitting(false);
101
107
  return;
102
108
  }
103
109
 
@@ -121,8 +127,9 @@ const CheckoutForm = ({ cart, children }: CheckoutFormProps) => {
121
127
  message: t("placeOrderErrorMessage"),
122
128
  severity: "error",
123
129
  });
130
+ isSubmittingRef.current = false;
131
+ setIsSubmitting(false);
124
132
  }
125
- setIsSubmitting(false);
126
133
  };
127
134
 
128
135
  return (
@@ -227,7 +234,11 @@ CheckoutForm.Footer = () => {
227
234
  color="primary"
228
235
  onClick={placeOrder}
229
236
  >
230
- {t("placeOrderButton")}
237
+ {isSubmitting ? (
238
+ <CircularProgress size={24} color="inherit" />
239
+ ) : (
240
+ t("placeOrderButton")
241
+ )}
231
242
  </Button>
232
243
  </Grid>
233
244
  );
@@ -115,13 +115,15 @@ AddressCard.Body = () => {
115
115
  if (!ctx) return null;
116
116
 
117
117
  const { address } = ctx;
118
+ // Join only non-empty values so a dangling comma is never rendered.
119
+ const stateRegion = [address.State, address.ThreeLetterISORegionName]
120
+ .filter((value) => value?.trim())
121
+ .join(", ");
118
122
  return (
119
123
  <>
120
124
  <Paragraph color="grey.700">{address.Street}</Paragraph>
121
125
  <Paragraph color="grey.700">{address.City}</Paragraph>
122
- <Paragraph color="grey.700">
123
- {address.State}, {address.ThreeLetterISORegionName}
124
- </Paragraph>
126
+ {stateRegion && <Paragraph color="grey.700">{stateRegion}</Paragraph>}
125
127
  <Paragraph color="grey.700">{address.ZipCode}</Paragraph>
126
128
  <Paragraph color="grey.700">{address.Phone}</Paragraph>
127
129
  </>
@@ -14,9 +14,9 @@ import DialogContent from "@mui/material/DialogContent";
14
14
  import Grid from "@mui/material/Grid2";
15
15
  import TextField from "@mui/material/TextField";
16
16
  import { merge } from "lodash";
17
+ import { useTranslations } from "next-intl";
17
18
  import React from "react";
18
19
  import { Controller, type UseFormReturn, useForm } from "react-hook-form";
19
- import { useTranslations } from "use-intl";
20
20
  import z from "zod";
21
21
 
22
22
  type AddressFormInput = {
@@ -117,7 +117,9 @@ OrderedProducts.Comment = ({ line }: LineProps) => {
117
117
  if (!line.Comment) return null;
118
118
  return (
119
119
  <Paragraph mt={1}>
120
- <H6 sx={{ display: "inline" }}>{t("commentLabel")}: </H6>
120
+ <Span sx={{ display: "inline", fontSize: 14, fontWeight: 600 }}>
121
+ {t("commentLabel")}:
122
+ </Span>
121
123
  {line.Comment}
122
124
  </Paragraph>
123
125
  );
@@ -56,7 +56,7 @@ const initialSearchArea: SearchArea = {
56
56
  * @param {Function} props.handleAddToCart - Function to handle adding the product to cart
57
57
  * @param {number} props.productId - ID of the product being searched
58
58
  *
59
- * @returns {JSX.Element} - Rendered modal component that displays available stores with inventory information
59
+ * @returns {React.ReactElement} - Rendered modal component that displays available stores with inventory information
60
60
  *
61
61
  * @example
62
62
  * <FindInStoreModal
@@ -71,7 +71,7 @@ const FindInStoreModal = ({
71
71
  open,
72
72
  handleAddToCart,
73
73
  productId,
74
- }: Props): JSX.Element => {
74
+ }: Props): React.ReactElement => {
75
75
  const t = useTranslations("productDetail");
76
76
  const [hideOutOfStock, setHideOutOfStock] = React.useState(false);
77
77
  const { data: channelConfig } = getChannelConfiguration.useSuspenseData();
@@ -154,7 +154,7 @@ const FindInStoreModal = ({
154
154
  const _stores = Object.values(stores);
155
155
  if (hideOutOfStock) {
156
156
  return _stores.filter(
157
- (store) => !isOutOfStock(store.location.OrgUnitNumber || ""),
157
+ (store) => !isOutOfStock(store?.location?.OrgUnitNumber || ""),
158
158
  );
159
159
  }
160
160
  return _stores;
@@ -205,7 +205,7 @@ const FindInStoreModal = ({
205
205
  address={location.Address || ""}
206
206
  actionArea={renderActionArea(
207
207
  location.OrgUnitNumber || "",
208
- pickupOptions || [],
208
+ (pickupOptions || []) as DeliveryOption[],
209
209
  inventoryRecord?.TotalAvailableInventoryLevelLabel || "",
210
210
  )}
211
211
  openFrom={location.OpenFrom || 0}
@@ -31,7 +31,7 @@ type Props = {
31
31
  * @param {boolean} props.isOutOfStock - Flag indicating if the product is out of stock at this location
32
32
  * @param {string} props.inventoryLabel - Label to display when product is out of stock
33
33
  *
34
- * @returns {JSX.Element} The rendered pickup option selection component
34
+ * @returns {React.ReactElement} The rendered pickup option selection component
35
35
  *
36
36
  * @example
37
37
  * <PickupOptionSelect
@@ -48,7 +48,7 @@ const PickupOptionSelect = ({
48
48
  onPickupClick,
49
49
  isOutOfStock,
50
50
  inventoryLabel,
51
- }: Props): JSX.Element => {
51
+ }: Props): React.ReactElement => {
52
52
  const [selectedOption, setSelectedOption] =
53
53
  React.useState<DeliveryOption | null>(pickupOptions[0] || null);
54
54
  const handlePickupOptionChange = (event: SelectChangeEvent) => {
@@ -19,14 +19,14 @@ type Props = {
19
19
  * @param {Object} props - Component props
20
20
  * @param {Function} props.handleSearch - Callback function that handles the search action with the search term
21
21
  *
22
- * @returns {JSX.Element} A form with a search input field that triggers the search on submit or Enter key
22
+ * @returns {React.ReactElement} A form with a search input field that triggers the search on submit or Enter key
23
23
  *
24
24
  * @example
25
25
  * ```tsx
26
26
  * <SearchHeader handleSearch={(term) => console.log(`Searching for ${term}`)} />
27
27
  * ```
28
28
  */
29
- const SearchHeader = ({ handleSearch }: Props): JSX.Element => {
29
+ const SearchHeader = ({ handleSearch }: Props): React.ReactElement => {
30
30
  const [searchTerm, setSearchTerm] = React.useState<string>("");
31
31
  const t = useTranslations("productDetail.Bopis");
32
32
  const renderSearchAdornment = () => (
@@ -1,4 +1,7 @@
1
- import type { ProductDetails } from "@evenicanpm/storefront-core/src/api-manager/schemas/product.schema";
1
+ import type {
2
+ ProductDetails,
3
+ ProductPrice,
4
+ } from "@evenicanpm/storefront-core/src/api-manager/schemas/product.schema";
2
5
  import type { SelectedDimensions } from "@evenicanpm/storefront-core/src/hooks/use-variants";
3
6
  import type {
4
7
  ProductDimensionValueInventoryAvailability,
@@ -44,8 +47,10 @@ export interface ProductImagesContext {
44
47
  export interface ProductInfoContext {
45
48
  /** Does the product have variants? If not we do not verify dimensions before adding to cart etc. **/
46
49
  hasVariants: boolean;
47
- /** **/
48
- inventoryPending: boolean;
50
+ /** Combined loading state for price and inventory */
51
+ isProductDetailsPending: boolean;
52
+ /** */
53
+ productPrices: ProductPrice[] | undefined;
49
54
  /** */
50
55
  shippingInventoryData: ProductWarehouseInventoryInformation | undefined;
51
56
  /** */
@@ -48,11 +48,17 @@ export interface Props {
48
48
  */
49
49
  const ProductInfo = ({ children }: Props) => {
50
50
  const [showDimensionError, setShowDimensionError] = useState(false);
51
- const { masterProduct, selectedVariant } = useProductIntro();
51
+ const {
52
+ masterProduct,
53
+ selectedVariant,
54
+ variantPending,
55
+ allDimensionsSelected,
56
+ } = useProductIntro();
52
57
 
53
58
  const hasVariants = masterProduct?.Dimensions?.length !== 0;
54
59
 
55
60
  const id = hasVariants ? selectedVariant?.RecordId : masterProduct?.RecordId;
61
+ const productId = selectedVariant?.RecordId || masterProduct?.RecordId;
56
62
 
57
63
  const shippingInventorySearchCriteria =
58
64
  !hasVariants || selectedVariant
@@ -65,11 +71,23 @@ const ProductInfo = ({ children }: Props) => {
65
71
  enabled: !!shippingInventorySearchCriteria,
66
72
  });
67
73
 
74
+ const { data: productPrices, isPending: pricesPending } =
75
+ getProductPrices.useData({
76
+ productIds: [productId],
77
+ });
78
+
79
+ const isProductDetailsPending =
80
+ pricesPending ||
81
+ !productPrices ||
82
+ variantPending ||
83
+ (allDimensionsSelected && inventoryPending);
84
+
68
85
  return (
69
86
  <ProductInfoContext.Provider
70
87
  value={{
71
88
  hasVariants,
72
- inventoryPending,
89
+ isProductDetailsPending,
90
+ productPrices,
73
91
  shippingInventoryData,
74
92
  showDimensionError,
75
93
  setShowDimensionError,
@@ -153,23 +171,13 @@ ProductInfo.Dimensions = () => {
153
171
  };
154
172
 
155
173
  ProductInfo.Price = () => {
156
- const IntroCtx = useProductIntro();
157
- if (!IntroCtx)
158
- throw new Error(
159
- "ProductInfo.Inventory must be wrapped in ProductInfoContext and a ProductIntroContext",
160
- );
161
- const { masterProduct, selectedVariant } = IntroCtx;
162
174
  const { formatCurrency } = useCurrencyFormatter();
163
-
164
- const { data: productPrices, isPending: pricesPending } =
165
- getProductPrices.useData({
166
- productIds: [selectedVariant?.RecordId || masterProduct?.RecordId],
167
- });
175
+ const { isProductDetailsPending, productPrices } = useProductInfo();
168
176
 
169
177
  return (
170
178
  <Box pt={1}>
171
179
  <H2 color="primary.main" mb={0.5} lineHeight="1">
172
- {pricesPending || !productPrices ? (
180
+ {isProductDetailsPending ? (
173
181
  <CircularProgress size={20} />
174
182
  ) : (
175
183
  formatCurrency(productPrices?.[0]?.CustomerContextualPrice || 0)
@@ -190,8 +198,8 @@ ProductInfo.Inventory = () => {
190
198
  throw new Error(
191
199
  "ProductInfo.Inventory must be wrapped in ProductInfoContext and a ProductIntroContext",
192
200
  );
193
- const { variantPending, allDimensionsSelected } = IntroCtx;
194
- const { shippingInventoryData, inventoryPending } = InfoCtx;
201
+ const { allDimensionsSelected } = IntroCtx;
202
+ const { shippingInventoryData, isProductDetailsPending } = InfoCtx;
195
203
  if (!allDimensionsSelected)
196
204
  return (
197
205
  <FlexBox alignItems="center" gap={1}>
@@ -209,9 +217,7 @@ ProductInfo.Inventory = () => {
209
217
  <FlexBox alignItems="center" gap={1}>
210
218
  {/* INVENTORY */}
211
219
  <Box sx={{ my: 3 }}>
212
- {inventoryPending || variantPending ? (
213
- <CircularProgress size={20} />
214
- ) : (
220
+ {!isProductDetailsPending && (
215
221
  <Typography variant="body2" color="text.secondary" fontWeight={600}>
216
222
  {getInventoryLabel(shippingInventoryData)}
217
223
  </Typography>
@@ -15,6 +15,7 @@ import useProductList from "@evenicanpm/storefront-core/src/pages/product-list/u
15
15
  import generateBreadcrumbs from "@evenicanpm/storefront-core/src/pages/product-list/utils/generate-breadcrumbs";
16
16
  import { searchCategory } from "@evenicanpm/storefront-core/src/pages/product-list/utils/search-for-category";
17
17
  import { usePreviousRefiners } from "@evenicanpm/storefront-core/src/pages/product-list/utils/use-previous-refiners";
18
+ import type { ProductSearchResult } from "@msdyn365-commerce/retail-proxy";
18
19
  import { Box, CircularProgress, Paper, Typography } from "@mui/material";
19
20
  import Grid from "@mui/material/Grid2";
20
21
  import { useLocale, useTranslations } from "next-intl";
@@ -149,7 +150,7 @@ ProductListView.Pagination = function Pager() {
149
150
  ProductListView.Results = function Results() {
150
151
  const t = useTranslations("ProductList");
151
152
  const { productData, isLoading, view, productPrices } = useProductList();
152
- const products = productData?.products ?? [];
153
+ const products = (productData?.products ?? []) as ProductSearchResult[];
153
154
  const paginationNode = <ProductListView.Pagination />;
154
155
 
155
156
  if (isLoading) {
@@ -1,9 +1,9 @@
1
1
  "use client";
2
+ import type { CmsNavItem } from "@evenicanpm/cms/src/content/navigation/types";
2
3
  import type { CategoryHierarchy } from "@evenicanpm/storefront-core/src/lib/category-helpers";
3
4
  import { createLink } from "@evenicanpm/storefront-core/src/providers/nav-provider/utils/createLink";
4
5
  import { useTranslations } from "next-intl";
5
6
  import { createContext, type PropsWithChildren, useState } from "react";
6
- import type { CmsNavItem } from "@/cms/interfaces/navigation";
7
7
 
8
8
  type NavContextType = {
9
9
  cmsNav: CmsNavItem[];
@@ -1,4 +1,4 @@
1
- import type { CmsNavItem } from "@evenicanpm/storefront-core/src/cms/interfaces/navigation";
1
+ import type { CmsNavItem } from "@evenicanpm/cms/src/content/navigation/types";
2
2
 
3
3
  export const createLink = (name: string, path: string): CmsNavItem => {
4
4
  return {
package/tsconfig.json CHANGED
@@ -11,9 +11,8 @@
11
11
  "resolveJsonModule": true,
12
12
  "outDir": "./dist",
13
13
  "jsx": "preserve",
14
- "typeRoots": ["../../../../storefront/node_modules/@types"],
14
+ "baseUrl": ".",
15
15
  "paths": {
16
- "*": ["../../../../storefront/node_modules/*"],
17
16
  "@evenicanpm/storefront-core/src/*": ["./src/*"],
18
17
  "@evenicanpm/storefront-core/src/lib/*": ["src/lib/*"],
19
18
  "@evenicanpm/storefront-core/src/api-manager/*": ["src/api-manager/*"],
@@ -1,11 +0,0 @@
1
- import type { CountryRegionInfo } from "@msdyn365-commerce/retail-proxy";
2
-
3
- export const countries: CountryRegionInfo[] = [
4
- { ShortName: "Canada", CountryRegionId: "CA", ISOCode: "CA" },
5
- {
6
- ShortName: "United States",
7
- CountryRegionId: "US",
8
- ISOCode: "US",
9
- },
10
- { ShortName: "Mexico", CountryRegionId: "MX", ISOCode: "MX" },
11
- ];
@@ -1,68 +0,0 @@
1
- import { queryClient } from "./query-client";
2
-
3
- type MutationInput = {
4
- cartLine?: Array<{ Quantity?: number }>;
5
- } & Record<string, unknown>;
6
-
7
- type CartMockData = {
8
- CartLines: Array<Record<string, unknown>>;
9
- } & Record<string, unknown>;
10
-
11
- /**
12
- *
13
- * Mocking up at the react-query level because our components
14
- * rely on react-query for triggering re-renders and state subscriptions.
15
- *
16
- */
17
- export const useCreateMutation = (apiManagerPath: readonly unknown[]) => {
18
- switch (apiManagerPath[1]) {
19
- case "updateCartLines":
20
- return {
21
- mutate: (input: MutationInput) => {
22
- console.log(input);
23
- queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
24
- ...oldData,
25
- CartLines: [
26
- {
27
- ...oldData.CartLines[0],
28
- Quantity: input.cartLine?.[0]?.Quantity,
29
- },
30
- ],
31
- }));
32
- },
33
- };
34
- case "deleteCartLine":
35
- return {
36
- mutate: (input: MutationInput) => {
37
- console.log(input);
38
- queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
39
- ...oldData,
40
- CartLines: [],
41
- }));
42
- },
43
- };
44
- case "addToCart":
45
- return {
46
- mutate: (input: MutationInput) => {
47
- console.log(input);
48
- queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
49
- ...oldData,
50
- CartLines: [
51
- {
52
- ProductId: 1,
53
- Quantity: 1,
54
- },
55
- ],
56
- }));
57
- },
58
- };
59
- case "sessionInit":
60
- return { mutateAsync: () => {} };
61
- default:
62
- return {
63
- mutate: () => {
64
- console.log("No Mock Implemented");
65
- },
66
- };
67
- }
68
- };
@@ -1,94 +0,0 @@
1
- import { useQuery } from "@tanstack/react-query";
2
- import categories from "../__mocks__/data/categories.json";
3
- import { data as products } from "../__mocks__/products";
4
- import wishlists from "../__mocks__/wishlists.json";
5
-
6
- const cartBase = {
7
- Id: "cart-123",
8
- Version: 1,
9
- CartLines: [
10
- {
11
- LineId: "line-1",
12
- ProductId: 1,
13
- Quantity: 0,
14
- },
15
- ],
16
- };
17
- const cart = cartBase;
18
-
19
- const shippingInventoryData = {
20
- AggregatedProductInventoryAvailabilities: [
21
- {
22
- TotalAvailableInventoryLevelCode: "AVAIL",
23
- },
24
- ],
25
- };
26
-
27
- // Need to mock create query as fn() and then
28
- // mock the return value in the respective storeies
29
- // files, per component
30
- export const createQuery = (apiManagerPath: string[]) => {
31
- console.log(apiManagerPath);
32
- if (apiManagerPath[0] === "cart")
33
- return {
34
- useData: () => useQuery({ queryKey: ["cart"], queryFn: () => cart }),
35
- };
36
- if (apiManagerPath[1] === "getInventory") {
37
- return {
38
- useData: () =>
39
- useQuery({
40
- queryKey: ["cart", "getInventory"],
41
- queryFn: () => shippingInventoryData,
42
- }),
43
- };
44
- }
45
- if (apiManagerPath[1] === "searchProducts") {
46
- console.log("here are the products");
47
- console.log(products.products.length);
48
- return {
49
- useData: () =>
50
- useQuery({
51
- queryKey: ["product", "searchProducts"],
52
- queryFn: () => Promise.resolve(products.products),
53
- }),
54
- useSuspenseData: () =>
55
- useQuery({
56
- queryKey: ["product", "searchProducts"],
57
- queryFn: () => Promise.resolve(products.products),
58
- }),
59
- };
60
- }
61
- if (apiManagerPath[1] === "getWishlists") {
62
- return {
63
- useData: () =>
64
- useQuery({
65
- queryKey: ["wishlists"],
66
- queryFn: () => wishlists,
67
- }),
68
- useSuspenseData: () =>
69
- useQuery({
70
- queryKey: ["wishlists"],
71
- queryFn: () => wishlists,
72
- }),
73
- };
74
- }
75
- if (apiManagerPath[1] === "getCategories") {
76
- console.log("Here i am!");
77
- return {
78
- useData: () =>
79
- useQuery({
80
- queryKey: ["categories", "getCategories"],
81
- queryFn: () => categories,
82
- }),
83
- useSuspenseData: () =>
84
- useQuery({
85
- queryKey: ["cateogires", "getCategories"],
86
- queryFn: () => categories,
87
- }),
88
- };
89
- }
90
- };
91
-
92
- export const createApiPath = (key: string, method: string) => {
93
- return [key, method];
94
- };