@evenicanpm/storefront-core 1.0.0-alpha → 1.0.1

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 (196) hide show
  1. package/package.json +3 -2
  2. package/src/api-manager/api-manager.config.json +1 -1
  3. package/src/api-manager/datasources/d365/d365-address.datasource.ts +4 -4
  4. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +100 -7
  5. package/src/api-manager/datasources/d365/d365-product.datasource.ts +39 -17
  6. package/src/api-manager/datasources/d365/d365-user.datasource.ts +220 -68
  7. package/src/api-manager/datasources/d365/utils/decode-jwt.ts +2 -2
  8. package/src/api-manager/datasources/e4/e4-cart.datasource.ts +6 -0
  9. package/src/api-manager/datasources/e4/e4-product.datasource.ts +22 -2
  10. package/src/api-manager/datasources/e4/e4.datasource.ts +1 -1
  11. package/src/api-manager/datasources/e4/e4.remaps.ts +24 -0
  12. package/src/api-manager/datasources/e4/e4.translator.ts +19 -1
  13. package/src/api-manager/datasources/e4/{e4-user.datasource.ts → user/e4-user.datasource.ts} +41 -19
  14. package/src/api-manager/datasources/e4/user/e4-user.remaps.ts +4 -0
  15. package/src/api-manager/datasources/e4/user/e4-user.translator.ts +10 -0
  16. package/src/api-manager/index.ts +77 -15
  17. package/src/api-manager/lib/get-graphql-client.ts +7 -7
  18. package/src/api-manager/schemas/address.schema.ts +1 -19
  19. package/src/api-manager/schemas/cart.schema.ts +18 -0
  20. package/src/api-manager/schemas/product.schema.ts +8 -1
  21. package/src/api-manager/schemas/user.schema.ts +52 -1
  22. package/src/api-manager/services/address/queries/get-states.ts +1 -1
  23. package/src/api-manager/services/cart/mutations/checkout.ts +16 -1
  24. package/src/api-manager/services/cart/queries/get-cart-lines-inventory.ts +15 -0
  25. package/src/api-manager/services/create-extension-query.ts +26 -0
  26. package/src/api-manager/services/create-mutation.ts +5 -1
  27. package/src/api-manager/services/create-query.ts +10 -4
  28. package/src/api-manager/services/organization/queries/get-channel-configuration.ts +1 -1
  29. package/src/api-manager/services/product/queries/get-product-prices.ts +16 -0
  30. package/src/api-manager/services/product/queries/search-products.ts +1 -0
  31. package/src/api-manager/services/user/mutations/add-to-wishlist.ts +24 -0
  32. package/src/api-manager/services/user/mutations/copy-cart-to-wishlist.ts +23 -0
  33. package/src/api-manager/services/user/mutations/create-wishlist.ts +4 -4
  34. package/src/api-manager/services/user/mutations/delete-wishlist.ts +4 -3
  35. package/src/api-manager/services/user/mutations/remove-from-wishlist.ts +21 -0
  36. package/src/api-manager/services/user/mutations/update-wishlist.ts +23 -0
  37. package/src/api-manager/services/user/queries/get-wishlist-details.ts +11 -4
  38. package/src/api-manager/services/user/queries/get-wishlists.ts +4 -3
  39. package/src/api-manager/types/Datasource.ts +42 -14
  40. package/src/auth/auth-options.ts +2 -2
  41. package/src/auth/msal.ts +9 -9
  42. package/src/auth/next-auth-cookie-manager.ts +5 -3
  43. package/src/auth/providers/aadb2c-provider.ts +3 -4
  44. package/src/cms/endpoints.ts +2 -3
  45. package/src/components/Blocks/Components/ProductCarousel/index.tsx +6 -6
  46. package/src/components/_components/LazyImage.tsx +5 -2
  47. package/src/components/_components/header/components/user.tsx +1 -2
  48. package/src/components/_components/navbar/category-based-menu/components/categories.tsx +1 -1
  49. package/src/components/_components/navbar/mega-menu/mega-menu.tsx +1 -1
  50. package/src/components/_components/navbar/nav-list/nav-item-child.tsx +2 -2
  51. package/src/components/_components/product-cards/product-card-1/components/quantity-buttons.tsx +17 -8
  52. package/src/components/_components/product-cards/product-card-1/product-card-1-wrapper.tsx +304 -0
  53. package/src/components/_components/product-cards/product-card-1/product-card.tsx +16 -55
  54. package/src/components/_components/product-cards/product-card-8/product-card.tsx +80 -60
  55. package/src/components/_components/product-cards/product-card-8/styles/index.ts +10 -0
  56. package/src/components/_components/product-cards/product-card-wishlist/components/hover-actions.tsx +37 -0
  57. package/src/components/_components/product-cards/product-card-wishlist/index.ts +1 -0
  58. package/src/components/_components/product-cards/product-card-wishlist/product-card.tsx +118 -0
  59. package/src/components/_components/product-cards/product-card-wishlist/styles/index.ts +51 -0
  60. package/src/components/_components/product-dimensions/product-dimensions.tsx +114 -0
  61. package/src/components/_components/product-quantity-buttons/product-quantity-buttons.tsx +88 -0
  62. package/src/components/_components/products-view/product-view-dialog.tsx +48 -10
  63. package/src/components/_components/products-view/products-grid-view.tsx +31 -17
  64. package/src/components/_components/products-view/products-list-view.tsx +30 -16
  65. package/src/components/_components/search-box/__test__/search-input-category.test.tsx +110 -0
  66. package/src/components/_components/search-box/__test__/search-input.test.tsx +137 -0
  67. package/src/components/_components/search-box/__test__/search-results.json +165 -0
  68. package/src/components/_components/search-box/components/search-result.tsx +66 -12
  69. package/src/components/_components/search-box/search-input-category.tsx +135 -32
  70. package/src/components/_components/search-box/styles/index.ts +2 -0
  71. package/src/components/_components/section-header/section-creator.tsx +1 -0
  72. package/src/components/_components/section-header/section-header.tsx +1 -1
  73. package/src/components/_components/settings/setting.tsx +1 -1
  74. package/src/components/_components/wishlist/add-to-wishlist-dialog.tsx +161 -0
  75. package/src/components/_components/wishlist/components/create-new-list.tsx +167 -0
  76. package/src/components/_components/wishlist/components/create-wishlist-button.tsx +40 -0
  77. package/src/components/_components/wishlist/components/dashboard-header.tsx +123 -0
  78. package/src/components/_components/wishlist/components/navigation.tsx +87 -0
  79. package/src/components/_components/wishlist/components/no-records.tsx +20 -0
  80. package/src/components/_components/wishlist/components/wishlist-modal-item.tsx +66 -0
  81. package/src/components/_components/wishlist/remove-from-wishlist-dialog.tsx +174 -0
  82. package/src/components/_components/wishlist/styles.ts +61 -0
  83. package/src/components/_components/wishlist-modal/TODO.md +10 -0
  84. package/src/components/_components/wishlist-modal/add-to-wishlist-button.tsx +68 -0
  85. package/src/components/_components/wishlist-modal/add-to-wishlist-dialog.tsx +161 -0
  86. package/src/components/_components/wishlist-modal/remove-from-wishlist-dialog.tsx +174 -0
  87. package/src/components/_components/wishlist-modal/wishlist-modal-item.tsx +65 -0
  88. package/src/components/countries-input.tsx +7 -12
  89. package/src/hooks/use-cart.tsx +82 -0
  90. package/src/hooks/use-variants.tsx +85 -0
  91. package/src/lib/cart-cookie-handler-constants.ts +8 -0
  92. package/src/lib/cart-cookie-handler.ts +27 -5
  93. package/src/lib/create-graphql-client.ts +1 -2
  94. package/src/lib/product-list-types.ts +41 -0
  95. package/src/pages/account/addresses/address-form.tsx +289 -0
  96. package/src/pages/account/addresses/address-item.tsx +104 -0
  97. package/src/pages/account/dashboard-header.tsx +123 -0
  98. package/src/pages/account/navigation.tsx +84 -0
  99. package/src/pages/account/no-records.tsx +20 -0
  100. package/src/pages/account/orders/icons/delivery.tsx +14 -0
  101. package/src/pages/account/orders/icons/package-box.tsx +13 -0
  102. package/src/pages/account/orders/icons/truck-filled.tsx +14 -0
  103. package/src/pages/account/orders/order-progress.tsx +111 -0
  104. package/src/pages/account/orders/order-row.tsx +61 -0
  105. package/src/pages/account/orders/order-summary.tsx +83 -0
  106. package/src/pages/account/orders/ordered-products.tsx +114 -0
  107. package/src/pages/account/profile/profile-button.test.tsx +59 -0
  108. package/src/pages/account/profile/profile-button.tsx +50 -0
  109. package/src/pages/account/profile/user-info.tsx +59 -0
  110. package/src/pages/account/styles.ts +32 -0
  111. package/src/pages/account/table-row.tsx +19 -0
  112. package/src/pages/account/wishlist/create-new-list.tsx +167 -0
  113. package/src/pages/account/wishlist/create-wishlist-button.tsx +40 -0
  114. package/src/pages/account/wishlist/wishlist-item.tsx +82 -0
  115. package/src/pages/blog/pagination.tsx +38 -0
  116. package/src/pages/cart/cart-item.tsx +312 -0
  117. package/src/pages/cart/checkout-form.tsx +122 -0
  118. package/src/pages/cart/coupon-entry.tsx +90 -0
  119. package/src/pages/cart/estimate-shipping.tsx +183 -0
  120. package/src/pages/cart/wrapper.tsx +30 -0
  121. package/src/pages/checkout/checkout-alt-form/address-card.tsx +73 -0
  122. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +133 -0
  123. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +120 -0
  124. package/src/pages/checkout/checkout-alt-form/customer-information.tsx +94 -0
  125. package/src/pages/checkout/checkout-alt-form/delivery-address.tsx +170 -0
  126. package/src/pages/checkout/checkout-alt-form/delivery-date.tsx +114 -0
  127. package/src/pages/checkout/checkout-alt-form/delivery-options.tsx +186 -0
  128. package/src/pages/checkout/checkout-alt-form/edit-address-form.tsx +130 -0
  129. package/src/pages/checkout/checkout-alt-form/heading.tsx +50 -0
  130. package/src/pages/checkout/checkout-alt-form/index.ts +1 -0
  131. package/src/pages/checkout/checkout-alt-form/new-address-form.tsx +294 -0
  132. package/src/pages/checkout/checkout-alt-form/node.tsx +9 -0
  133. package/src/pages/checkout/checkout-alt-form/payment-details.tsx +344 -0
  134. package/src/pages/checkout/checkout-alt-form/safe-card-preview-data.tsx +36 -0
  135. package/src/pages/checkout/checkout-alt-form/types.ts +20 -0
  136. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +39 -0
  137. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +40 -0
  138. package/src/pages/checkout/checkout-alt-summary/index.ts +1 -0
  139. package/src/pages/checkout/checkout-alt-summary/list-item.tsx +31 -0
  140. package/src/pages/confirmation/address.tsx +22 -0
  141. package/src/pages/confirmation/confirmation-summary.tsx +52 -0
  142. package/src/pages/confirmation/ordered-products.tsx +108 -0
  143. package/src/pages/product-details/available-shops.tsx +48 -0
  144. package/src/pages/product-details/bopis/find-in-store-button.tsx +63 -0
  145. package/src/pages/product-details/bopis/find-in-store-modal.tsx +257 -0
  146. package/src/pages/product-details/bopis/pickup-option-select.tsx +127 -0
  147. package/src/pages/product-details/bopis/search-header.tsx +69 -0
  148. package/src/pages/product-details/checkbox-label.tsx +20 -0
  149. package/src/pages/product-details/currency.ts +64 -0
  150. package/src/pages/product-details/frequently-bought.tsx +90 -0
  151. package/src/pages/product-details/frequently-product-card.tsx +60 -0
  152. package/src/pages/product-details/product-comment.tsx +44 -0
  153. package/src/pages/product-details/product-description.tsx +22 -0
  154. package/src/pages/product-details/product-filter-card.tsx +257 -0
  155. package/src/pages/product-details/product-intro/product-intro-images.tsx +87 -0
  156. package/src/pages/product-details/product-intro/product-intro.tsx +250 -0
  157. package/src/pages/product-details/product-review.tsx +131 -0
  158. package/src/pages/product-details/product-tabs.tsx +51 -0
  159. package/src/pages/product-details/related-products.tsx +42 -0
  160. package/src/pages/product-details/types.ts +11 -0
  161. package/src/pages/product-list/breadcrumbs.tsx +39 -0
  162. package/src/pages/product-list/checkbox-label.tsx +20 -0
  163. package/src/pages/product-list/facet-group.tsx +125 -0
  164. package/src/pages/product-list/facet.tsx +36 -0
  165. package/src/pages/product-list/list-filter.tsx +40 -0
  166. package/src/pages/product-list/pagination.tsx +80 -0
  167. package/src/pages/product-list/product-list-context.tsx +302 -0
  168. package/src/pages/product-list/product-list-state.ts +187 -0
  169. package/src/pages/product-list/product-rating.tsx +16 -0
  170. package/src/pages/product-list/quick-view-dialog-content.tsx +205 -0
  171. package/src/pages/product-list/quick-view-dialog.tsx +54 -0
  172. package/src/pages/product-list/range-filter.tsx +125 -0
  173. package/src/pages/product-list/scrollbar.tsx +38 -0
  174. package/src/pages/product-list/search-bar.tsx +162 -0
  175. package/src/pages/product-list/selected-facets.tsx +80 -0
  176. package/src/pages/product-list/side-nav.tsx +49 -0
  177. package/src/pages/product-list/swatch.tsx +80 -0
  178. package/src/pages/product-list/types.ts +18 -0
  179. package/src/pages/product-list/use-product-list.ts +12 -0
  180. package/src/pages/product-list/useCategoryFilter.tsx +26 -0
  181. package/src/pages/product-list/utils/facet-helpers.ts +5 -0
  182. package/src/pages/product-list/utils/generate-breadcrumbs.ts +30 -0
  183. package/src/pages/product-list/utils/getCategoryFilterFromSlug.ts +153 -0
  184. package/src/pages/product-list/utils/product-list-helper.ts +111 -0
  185. package/src/pages/product-list/utils/product-list-types.ts +41 -0
  186. package/src/pages/product-list/utils/search-for-category.ts +76 -0
  187. package/src/pages/product-list/utils/sort-options.ts +44 -0
  188. package/src/pages/product-list/utils/use-previous-refiners.ts +14 -0
  189. package/src/pages/quickorder/order-upload.tsx +150 -0
  190. package/src/pages/quickorder/quick-order-form.tsx +343 -0
  191. package/src/pages/quickorder/quick-order-row.tsx +144 -0
  192. package/tsconfig.json +1 -0
  193. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +0 -43515
  194. package/src/config.ts +0 -47
  195. /package/src/{components/_components/hooks/useOverflowDetect.ts → hooks/use-overflow-detect.ts} +0 -0
  196. /package/src/{components/_components/hooks/useSettings.ts → hooks/use-settings.ts} +0 -0
