@evenicanpm/storefront-core 2.4.2 → 2.5.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 (142) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +90 -90
  3. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +4 -3
  4. package/src/api-manager/datasources/d365/d365-user.datasource.ts +3 -3
  5. package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +1 -0
  6. package/src/api-manager/datasources/e4/address/e4-address.datasource.ts +5 -14
  7. package/src/api-manager/datasources/e4/address/e4-address.translator.ts +4 -3
  8. package/src/api-manager/datasources/e4/categories/e4-categories.translator.ts +1 -1
  9. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +29 -3
  10. package/src/api-manager/datasources/e4/product/e4-product.translator.ts +1 -1
  11. package/src/api-manager/index.ts +1 -1
  12. package/src/api-manager/lib/get-graphql-client.ts +1 -1
  13. package/src/api-manager/services/cart/mutations/add-discount-code.ts +0 -11
  14. package/src/api-manager/services/create-query.ts +13 -7
  15. package/src/api-manager/services/user/mutations/add-to-wishlist.ts +1 -1
  16. package/src/api-manager/services/user/mutations/remove-from-wishlist.ts +1 -1
  17. package/src/auth/AUTH GUIDE.md +880 -0
  18. package/src/cms/blocks/components/featured-categories.tsx +18 -15
  19. package/src/cms/blocks/components/featured-products.tsx +3 -2
  20. package/src/cms/blocks/components/footer/data/index.ts +0 -0
  21. package/src/cms/blocks/components/footer/index.tsx +0 -0
  22. package/src/cms/blocks/components/footer/sections/footer-app-store.tsx +0 -0
  23. package/src/cms/blocks/components/footer/sections/footer-contact.tsx +0 -2
  24. package/src/cms/blocks/components/footer/sections/footer-links.tsx +0 -0
  25. package/src/cms/blocks/components/footer/sections/footer-logo.tsx +2 -1
  26. package/src/cms/blocks/components/footer/sections/footer-social-links.tsx +0 -0
  27. package/src/cms/blocks/components/footer/styles/index.ts +0 -0
  28. package/src/cms/blocks/components/hero-carousel/index.tsx +0 -0
  29. package/src/cms/blocks/components/product-carousel/index.tsx +0 -0
  30. package/src/cms/blocks/components/shared/featured-product-card.tsx +0 -0
  31. package/src/cms/blocks/components/shared/product-category-item.tsx +0 -0
  32. package/src/cms/blocks/components/shared/product-grid.tsx +0 -0
  33. package/src/cms/blocks/components/shared/top-categories-card.tsx +0 -0
  34. package/src/cms/blocks/components/shared/top-rating-product-card.tsx +0 -0
  35. package/src/cms/blocks/icons/components/category.tsx +0 -0
  36. package/src/cms/blocks/icons/components/dotted-star.tsx +0 -0
  37. package/src/cms/blocks/icons/components/gift-box.tsx +0 -0
  38. package/src/cms/blocks/icons/components/light.tsx +0 -0
  39. package/src/cms/blocks/icons/components/new-arrival.tsx +0 -0
  40. package/src/cms/blocks/icons/components/rank-badge.tsx +0 -0
  41. package/src/components/SearchInput.stories.tsx +12 -10
  42. package/src/components/base-layout.tsx +42 -0
  43. package/src/components/categories/category-list/category-list.tsx +32 -12
  44. package/src/components/header/__tests__/header.test.tsx +0 -24
  45. package/src/components/header/components/mobile-header.tsx +16 -8
  46. package/src/components/header/header.tsx +6 -3
  47. package/src/components/mini-cart/components/bottom-actions.tsx +7 -2
  48. package/src/components/mini-cart/components/cart-item.tsx +16 -13
  49. package/src/components/mini-cart/components/empty-view.tsx +12 -6
  50. package/src/components/mini-cart/components/top-header.tsx +9 -8
  51. package/src/components/mini-cart/mini-cart-trigger.tsx +11 -5
  52. package/src/components/mini-cart/mini-cart.tsx +30 -15
  53. package/src/components/mobile-navigation/mobile-navigation-bar.tsx +6 -2
  54. package/src/components/navbar/categories.tsx +8 -2
  55. package/src/components/navbar/category-based-menu/category-based-menu.tsx +6 -2
  56. package/src/components/navbar/mega-menu/mega-menu.tsx +22 -14
  57. package/src/components/navbar/mobile-menu/mobile-menu.test.tsx +8 -11
  58. package/src/components/navbar/mobile-menu/mobile-menu.tsx +13 -11
  59. package/src/components/navbar/nav-list/nav-list.tsx +3 -2
  60. package/src/components/navbar/navbar.tsx +4 -2
  61. package/src/components/product-cards/product-card/product-card.tsx +50 -26
  62. package/src/components/product-cards/product-card-plp/product-card.tsx +37 -16
  63. package/src/components/product-cards/product-card-plp-list/product-card.tsx +38 -16
  64. package/src/components/product-cards/product-card-search/product-card.tsx +34 -13
  65. package/src/components/product-dialog/ProductDialog.stories.tsx +20 -18
  66. package/src/components/product-dialog/compound/product-dialog.tsx +7 -4
  67. package/src/components/product-dimensions/compound/dimensions-group.tsx +4 -2
  68. package/src/components/product-dimensions/compound/dimensions.tsx +15 -6
  69. package/src/components/product-quantity-buttons/compound/quantity-buttons-root.tsx +21 -13
  70. package/src/components/products-view/compound/products-grid-view.tsx +11 -4
  71. package/src/components/products-view/compound/products-list-view.tsx +11 -4
  72. package/src/components/search-bar/compound/search-bar-root.tsx +6 -4
  73. package/src/components/search-bar/compound/textfield-adornment-category.tsx +25 -8
  74. package/src/components/search-bar/hooks/use-click-outside.ts +7 -1
  75. package/src/components/search-bar/hooks/use-search.ts +3 -3
  76. package/src/components/user/index.ts +3 -0
  77. package/src/components/user/use-user-auth.ts +163 -0
  78. package/src/components/user/user.tsx +215 -0
  79. package/src/components/wishlist-dialogs/add-to-wishlist/AddToWishlist.stories.tsx +20 -15
  80. package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +11 -8
  81. package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-list.tsx +18 -3
  82. package/src/components/wishlist-dialogs/add-to-wishlist/index.tsx +0 -1
  83. package/src/components/wishlist-dialogs/create-wishlist/compound/create-wishlist-dialog.tsx +6 -2
  84. package/src/hooks/use-overflow-detect.ts +4 -4
  85. package/src/lib/graphqlRequestSdk.ts +0 -0
  86. package/src/lib/page-slugs.ts +16 -0
  87. package/src/pages/account/account-navigation.tsx +7 -14
  88. package/src/pages/account/account-routes.ts +4 -9
  89. package/src/pages/account/addresses/address-form.tsx +38 -16
  90. package/src/pages/account/addresses/address-item.tsx +18 -8
  91. package/src/pages/account/dashboard-header.tsx +20 -13
  92. package/src/pages/account/orders/order-history-filters-panel.tsx +21 -20
  93. package/src/pages/account/orders/order-history-header.tsx +14 -4
  94. package/src/pages/account/orders/order-history-root.tsx +3 -1
  95. package/src/pages/account/orders/order-history-row.tsx +12 -4
  96. package/src/pages/account/orders/order-history-search-and-filter.tsx +88 -59
  97. package/src/pages/account/orders/order-history-search-dropdown.tsx +26 -9
  98. package/src/pages/account/orders/order-history-sort.tsx +3 -1
  99. package/src/pages/account/orders/order-history-table.tsx +27 -5
  100. package/src/pages/account/orders/order-progress.tsx +12 -6
  101. package/src/pages/account/orders/order-row.tsx +18 -6
  102. package/src/pages/account/orders/order-summary.tsx +24 -8
  103. package/src/pages/account/orders/ordered-products.tsx +15 -5
  104. package/src/pages/account/profile/user-info.tsx +11 -3
  105. package/src/pages/account/table-row-skeleton.tsx +1 -1
  106. package/src/pages/account/wishlist/create-new-list.tsx +33 -9
  107. package/src/pages/account/wishlist/wishlist-item.tsx +18 -6
  108. package/src/pages/cart/cart-item.tsx +21 -7
  109. package/src/pages/cart/checkout-form.tsx +55 -22
  110. package/src/pages/cart/coupon-entry.tsx +143 -36
  111. package/src/pages/cart/estimate-shipping.tsx +45 -21
  112. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +22 -14
  113. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +37 -16
  114. package/src/pages/checkout/checkout-alt-form/steps/address/address-card.tsx +9 -3
  115. package/src/pages/checkout/checkout-alt-form/steps/address/delivery-address.tsx +84 -51
  116. package/src/pages/checkout/checkout-alt-form/steps/address/new-address-form.tsx +41 -19
  117. package/src/pages/checkout/checkout-alt-form/steps/customer-info/customer-information.tsx +17 -10
  118. package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +15 -6
  119. package/src/pages/checkout/checkout-alt-form/steps/shipping/delivery-options.tsx +6 -2
  120. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +12 -4
  121. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +12 -5
  122. package/src/pages/checkout/checkout-alt-summary/list-item.tsx +17 -10
  123. package/src/pages/confirmation/address.tsx +6 -2
  124. package/src/pages/confirmation/confirmation-summary.tsx +12 -4
  125. package/src/pages/confirmation/ordered-products.tsx +12 -4
  126. package/src/pages/product-details/bopis/store-card/shop-card.tsx +2 -2
  127. package/src/pages/product-details/bopis/store-card/utils/getDaysResources.ts +1 -1
  128. package/src/pages/product-details/product-intro/compound/product-info.tsx +41 -18
  129. package/src/pages/product-details/product-intro/compound/product-intro-images.tsx +18 -12
  130. package/src/pages/product-details/product-intro/compound/product-intro.tsx +20 -10
  131. package/src/pages/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +1 -1
  132. package/src/pages/product-details/product-tabs.tsx +20 -12
  133. package/src/pages/product-list/product-list-view.tsx +4 -2
  134. package/src/pages/product-list/search-bar.tsx +7 -7
  135. package/src/pages/product-list/side-nav.tsx +5 -3
  136. package/src/pages/quickorder/order-upload.tsx +1 -1
  137. package/src/pages/quickorder/provider.tsx +26 -14
  138. package/src/pages/quickorder/quick-order.tsx +49 -22
  139. package/src/providers/nav-provider/index.tsx +16 -11
  140. package/tsconfig.json +0 -5
  141. package/src/components/header/components/user.tsx +0 -322
  142. /package/src/components/{header → user}/__tests__/user.test.tsx +0 -0