@@ -12,7 +12,8 @@ import {
12
12
  import * as translator from "./e4.translator";
13
13
  import getGraphqlClient from "../../lib/get-graphql-client";
14
14
  import { ProductApi } from "../../types/Datasource";
15
- import { generateCategoryFilterString } from "./e4.translator";
15
+ // See TODO below
16
+ //import { generateCategoryFilterString } from "./e4.translator";
16
17
 
17
18
  const product: ProductApi = {
18
19
  async getProductById(
@@ -61,7 +62,9 @@ const product: ProductApi = {
61
62
  const { data, errors } = await e4Api.productTypeaheadSearchByCategory({
62
63
  input: {
63
64
  search: input.searchText,
64
- categoryId: await generateCategoryFilterString(input.category ?? 0),
65
+ // TODO: investigate why this was added..
66
+ // it's throwing a type error.
67
+ //categoryId: await generateCategoryFilterString(input.category ?? 0),
65
68
  top: 10,
66
69
  skip: 0,
67
70
  },
@@ -152,6 +155,23 @@ const product: ProductApi = {
152
155
  ): Promise<ProductSearchResult[]> {
153
156
  throw new Error(`Function not implemented.${input}`);
154
157
  },
158
+ async getProductPrices(
159
+ input: productSchema.ProductPriceInput,
160
+ ): Promise<productSchema.ProductPrice[]> {
161
+ const e4Api = await getGraphqlClient();
162
+ const { data, errors } = await e4Api.GetProductsByIds({
163
+ ids: input.productIds?.map(String),
164
+ });
165
+
166
+ if (errors) {
167
+ throw new Error("Error getting products");
168
+ }
169
+
170
+ const translatedData = translator.productPricesTransformValidate(
171
+ data?.getProductsByIds as Product[],
172
+ );
173
+ return translatedData;
174
+ },
155
175
  };
156
176
 
157
177
  export default product;
@@ -3,7 +3,7 @@ import getGraphqlClient from "../../lib/get-graphql-client";
3
3
  import { Category as E4Category } from "./graphqlRequestSdk";
4
4
  import * as translator from "./e4.translator";
5
5
  // Data source files
6
- import user from "./e4-user.datasource";
6
+ import user from "./user/e4-user.datasource";
7
7
  import session from "./e4-session.datasource";
8
8
  import { cart } from "./e4-cart.datasource";
9
9
  import order from "./e4-order.datasource";
@@ -63,6 +63,30 @@ export const productMap = {
63
63
  },
64
64
  };
65
65
 
66
+ export const productPricesMap = {
67
+ id: [
68
+ {
69
+ key: "ProductId",
70
+ transform: (id: string) => {
71
+ if (id !== null) return parseInt(id);
72
+ },
73
+ },
74
+ {
75
+ key: "ListingId",
76
+ transform: (id: string) => {
77
+ if (id !== null) return parseInt(id);
78
+ },
79
+ },
80
+ ],
81
+ price: [
82
+ { key: "Price", default: 0 },
83
+ { key: "BasePrice", default: 0 },
84
+ { key: "AdjustedPrice", default: 0 },
85
+ { key: "CustomerContextualPrice", default: 0 },
86
+ ],
87
+ sku: "ItemId",
88
+ };
89
+
66
90
  export const productDetailsMap = {
67
91
  ...productMap,
68
92
  MasterProductId: "MasterProductId",
@@ -4,6 +4,7 @@ import {
4
4
  productSchema,
5
5
  ProductDetails,
6
6
  SearchByCriteriaInput,
7
+ ProductPrice,
7
8
  } from "@/api-manager/schemas/product.schema";
8
9
  import { Address } from "@/api-manager/schemas/address.schema";
9
10
  import { User } from "@/api-manager/schemas/user.schema";
@@ -31,12 +32,14 @@ import {
31
32
  cartMap,
32
33
  productDetailsMap,
33
34
  productMap,
35
+ productPricesMap,
34
36
  addressReverseMap,
35
37
  countriesMap,
36
38
  countryMap,
37
39
  deliveryOptionsMap,
38
40
  } from "./e4.remaps";
39
41
  import { CategoryHierarchy } from "@/lib/category-helpers";
42
+ import { productPriceSchema } from "@/api-manager/schemas/d365.schema";
40
43
 
41
44
  interface TransformedRefinerValue {
42
45
  RefinerRecordId: number;
@@ -338,6 +341,20 @@ export const productTransformValidate = (
338
341
  return translatedData;
339
342
  };
340
343
 
344
+ export const productPricesTransformValidate = (
345
+ products: Product[],
346
+ ): ProductPrice[] => {
347
+ const translatedData = products.map(
348
+ (p) => merge(p, productPricesMap) as ProductPrice,
349
+ );
350
+ try {
351
+ translatedData.map((pp: ProductPrice) => productPriceSchema.parse(pp));
352
+ } catch (e) {
353
+ console.warn("ProductPrice data does not match retail-proxy schema", e);
354
+ }
355
+ return translatedData;
356
+ };
357
+
341
358
  export const productWithCategoryTransformValidate = (
342
359
  productData: Product,
343
360
  ): ProductSearchResult => {
@@ -475,7 +492,8 @@ const getStateId = async (stateCode: string, countryId: string) => {
475
492
  } else {
476
493
  const e4Api = await getGraphqlClient();
477
494
  const { data } = await e4Api.e4Country({ id: countryId });
478
- countryCache.set(cacheKey, data?.country as Country);
495
+ country = data?.country as Country;
496
+ countryCache.set(cacheKey, country);
479
497
  }
480
498
 
481
499
  const state = country?.state?.find((s) => s?.code === stateCode);
@@ -1,11 +1,20 @@
1
- import getGraphqlClient from "../../lib/get-graphql-client";
2
- import { User, UserCounts } from "@/api-manager/schemas/user.schema";
3
- import * as translator from "./e4.translator";
4
- import { CreateWishListInput, WishList } from "./graphqlRequestSdk";
5
- import { userMap } from "./e4.remaps";
1
+ import getGraphqlClient from "../../../lib/get-graphql-client";
2
+ import {
3
+ CopyCartToWishlistInput,
4
+ User,
5
+ UserCounts,
6
+ Wishlist,
7
+ WishlistCreateInput,
8
+ WishlistDetailInput,
9
+ WishlistLinesInput,
10
+ } from "@/api-manager/schemas/user.schema";
11
+ import * as translator from "../e4.translator";
12
+ import { userMap } from "../e4.remaps";
6
13
  import { merge } from "object-mapper";
14
+ import { UserApi } from "@/api-manager/types/Datasource";
15
+ import { translateWishList } from "./e4-user.translator";
7
16
 
8
- const user = {
17
+ const user: UserApi = {
9
18
  async updateUser(user: User): Promise<User> {
10
19
  const e4Api = await getGraphqlClient(true);
11
20
 
@@ -46,22 +55,21 @@ const user = {
46
55
  addressCount: data?.getAddressCount ?? 0,
47
56
  };
48
57
  },
49
- async getWishlists(): Promise<WishList[]> {
58
+ async getWishlists(): Promise<Wishlist[]> {
50
59
  const e4Api = await getGraphqlClient(true);
51
60
 
52
61
  const { data, errors } = await e4Api.e4WishListListSM();
53
-
54
62
  if (errors) {
55
63
  throw new Error("Error getting wishlists");
56
64
  }
57
65
 
58
- return data?.wishlistList?.nodes ?? [];
66
+ return translateWishList(data);
59
67
  },
60
68
 
61
- async getWishlistDetails(id: string): Promise<WishList> {
69
+ async getWishlistDetails(input: WishlistDetailInput): Promise<Wishlist> {
62
70
  const e4Api = await getGraphqlClient(true);
63
71
 
64
- const { data, errors } = await e4Api.e4WishListReadMD({ id });
72
+ const { data, errors } = await e4Api.e4WishListReadMD({ id: input.Id });
65
73
 
66
74
  if (errors) {
67
75
  throw new Error("Error getting wishlist details");
@@ -79,16 +87,17 @@ const user = {
79
87
  edges: [],
80
88
  ...wishList.products,
81
89
  },
82
- } as unknown as WishList;
90
+ } as unknown as Wishlist;
83
91
  },
84
- async createWishlist(wishlist: CreateWishListInput): Promise<WishList> {
85
- if (!wishlist.name) {
92
+ async createWishlist(input: WishlistCreateInput): Promise<Wishlist> {
93
+ const { Name: name } = input;
94
+ if (name) {
86
95
  throw new Error("Wishlist name is required");
87
96
  }
88
97
 
89
98
  const e4Api = await getGraphqlClient(true);
90
99
 
91
- const { data, errors } = await e4Api.upsertWishlist({ wishlist });
100
+ const { data, errors } = await e4Api.upsertWishlist({ wishlist: { name } });
92
101
 
93
102
  if (errors) {
94
103
  throw new Error("Error creating wishlist");
@@ -100,18 +109,31 @@ const user = {
100
109
  throw new Error("No wishlist found");
101
110
  }
102
111
 
103
- return wishList as WishList;
112
+ return wishList as Wishlist;
104
113
  },
105
- async deleteWishlist(id: string): Promise<string> {
114
+ async deleteWishlist(input: WishlistDetailInput): Promise<void> {
106
115
  const e4Api = await getGraphqlClient(true);
107
116
 
108
- const { data, errors } = await e4Api.deleteWishList({ id });
117
+ const { data, errors } = await e4Api.deleteWishList({ id: input.Id });
109
118
 
110
119
  if (errors) {
111
120
  throw new Error("Error creating wishlist");
112
121
  }
122
+ },
123
+ async updateWishlist(input: WishlistLinesInput): Promise<Wishlist> {
124
+ throw new Error("updateWishlist not implemented");
125
+ },
126
+
127
+ async addProductsToWishlist(input: WishlistLinesInput): Promise<Wishlist> {
128
+ throw new Error("addProductsToWishlist not implemented");
129
+ },
130
+
131
+ async removeProductsFromWishlist(input: WishlistLinesInput): Promise<void> {
132
+ throw new Error("removeProductsFromWishlist not implemented");
133
+ },
113
134
 
114
- return data?.deleteWishList ?? "";
135
+ async copyCartToWishlist(input: CopyCartToWishlistInput): Promise<Wishlist> {
136
+ throw new Error("copyCartToWishlist not implemented");
115
137
  },
116
138
  };
117
139
 
@@ -0,0 +1,4 @@
1
+ export const wishListMap = {
2
+ "[].id": "[].Id",
3
+ "[].name": "[].Name",
4
+ };
@@ -0,0 +1,10 @@
1
+ import { E4WishListListSmQuery } from "../graphqlRequestSdk";
2
+ import { merge } from "object-mapper";
3
+ import { wishListMap } from "./e4-user.remaps";
4
+ import { Wishlist } from "@/api-manager/schemas/user.schema";
5
+
6
+ export const translateWishList = (data: E4WishListListSmQuery) => {
7
+ const e4WishList = data.wishlistList.nodes;
8
+ const translatedData = merge(e4WishList, wishListMap) as Wishlist[];
9
+ return translatedData;
10
+ };
@@ -4,6 +4,7 @@ import { D365Datasource } from "./datasources/d365/index";
4
4
  import rawConfig from "./api-manager.config.json";
5
5
  import { z } from "zod";
6
6
  import { getContextFromCookie } from "./datasources/d365/utils/get-context-cookie";
7
+
7
8
  type EntityNames = keyof DatasourceApis;
8
9
 
9
10
  interface Config {
@@ -18,9 +19,25 @@ const Config = z.object({
18
19
  overrides: z.record(z.string(), z.string()),
19
20
  });
20
21
 
22
+ const OVERRIDES_FILE = "./overrides.datasource.ts";
23
+ const CORE_DATASOURCES = [
24
+ "user",
25
+ "session",
26
+ "cart",
27
+ "product",
28
+ "categories",
29
+ "order",
30
+ "address",
31
+ "organization",
32
+ ];
21
33
  /**
22
34
  * Loads up all specified apis/datasources based on parsed config
23
- * and returns the ApiManager object.
35
+ * and returns the ApiManager object. Also handles "project" overrides
36
+ * by checking inside a specific folder for API's. Will grab default export
37
+ * from specified folder, validate the API schema and then prioritize returning
38
+ * that from api-manager.
39
+ *
40
+ * In this way, we have two levels of configuration of the api-manager.
24
41
  * @returns ApiManager object.
25
42
  */
26
43
  async function createApiManager() {
@@ -32,6 +49,26 @@ async function createApiManager() {
32
49
  }
33
50
  }
34
51
 
52
+ const storefrontOverrides: { [key: string]: any } = {};
53
+
54
+ // @ts-ignore
55
+ function importAll(r) {
56
+ r.keys().forEach(
57
+ (key: keyof typeof storefrontOverrides) =>
58
+ (storefrontOverrides[key] = r(key)),
59
+ );
60
+ }
61
+
62
+ // Arguments must be literals not variables.
63
+ importAll(
64
+ // @ts-ignore
65
+ require.context(
66
+ "@storefront/extensions/api-manager-overrides",
67
+ true,
68
+ /overrides\.datasource\.ts/,
69
+ ),
70
+ );
71
+
35
72
  const context = await getContextFromCookie();
36
73
  const datasourceMap: Record<string, Datasource> = {
37
74
  d365: new D365Datasource("d365", context),
@@ -46,26 +83,51 @@ async function createApiManager() {
46
83
  );
47
84
 
48
85
  const getDatasource = (entityName: EntityNames) => {
86
+ // Project-Level Overrides
87
+ let projectOverrides = {};
88
+ if (storefrontOverrides?.[OVERRIDES_FILE]?.["default"]?.[entityName]) {
89
+ projectOverrides =
90
+ storefrontOverrides[OVERRIDES_FILE]["default"][entityName];
91
+ }
92
+
93
+ // Config-Level Overrides
49
94
  const override = config?.overrides?.[entityName];
95
+
96
+ // Build the base entity from api-manager config settings
97
+ let baseEntity = {};
50
98
  if (override) {
51
- return (
99
+ baseEntity =
52
100
  secondaryDatasources.find((ds) => ds.name === override) ??
53
- primaryDatasource
54
- );
101
+ primaryDatasource;
102
+ } else {
103
+ baseEntity = primaryDatasource;
104
+ }
105
+
106
+ // Attach Project Overrides to Entity
107
+ for (const key in projectOverrides) {
108
+ baseEntity[entityName][key] = projectOverrides[key];
55
109
  }
56
- return primaryDatasource;
57
- };
58
110
 
59
- return {
60
- user: getDatasource("user").user,
61
- session: getDatasource("session").session,
62
- cart: getDatasource("cart").cart,
63
- product: getDatasource("product").product,
64
- categories: getDatasource("categories").categories,
65
- order: getDatasource("order").order,
66
- address: getDatasource("address").address,
67
- organization: getDatasource("organization").organization,
111
+ return baseEntity;
68
112
  };
113
+
114
+ const datasources = {};
115
+
116
+ CORE_DATASOURCES.forEach((entityName) => {
117
+ const datasource = getDatasource(entityName as EntityNames);
118
+ datasources[entityName] = datasource[entityName];
119
+ });
120
+
121
+ storefrontOverrides[OVERRIDES_FILE]?.default &&
122
+ Object.keys(storefrontOverrides[OVERRIDES_FILE]["default"]).forEach(
123
+ (key) => {
124
+ if (!CORE_DATASOURCES.includes(key)) {
125
+ datasources[key] =
126
+ storefrontOverrides[OVERRIDES_FILE]["default"][key];
127
+ }
128
+ },
129
+ );
130
+ return datasources;
69
131
  }
70
132
 
71
133
  export default createApiManager;
@@ -6,9 +6,6 @@ import { getServerSession } from "next-auth/next";
6
6
  // TODO: Get auth inside client packages
7
7
  import authOptions from "@/auth/auth-options";
8
8
  import { cookies } from "next/headers";
9
-
10
- const API_URL = process.env.REACT_APP_E4_API_ENDPOINT; //`https://api.e4.local/graphql`;
11
-
12
9
  /**
13
10
  * Initializes and returns a cached GraphQL client
14
11
  * @param withAuth (optional) - If true, includes the authorization token
@@ -41,10 +38,13 @@ const getGraphQLClient = async (withAuth = false) => {
41
38
 
42
39
  // Best Practice: Never reuse a GraphQL client that mutates headers across different requests in server environments.
43
40
  // When we resuse the client instance, sometimes the token is missing in e4 backend.
44
- const client = new GraphQLClient(API_URL, {
45
- headers,
46
- credentials: "include",
47
- });
41
+ const client = new GraphQLClient(
42
+ process.env.NEXT_PUBLIC_E4_API_ENDPOINT || "",
43
+ {
44
+ headers,
45
+ credentials: "include",
46
+ },
47
+ );
48
48
 
49
49
  client.setHeaders(headers);
50
50
  return getSdk(client);
@@ -9,25 +9,6 @@ export const addressTypeSchema = z
9
9
  })
10
10
  .optional();
11
11
 
12
- // export const addressSchema = z.object({
13
- // Id:z.string().optional(),
14
- // Name:z.string().optional(),
15
- // FirstName: z.string().optional(),
16
- // LastName: z.string().optional(),
17
- // IsPrimary:z.boolean().optional(),
18
- // AddressType: addressTypeSchema,
19
- // AddressLine1: z.string().optional(),
20
- // AddressLine2: z.string().optional(),
21
- // City: z.string().optional(),
22
- // StateId: z.string().optional(),
23
- // StateName: z.string().optional(),
24
- // CountryId: z.string().optional(),
25
- // CountryName: z.string().optional(),
26
- // CountryCode: z.string().optional(),
27
- // ZipCode: z.string().optional(),
28
- // Phone: z.string().optional(),
29
- // });
30
-
31
12
  const getCountriesInput = z.object({
32
13
  locale: z.string().optional(),
33
14
  });
@@ -40,6 +21,7 @@ const getDeliveryOptionsInput = z.object({
40
21
  shippingAddress: addressSchema,
41
22
  cartLineIds: z.array(z.string()),
42
23
  });
24
+
43
25
  export type GetCountriesInput = z.infer<typeof getCountriesInput>;
44
26
  export type GetStatesInput = z.infer<typeof getStatesInput>;
45
27
  export type GetDeliveryOptionsInput = z.infer<typeof getDeliveryOptionsInput>;
@@ -4,6 +4,7 @@ import {
4
4
  cartLineSchema,
5
5
  cartSchema,
6
6
  retrievePaymentResultSettingsSchema,
7
+ searchAreaSchema,
7
8
  simpleProductSchema,
8
9
  tokenizedPaymentCardSchema,
9
10
  } from "./d365.schema";
@@ -67,6 +68,20 @@ export const removeCartLineDeliveryModeInput = z.object({
67
68
  cart: extendedCartSchema.partial(),
68
69
  });
69
70
 
71
+ const SimpleCartLinesSearchCriteria = z.object({
72
+ productId: z.number(),
73
+ deliveryMode: z.string().optional(),
74
+ inventLocationId: z.string().optional(),
75
+ });
76
+
77
+ const InventorySearchCartLinesCriteriaInput = z.object({
78
+ searchCriteria: z.object({
79
+ cartLines: SimpleCartLinesSearchCriteria.array(),
80
+ searchArea: searchAreaSchema.optional(),
81
+ }),
82
+ allDimensionsSelected: z.boolean(),
83
+ });
84
+
70
85
  // Typescript Types
71
86
  // export type Cart = z.infer<typeof cartSchema>;
72
87
  // export type CartLine = z.infer<typeof cartLineSchema>;
@@ -90,3 +105,6 @@ export type RemoveCartLineDeliveryModeInput = z.infer<
90
105
  typeof removeCartLineDeliveryModeInput
91
106
  >;
92
107
  export type CheckoutInput = z.infer<typeof checkoutInput>;
108
+ export type InventorySearchCartLinesCriteriaInput = z.infer<
109
+ typeof InventorySearchCartLinesCriteriaInput
110
+ >;
@@ -6,6 +6,7 @@ import {
6
6
  productSearchResultSchema,
7
7
  queryResultSettingsSchema,
8
8
  searchAreaSchema,
9
+ productPriceSchema,
9
10
  } from "./d365.schema";
10
11
  import {
11
12
  AttributeValue,
@@ -58,7 +59,7 @@ export const productWCategorySchema = productSchema.extend({
58
59
  });
59
60
 
60
61
  export const getProductByIdInput = z.object({
61
- id: z.number().or(z.string()),
62
+ id: z.number().or(z.string()).or(z.undefined()),
62
63
  });
63
64
 
64
65
  export const getRelatedProductsInput = z.object({
@@ -117,6 +118,10 @@ const InventorySearchCriteriaInput = z.object({
117
118
  allDimensionsSelected: z.boolean(),
118
119
  });
119
120
 
121
+ const productPriceInput = z.object({
122
+ productIds: z.number().array(),
123
+ });
124
+
120
125
  // entities
121
126
  export type Product = z.infer<typeof productSchema>;
122
127
  export type ProductDetails = SimpleProduct & {
@@ -138,3 +143,5 @@ export type SearchByCriteriaResponse = z.infer<typeof searchByCriteriaResponse>;
138
143
  export type InventorySearchCriteriaInput = z.infer<
139
144
  typeof InventorySearchCriteriaInput
140
145
  >;
146
+ export type ProductPrice = z.infer<typeof productPriceSchema>;
147
+ export type ProductPriceInput = z.infer<typeof productPriceInput>;
@@ -1,5 +1,10 @@
1
1
  import z from "zod";
2
- import { customerSchema } from "./d365.schema";
2
+ import {
3
+ customerSchema,
4
+ productListLineSchema,
5
+ productListSchema,
6
+ simpleProductSchema,
7
+ } from "./d365.schema";
3
8
 
4
9
  export const userCountsSchema = z.object({
5
10
  orderCount: z.number().optional(),
@@ -12,3 +17,49 @@ export const extendedCustomerSchema = customerSchema.extend({
12
17
 
13
18
  export type User = z.infer<typeof extendedCustomerSchema>;
14
19
  export type UserCounts = z.infer<typeof userCountsSchema>;
20
+
21
+ // Wishlist
22
+
23
+ export const extendedProductListLineSchema = productListLineSchema.extend({
24
+ Product: simpleProductSchema.optional(),
25
+ });
26
+
27
+ export const extendedWishlistSchema = productListSchema
28
+ .omit({ ProductListLines: true })
29
+ .extend({ ProductListLines: extendedProductListLineSchema.array() });
30
+
31
+ const wishlistBaseInput = z.object({
32
+ Id: z.string(),
33
+ });
34
+
35
+ export const wishlistCreateInput = wishlistBaseInput.extend({
36
+ Name: z.string(),
37
+ Comment: z.string().optional(),
38
+ IsPublic: z.boolean().optional(),
39
+ });
40
+
41
+ const updateWishlistInput = wishlistBaseInput.extend({
42
+ Name: z.string(),
43
+ });
44
+
45
+ const wishlistLinesInput = wishlistBaseInput.extend({
46
+ ProductListLines: productListLineSchema.array(),
47
+ });
48
+
49
+ const copyCartToWishlistInput = z.object({
50
+ Id: z.string(),
51
+ CartID: z.string(),
52
+ IsRewrite: z.boolean(),
53
+ IsQuantityAggregate: z.boolean(),
54
+ });
55
+
56
+ export type Wishlist = z.infer<typeof productListSchema>;
57
+ export type ExtendedWishlist = z.infer<typeof extendedWishlistSchema>;
58
+ export type ExtendedProductListLine = z.infer<
59
+ typeof extendedProductListLineSchema
60
+ >;
61
+ export type WishlistDetailInput = z.infer<typeof wishlistBaseInput>;
62
+ export type WishlistCreateInput = z.infer<typeof wishlistCreateInput>;
63
+ export type UpdateWishlistInput = z.infer<typeof updateWishlistInput>;
64
+ export type WishlistLinesInput = z.infer<typeof wishlistLinesInput>;
65
+ export type CopyCartToWishlistInput = z.infer<typeof copyCartToWishlistInput>;
@@ -5,7 +5,7 @@ import { createQuery, createApiPath } from "../../create-query";
5
5
  const apiPath = createApiPath("address", "getStates");
6
6
  const config = {
7
7
  queryKey: "states",
8
- disablePropertis: ["countryId"],
8
+ disableProperties: ["countryId"] as Array<keyof GetStatesInput>,
9
9
  };
10
10
 
11
11
  const getStates = createQuery<GetStatesInput, StateProvinceInfo[]>(
@@ -1,11 +1,26 @@
1
1
  import { Cart, CheckoutInput } from "@/api-manager/schemas/cart.schema";
2
2
  import { createApiPath } from "../../create-query";
3
3
  import { useCreateMutation } from "../../create-mutation";
4
+ import { deleteCartCookie } from "@/api-manager/lib/cart-cookie-handler";
5
+ import getCart from "../queries/get-cart";
6
+ import { useQueryClient } from "@tanstack/react-query";
4
7
 
5
8
  const apiPath = createApiPath("cart", "checkout");
6
9
 
7
10
  const useCheckout = () => {
8
- return useCreateMutation<CheckoutInput, Cart>(apiPath);
11
+ const queryClient = useQueryClient();
12
+ return useCreateMutation<CheckoutInput, Cart>(apiPath, {
13
+ onSuccess: async (_data, variables) => {
14
+ // Delete old cart cookie
15
+ await deleteCartCookie();
16
+
17
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
18
+ },
19
+ onError: (error) => {
20
+ console.error("Checkout failed:", error);
21
+ // Handle error appropriately, e.g., show a notification
22
+ },
23
+ });
9
24
  };
10
25
 
11
26
  export default useCheckout;
@@ -0,0 +1,15 @@
1
+ import { ProductWarehouseInventoryInformation } from "@msdyn365-commerce/retail-proxy";
2
+ import { InventorySearchCartLinesCriteriaInput } from "@/api-manager/schemas/cart.schema";
3
+ import { createApiPath, createQuery } from "../../create-query";
4
+
5
+ const apiPath = createApiPath("cart", "getCartLinesInventory");
6
+
7
+ const getCartLinesInventory = createQuery<
8
+ InventorySearchCartLinesCriteriaInput,
9
+ ProductWarehouseInventoryInformation
10
+ >(apiPath, {
11
+ queryKey: "cart-lines-Inventory",
12
+ disableProperties: ["allDimensionsSelected", "searchCriteria"],
13
+ });
14
+
15
+ export default getCartLinesInventory;
@@ -0,0 +1,26 @@
1
+ import { QueryFunction } from "@tanstack/react-query";
2
+
3
+ /**
4
+ * This is a wrapper function for building an extension query.
5
+ * We don't require full-blown api-manager instance for extensions,
6
+ * because there is no data switching. Also not exporting all the react-query
7
+ * methods to keep the code flexible and adaptable for projects. This wrapper
8
+ * handles client side mutex to prevent multiple requests firing after refresh
9
+ * token has been found to be expired.
10
+ *
11
+ * During development, any additional hooks, events and extensions can be added to
12
+ * this function without affecting project.
13
+ *
14
+ * @param queryKey
15
+ * @param queryFn
16
+ * @returns
17
+ */
18
+ const buildQueryData = <TQueryFnData = unknown, TQueryFnInput = unknown>(
19
+ queryKey: string,
20
+ queryFn: QueryFunction<TQueryFnData>,
21
+ ) => {
22
+ // TODO: insert mutex stuff here
23
+ return { queryKey: [queryKey], queryFn };
24
+ };
25
+
26
+ export default buildQueryData;