@@ -11,7 +11,7 @@ import { withZodSchema } from "formik-validator-zod";
11
11
  import isFunction from "lodash/isFunction";
12
12
  import { useTranslations } from "next-intl";
13
13
  import type React from "react";
14
- import { createContext } from "react";
14
+ import { createContext, useMemo } from "react";
15
15
  import { z } from "zod";
16
16
 
17
17
  interface Props {
@@ -82,9 +82,13 @@ export default function CreateNewList(props: Readonly<Props>) {
82
82
  resetForm();
83
83
  }
84
84
  };
85
+ const contextValue = useMemo(
86
+ () => ({ formik, handleClose }),
87
+ [formik, handleClose],
88
+ );
85
89
 
86
90
  return (
87
- <CreateNewWishlistContext.Provider value={{ formik, handleClose }}>
91
+ <CreateNewWishlistContext.Provider value={contextValue}>
88
92
  <Dialog open={props.open} onClose={handleClose}>
89
93
  <DialogContent>
90
94
  {isSubmitting ? <CircularProgress /> : children}
@@ -4,8 +4,8 @@ import { useCallback, useEffect, useRef, useState } from "react";
4
4
 
5
5
  export default function useOverflowDetect() {
6
6
  const elementRef = useRef<HTMLElement | null>(null);
7
- const [isLeftOverflowing, setLeftIsOverflowing] = useState(false);
8
- const [isRightOverflowing, setRightIsOverflowing] = useState(false);
7
+ const [isLeftOverflowing, setIsLeftOverflowing] = useState(false);
8
+ const [isRightOverflowing, setIsRightOverflowing] = useState(false);
9
9
 
10
10
  const checkOverflow = useCallback(() => {
11
11
  const element = elementRef.current;
@@ -15,9 +15,9 @@ export default function useOverflowDetect() {
15
15
  const viewportWidth =
16
16
  window.innerWidth || document.documentElement.clientWidth;
17
17
 
18
- if (elementRect.left < 0) setLeftIsOverflowing(true);
18
+ if (elementRect.left < 0) setIsLeftOverflowing(true);
19
19
 
20
- if (elementRect.right > viewportWidth) setRightIsOverflowing(true);
20
+ if (elementRect.right > viewportWidth) setIsRightOverflowing(true);
21
21
  }
22
22
  }, []);
23
23
 
File without changes
@@ -1,3 +1,19 @@
1
+ /**
2
+ * Central registry of URL path segments for storefront pages.
3
+ *
4
+ * Use these constants instead of hardcoding path strings inline.
5
+ * Values are bare slugs (no leading slash) so they can be composed
6
+ * freely into full paths or passed to URL constructors.
7
+ *
8
+ * @example
9
+ * // Building a product detail URL
10
+ * `/${PageSlug.ProductDetail}/${slug}/${id}`
11
+ *
12
+ * // Navigating to the product list
13
+ * new URL(PageSlug.ProductList, globalThis.location.origin)
14
+ *
15
+ * Add new entries here when introducing new top-level pages.
16
+ */
1
17
  export const PageSlug = {
2
18
  ProductDetail: "product",
3
19
  ProductList: "products",
@@ -11,7 +11,6 @@ import {
11
11
  } from "@evenicanpm/storefront-core/src/components/Typography";
12
12
  import {
13
13
  type AccountRoute,
14
- type AccountRouteOverrides,
15
14
  useCreateAccountRoutes,
16
15
  } from "@evenicanpm/storefront-core/src/pages/account/account-routes";
17
16
  // STYLED COMPONENTS
@@ -21,7 +20,7 @@ import {
21
20
  } from "@evenicanpm/storefront-core/src/pages/account/styles";
22
21
  import { usePathname } from "next/navigation";
23
22
  import { useTranslations } from "next-intl";
24
- import { createContext, Fragment, type ReactNode } from "react";
23
+ import { createContext, Fragment, type ReactNode, useMemo } from "react";
25
24
  import type { IconType } from "react-icons";
26
25
 
27
26
  /**
@@ -82,10 +81,8 @@ const routeToNavItem = (route: AccountRoute): NavItem => {
82
81
  };
83
82
  };
84
83
 
85
- export const DEFAULT_MENU_ITEMS = (
86
- overrides?: AccountRouteOverrides,
87
- ): NavSection[] => {
88
- const routes = useCreateAccountRoutes(overrides);
84
+ export const DEFAULT_MENU_ITEMS = (): NavSection[] => {
85
+ const routes = useCreateAccountRoutes();
89
86
  return [
90
87
  {
91
88
  title: "DASHBOARD",
@@ -112,21 +109,16 @@ const AccountNavigationContext =
112
109
  interface AccountNavigationProps {
113
110
  items?: NavSection[];
114
111
  children?: ReactNode;
115
- routeOverrides?: AccountRouteOverrides;
116
112
  }
117
113
 
118
- const AccountNavigation = ({
119
- items,
120
- children,
121
- routeOverrides,
122
- }: AccountNavigationProps) => {
114
+ const AccountNavigation = ({ items, children }: AccountNavigationProps) => {
123
115
  const pathname = usePathname();
124
116
  const t = useTranslations("Account.Menu");
125
117
 
126
118
  const { data: user } = getUser.useData();
127
119
  const { data: counts } = getUserCounts.useData(String(user?.RecordId) || "");
128
120
 
129
- const defaultItems = DEFAULT_MENU_ITEMS(routeOverrides);
121
+ const defaultItems = DEFAULT_MENU_ITEMS();
130
122
 
131
123
  // Render either the props items or the default items
132
124
  const itemsWCounts = (items || defaultItems).map((section) => ({
@@ -141,9 +133,10 @@ const AccountNavigation = ({
141
133
  return item;
142
134
  }),
143
135
  }));
136
+ const contextValue = useMemo(() => ({ items: itemsWCounts }), [itemsWCounts]);
144
137
 
145
138
  return (
146
- <AccountNavigationContext.Provider value={{ items: itemsWCounts }}>
139
+ <AccountNavigationContext.Provider value={contextValue}>
147
140
  <MainContainer>
148
141
  {children ||
149
142
  itemsWCounts.map((section) => (
@@ -64,23 +64,18 @@ export const RoutesOverrideContext = createContext<
64
64
  export type AccountRouteKey = keyof typeof defaults;
65
65
 
66
66
  /**
67
- * Hook that builds the account routes map, merging defaults with context-level
68
- * and call-site overrides. Override priority (highest wins):
69
- * 1. `overrides` parameter passed directly to the hook
70
- * 2. Values from {@link RoutesOverrideContext}
71
- * 3. Built-in defaults
67
+ * Hook that builds the account routes map, merging defaults with any overrides
68
+ * supplied via {@link RoutesOverrideContext}.
72
69
  *
73
- * @param overrides - Optional route overrides applied at the call site
74
- * @returns The full set of account routes with any overrides merged in
70
+ * @returns The full set of account routes with context overrides merged in
75
71
  */
76
- export const useCreateAccountRoutes = (overrides?: AccountRouteOverrides) => {
72
+ export const useCreateAccountRoutes = () => {
77
73
  const ctxOverrides = useContext(RoutesOverrideContext);
78
74
  return {
79
75
  ...defaults,
80
76
  wishlists: {
81
77
  ...defaults.wishlists,
82
78
  ...ctxOverrides?.wishlists,
83
- ...overrides?.wishlists,
84
79
  },
85
80
  };
86
81
  };
@@ -26,7 +26,7 @@ import { withZodSchema } from "formik-validator-zod";
26
26
  import _ from "lodash";
27
27
  import { useRouter } from "next/navigation";
28
28
  import { useLocale, useTranslations } from "next-intl";
29
- import React, { createContext, useContext, useRef } from "react";
29
+ import React, { createContext, useContext, useMemo, useRef } from "react";
30
30
  import { z } from "zod";
31
31
 
32
32
  interface AddressFormValues {
@@ -61,11 +61,15 @@ const AddressForm = ({ address, children }: RootProps) => {
61
61
  const locale = useLocale();
62
62
 
63
63
  const { data: countries } = getCountries.useData({ locale });
64
- const defaultCountry = countries?.find(
65
- (c) => c.CountryRegionId === address?.ThreeLetterISORegionName,
66
- );
64
+ const defaultCountry = Array.isArray(countries)
65
+ ? countries?.find(
66
+ (c) => c.CountryRegionId === address?.ThreeLetterISORegionName,
67
+ )
68
+ : null;
69
+
67
70
  const [selectedCountry, setSelectedCountry] =
68
71
  React.useState<CountryRegionInfo | null>(defaultCountry ?? null);
72
+
69
73
  const { data: states } = getStates.useData({
70
74
  countryId: selectedCountry?.CountryRegionId,
71
75
  });
@@ -142,11 +146,19 @@ const AddressForm = ({ address, children }: RootProps) => {
142
146
  onSubmit: handleSubmit,
143
147
  validate: withZodSchema(FormSchema),
144
148
  });
149
+ const contextValue = useMemo(
150
+ () => ({
151
+ formik,
152
+ countries,
153
+ states,
154
+ selectedCountry,
155
+ setSelectedCountry,
156
+ }),
157
+ [formik, countries, states, selectedCountry, setSelectedCountry],
158
+ );
145
159
 
146
160
  return (
147
- <FormContext.Provider
148
- value={{ formik, countries, states, selectedCountry, setSelectedCountry }}
149
- >
161
+ <FormContext.Provider value={contextValue}>
150
162
  <form onSubmit={formik.handleSubmit}>{children}</form>
151
163
  </FormContext.Provider>
152
164
  );
@@ -167,7 +179,7 @@ AddressForm.Fields = ({ children }: { children?: React.ReactNode }) => {
167
179
  );
168
180
  };
169
181
 
170
- AddressForm.Field = ({ name, label, required }: FieldProps) => {
182
+ const AddressFormField = ({ name, label, required }: FieldProps) => {
171
183
  const ctx = useContext(FormContext);
172
184
  const t = useTranslations("Address");
173
185
  if (!ctx) return null;
@@ -194,7 +206,9 @@ AddressForm.Field = ({ name, label, required }: FieldProps) => {
194
206
  );
195
207
  };
196
208
 
197
- AddressForm.Country = () => {
209
+ AddressForm.Field = AddressFormField;
210
+
211
+ const AddressFormCountry = () => {
198
212
  const ctx = useContext(FormContext);
199
213
  const t = useTranslations("Address");
200
214
  if (!ctx) return null;
@@ -226,11 +240,13 @@ AddressForm.Country = () => {
226
240
  }}
227
241
  >
228
242
  <MenuItem value="">Select Country</MenuItem>
229
- {countries?.map(({ ShortName, CountryRegionId }) => (
230
- <MenuItem key={CountryRegionId} value={CountryRegionId}>
231
- {ShortName}
232
- </MenuItem>
233
- ))}
243
+ {Array.isArray(countries) &&
244
+ countries.length > 0 &&
245
+ countries?.map(({ ShortName, CountryRegionId }) => (
246
+ <MenuItem key={CountryRegionId} value={CountryRegionId}>
247
+ {ShortName}
248
+ </MenuItem>
249
+ ))}
234
250
  </Select>
235
251
  {formik.touched.Country &&
236
252
  formik.errors.Country &&
@@ -244,7 +260,9 @@ AddressForm.Country = () => {
244
260
  );
245
261
  };
246
262
 
247
- AddressForm.State = () => {
263
+ AddressForm.Country = AddressFormCountry;
264
+
265
+ const AddressFormState = () => {
248
266
  const ctx = useContext(FormContext);
249
267
  const t = useTranslations("Address");
250
268
  if (!ctx) return null;
@@ -287,6 +305,8 @@ AddressForm.State = () => {
287
305
  );
288
306
  };
289
307
 
308
+ AddressForm.State = AddressFormState;
309
+
290
310
  AddressForm.Actions = ({ children }: { children?: React.ReactNode }) => {
291
311
  return (
292
312
  <Grid size={{ sm: 6, xs: 12 }} sx={{ mt: 3 }}>
@@ -295,7 +315,7 @@ AddressForm.Actions = ({ children }: { children?: React.ReactNode }) => {
295
315
  );
296
316
  };
297
317
 
298
- AddressForm.SubmitButton = () => {
318
+ const AddressFormSubmitButton = () => {
299
319
  const t = useTranslations("Address");
300
320
  const ctx = useContext(FormContext);
301
321
  const isSubmitting = ctx?.formik.isSubmitting ?? false;
@@ -316,4 +336,6 @@ AddressForm.SubmitButton = () => {
316
336
  );
317
337
  };
318
338
 
339
+ AddressForm.SubmitButton = AddressFormSubmitButton;
340
+
319
341
  export default AddressForm;
@@ -13,7 +13,7 @@ import IconButton from "@mui/material/IconButton";
13
13
  import type { SxProps } from "@mui/material/styles";
14
14
  import { useRouter } from "next/navigation";
15
15
  import { useTranslations } from "next-intl";
16
- import { createContext, type ReactNode, useContext } from "react";
16
+ import { createContext, type ReactNode, useContext, useMemo } from "react";
17
17
  // ICONS
18
18
  import { MdDelete as Delete, MdEdit as Edit } from "react-icons/md";
19
19
 
@@ -61,11 +61,13 @@ const AddressListItem = ({ address, children, sx }: RootProps) => {
61
61
  });
62
62
  }
63
63
  };
64
+ const contextValue = useMemo(
65
+ () => ({ address, setPrimaryAddress, editAddress, handleDelete }),
66
+ [address, setPrimaryAddress, editAddress, handleDelete],
67
+ );
64
68
 
65
69
  return (
66
- <AddressContext.Provider
67
- value={{ address, setPrimaryAddress, editAddress, handleDelete }}
68
- >
70
+ <AddressContext.Provider value={contextValue}>
69
71
  <TableRow
70
72
  sx={{
71
73
  display: "grid",
@@ -89,7 +91,7 @@ interface FieldProps {
89
91
  render?: (value: FieldValue, address: Address) => ReactNode;
90
92
  }
91
93
 
92
- AddressListItem.Field = ({ field, render }: FieldProps) => {
94
+ const AddressListItemField = ({ field, render }: FieldProps) => {
93
95
  const ctx = useContext(AddressContext);
94
96
  if (!ctx) return null;
95
97
  const { address } = ctx;
@@ -105,6 +107,8 @@ AddressListItem.Field = ({ field, render }: FieldProps) => {
105
107
  );
106
108
  };
107
109
 
110
+ AddressListItem.Field = AddressListItemField;
111
+
108
112
  AddressListItem.Actions = ({ children }: { children: ReactNode }) => {
109
113
  return (
110
114
  <Box display="flex" alignItems="center" gap={1} justifySelf="end">
@@ -113,7 +117,7 @@ AddressListItem.Actions = ({ children }: { children: ReactNode }) => {
113
117
  );
114
118
  };
115
119
 
116
- AddressListItem.Primary = () => {
120
+ const AddressListItemPrimary = () => {
117
121
  const ctx = useContext(AddressContext);
118
122
  const t = useTranslations("Account.Addresses");
119
123
  if (!ctx) return null;
@@ -135,7 +139,9 @@ AddressListItem.Primary = () => {
135
139
  );
136
140
  };
137
141
 
138
- AddressListItem.Edit = () => {
142
+ AddressListItem.Primary = AddressListItemPrimary;
143
+
144
+ const AddressListItemEdit = () => {
139
145
  const ctx = useContext(AddressContext);
140
146
  if (!ctx) return null;
141
147
  const { address, editAddress } = ctx;
@@ -149,7 +155,9 @@ AddressListItem.Edit = () => {
149
155
  );
150
156
  };
151
157
 
152
- AddressListItem.Delete = () => {
158
+ AddressListItem.Edit = AddressListItemEdit;
159
+
160
+ const AddressListItemDelete = () => {
153
161
  const ctx = useContext(AddressContext);
154
162
  if (!ctx) return null;
155
163
  const { address, handleDelete } = ctx;
@@ -163,4 +171,6 @@ AddressListItem.Delete = () => {
163
171
  );
164
172
  };
165
173
 
174
+ AddressListItem.Delete = AddressListItemDelete;
175
+
166
176
  export default AddressListItem;
@@ -15,7 +15,7 @@ import {
15
15
  } from "@mui/material";
16
16
  import Link from "next/link";
17
17
  import { useTranslations } from "next-intl";
18
- import { createContext, type ReactNode, useContext } from "react";
18
+ import { createContext, type ReactNode, useContext, useMemo } from "react";
19
19
  import type { IconType } from "react-icons";
20
20
  import { MdMenu as Menu } from "react-icons/md";
21
21
 
@@ -44,14 +44,22 @@ const DashboardHeaderContext =
44
44
  interface RootProps {
45
45
  children: ReactNode;
46
46
  }
47
+
48
+ const renderSideNavMenuButton = (close: () => void) => (
49
+ <IconButton onClick={close}>
50
+ <Menu fontSize="small" />
51
+ </IconButton>
52
+ );
53
+
47
54
  const DashboardHeader = ({ children }: RootProps) => {
48
55
  const t = useTranslations();
49
56
  const isTablet = useMediaQuery((theme: Theme) =>
50
57
  theme.breakpoints.down(1025),
51
58
  );
59
+ const contextValue = useMemo(() => ({ isTablet, t }), [isTablet, t]);
52
60
 
53
61
  return (
54
- <DashboardHeaderContext.Provider value={{ isTablet, t }}>
62
+ <DashboardHeaderContext.Provider value={contextValue}>
55
63
  <StyledBox>
56
64
  <FlexBox mt={2} className="headerHold">
57
65
  {children}
@@ -61,7 +69,7 @@ const DashboardHeader = ({ children }: RootProps) => {
61
69
  );
62
70
  };
63
71
 
64
- DashboardHeader.Title = ({
72
+ const DashboardHeaderTitle = ({
65
73
  title,
66
74
  titleMessage,
67
75
  Icon,
@@ -87,7 +95,9 @@ DashboardHeader.Title = ({
87
95
  );
88
96
  };
89
97
 
90
- DashboardHeader.Buttons = ({ children }: { children: ReactNode }) => {
98
+ DashboardHeader.Title = DashboardHeaderTitle;
99
+
100
+ const DashboardHeaderButtons = ({ children }: { children: ReactNode }) => {
91
101
  const ctx = useContext(DashboardHeaderContext);
92
102
  if (!ctx) return null;
93
103
  const { isTablet } = ctx;
@@ -108,7 +118,9 @@ DashboardHeader.Buttons = ({ children }: { children: ReactNode }) => {
108
118
  );
109
119
  };
110
120
 
111
- DashboardHeader.GeneratedButton = ({
121
+ DashboardHeader.Buttons = DashboardHeaderButtons;
122
+
123
+ const DashboardHeaderGeneratedButton = ({
112
124
  href,
113
125
  text,
114
126
  }: {
@@ -130,17 +142,12 @@ DashboardHeader.GeneratedButton = ({
130
142
  );
131
143
  };
132
144
 
145
+ DashboardHeader.GeneratedButton = DashboardHeaderGeneratedButton;
146
+
133
147
  DashboardHeader.SideNav = ({ children }: { children?: ReactNode }) => {
134
148
  return (
135
149
  <div className="sidenav">
136
- <SideNav
137
- position="left"
138
- handler={(close) => (
139
- <IconButton onClick={close}>
140
- <Menu fontSize="small" />
141
- </IconButton>
142
- )}
143
- >
150
+ <SideNav position="left" handler={renderSideNavMenuButton}>
144
151
  {children ?? <Navigation />}
145
152
  </SideNav>
146
153
  </div>
@@ -20,6 +20,7 @@ import {
20
20
  type FilterOption,
21
21
  FilterSourceType,
22
22
  type OrderStatusFilter,
23
+ type OriginFilter,
23
24
  } from "./order-history-filter-types";
24
25
  import { useOrdersSearchAndFilter } from "./order-history-search-and-filter";
25
26
 
@@ -155,9 +156,9 @@ const OrdersFilterPanel = ({ children }: { children: React.ReactNode }) => {
155
156
  </Button>
156
157
  <Box>
157
158
  {activeFilters.length > 0 &&
158
- activeFilters.map((filter, index) => (
159
+ activeFilters.map((filter) => (
159
160
  <Box
160
- key={index}
161
+ key={`${filter?.sourceType}-${filter?.label}`}
161
162
  sx={{
162
163
  display: "inline-flex",
163
164
  alignItems: "center",
@@ -247,13 +248,13 @@ const OriginsFilterList = () => {
247
248
  flexWrap: "nowrap",
248
249
  }}
249
250
  >
250
- {originOptions.map((originOption, index) => (
251
+ {originOptions.map((originOption: OriginFilter) => (
251
252
  <FormControlLabel
252
- key={index}
253
+ key={`${originOption?.value}-${originOption?.label}`}
253
254
  control={
254
255
  <Checkbox
255
256
  checked={selectedOrigins.some(
256
- (o) => o.value === originOption.value,
257
+ (o: OriginFilter) => o.value === originOption.value,
257
258
  )}
258
259
  onChange={() => handleSelectOrigin(originOption)}
259
260
  />
@@ -267,7 +268,8 @@ const OriginsFilterList = () => {
267
268
  };
268
269
 
269
270
  const CreatedDateFilterList = () => {
270
- const { createdDate, handleSelectCreatedDate } = useOrdersSearchAndFilter();
271
+ const { createdDate, handleSelectCreatedDate, handleSetCreatedDate } =
272
+ useOrdersSearchAndFilter();
271
273
  const t = useTranslations("Account.Orders");
272
274
 
273
275
  return (
@@ -277,8 +279,8 @@ const CreatedDateFilterList = () => {
277
279
  </Typography>
278
280
  <Divider />
279
281
  <RadioGroup value={createdDate?.label ?? ""}>
280
- {createdDateOptions.map((createdDateOption, index) => (
281
- <div key={index}>
282
+ {createdDateOptions.map((createdDateOption) => (
283
+ <div key={createdDateOption.label}>
282
284
  <FormControlLabel
283
285
  key={createdDateOption.label}
284
286
  value={createdDateOption.label}
@@ -292,18 +294,17 @@ const CreatedDateFilterList = () => {
292
294
  label={createdDateOption.label}
293
295
  />
294
296
  {createdDateOption.label === "Custom Dates" &&
295
- createdDate &&
296
- createdDate.label === "Custom Dates" && (
297
+ createdDate?.label === "Custom Dates" && (
297
298
  <Box mt={1} display="flex" flexDirection="column" gap={2}>
298
299
  <TextField
299
300
  label="From"
300
301
  type="date"
301
302
  value={createdDate?.startValue || ""}
302
- onChange={(e) =>
303
- handleSelectCreatedDate(
304
- { ...createdDate, startValue: e.target.value },
305
- false,
306
- )
303
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
304
+ handleSetCreatedDate({
305
+ ...createdDate,
306
+ startValue: e.target.value,
307
+ })
307
308
  }
308
309
  slotProps={{
309
310
  inputLabel: {
@@ -316,11 +317,11 @@ const CreatedDateFilterList = () => {
316
317
  label="To"
317
318
  type="date"
318
319
  value={createdDate?.endValue || ""}
319
- onChange={(e) =>
320
- handleSelectCreatedDate(
321
- { ...createdDate, endValue: e.target.value },
322
- false,
323
- )
320
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
321
+ handleSetCreatedDate({
322
+ ...createdDate,
323
+ endValue: e.target.value,
324
+ })
324
325
  }
325
326
  slotProps={{
326
327
  inputLabel: {
@@ -25,17 +25,21 @@ const OrderHistoryTableHeader = ({
25
25
  );
26
26
  };
27
27
 
28
- OrderHistoryTableHeader.OrderNumber = () => {
28
+ const OrderHistoryTableHeaderOrderNumber = () => {
29
29
  const t = useTranslations("Account.Orders");
30
30
  return <Paragraph ellipsis>{t("orderNumberHeader")}</Paragraph>;
31
31
  };
32
32
 
33
- OrderHistoryTableHeader.OrderStatus = () => {
33
+ OrderHistoryTableHeader.OrderNumber = OrderHistoryTableHeaderOrderNumber;
34
+
35
+ const OrderHistoryTableHeaderOrderStatus = () => {
34
36
  const t = useTranslations("Account.Orders");
35
37
  return <Box textAlign="center">{t("orderStatusHeader")}</Box>;
36
38
  };
37
39
 
38
- OrderHistoryTableHeader.TransactionDate = () => {
40
+ OrderHistoryTableHeader.OrderStatus = OrderHistoryTableHeaderOrderStatus;
41
+
42
+ const OrderHistoryTableHeaderTransactionDate = () => {
39
43
  const t = useTranslations("Account.Orders");
40
44
  return (
41
45
  <Paragraph textAlign={{ sm: "center", xs: "left" }}>
@@ -44,11 +48,17 @@ OrderHistoryTableHeader.TransactionDate = () => {
44
48
  );
45
49
  };
46
50
 
47
- OrderHistoryTableHeader.TransactionAmount = () => {
51
+ OrderHistoryTableHeader.TransactionDate =
52
+ OrderHistoryTableHeaderTransactionDate;
53
+
54
+ const OrderHistoryTableHeaderTransactionAmount = () => {
48
55
  const t = useTranslations("Account.Orders");
49
56
  return (
50
57
  <Paragraph textAlign="center">{t("transactionAmountHeader")}</Paragraph>
51
58
  );
52
59
  };
53
60
 
61
+ OrderHistoryTableHeader.TransactionAmount =
62
+ OrderHistoryTableHeaderTransactionAmount;
63
+
54
64
  export default OrderHistoryTableHeader;
@@ -74,11 +74,13 @@ const OrderHistory = ({ children }: { children: React.ReactNode }) => {
74
74
 
75
75
  OrderHistory.SearchAndFilter = OrdersSearchAndFilter;
76
76
 
77
- OrderHistory.Sort = () => {
77
+ const OrderHistorySort = () => {
78
78
  const { setSortColumn } = useOrderHistory();
79
79
  return <OrdersSort setSortColumn={setSortColumn} />;
80
80
  };
81
81
 
82
+ OrderHistory.Sort = OrderHistorySort;
83
+
82
84
  OrderHistory.Table = OrderHistoryTable;
83
85
 
84
86
  // OrderHistory.Table = () => {
@@ -62,12 +62,14 @@ const OrderHistoryTableRow = ({ children }: { children: React.ReactNode }) => {
62
62
  );
63
63
  };
64
64
 
65
- OrderHistoryTableRow.OrderNumber = () => {
65
+ const OrderHistoryTableRowOrderNumber = () => {
66
66
  const { order } = useOrderHistoryTableRow();
67
67
  return <H6 ellipsis>{order?.ChannelReferenceId}</H6>;
68
68
  };
69
69
 
70
- OrderHistoryTableRow.OrderStatus = () => {
70
+ OrderHistoryTableRow.OrderNumber = OrderHistoryTableRowOrderNumber;
71
+
72
+ const OrderHistoryTableRowOrderStatus = () => {
71
73
  const { order } = useOrderHistoryTableRow();
72
74
  const { label, color } = getStatusInfo(order.StatusValue ?? 0);
73
75
  return (
@@ -77,7 +79,9 @@ OrderHistoryTableRow.OrderStatus = () => {
77
79
  );
78
80
  };
79
81
 
80
- OrderHistoryTableRow.TransactionDate = () => {
82
+ OrderHistoryTableRow.OrderStatus = OrderHistoryTableRowOrderStatus;
83
+
84
+ const OrderHistoryTableRowTransactionDate = () => {
81
85
  const { order } = useOrderHistoryTableRow();
82
86
  return (
83
87
  <Paragraph textAlign={{ sm: "center", xs: "left" }}>
@@ -86,7 +90,9 @@ OrderHistoryTableRow.TransactionDate = () => {
86
90
  );
87
91
  };
88
92
 
89
- OrderHistoryTableRow.TransactionAmount = () => {
93
+ OrderHistoryTableRow.TransactionDate = OrderHistoryTableRowTransactionDate;
94
+
95
+ const OrderHistoryTableRowTransactionAmount = () => {
90
96
  const { order } = useOrderHistoryTableRow();
91
97
  const { formatCurrency } = useCurrencyFormatter();
92
98
 
@@ -97,6 +103,8 @@ OrderHistoryTableRow.TransactionAmount = () => {
97
103
  );
98
104
  };
99
105
 
106
+ OrderHistoryTableRow.TransactionAmount = OrderHistoryTableRowTransactionAmount;
107
+
100
108
  OrderHistoryTableRow.DetailsIcon = () => {
101
109
  return (
102
110
  <Box display={{ sm: "inline-flex", xs: "none" }} justifyContent="end">