@evenicanpm/storefront-core 1.3.0 → 1.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.
- package/__mocks__/get-product-by-id.ts +0 -1
- package/__mocks__/query-client.ts +1 -1
- package/package.json +3 -3
- package/src/api-manager/datasources/d365/d365-address.datasource.ts +2 -2
- package/src/api-manager/datasources/d365/d365-cart.datasource.ts +4 -5
- package/src/api-manager/datasources/d365/d365-categories.datasource.ts +1 -1
- package/src/api-manager/datasources/d365/d365-product.datasource.ts +21 -7
- package/src/api-manager/datasources/d365/d365-session.datasource.ts +5 -7
- package/src/api-manager/datasources/d365/d365-user.datasource.ts +12 -23
- package/src/api-manager/datasources/d365/d365.datasource.ts +0 -2
- package/src/api-manager/datasources/d365/index.ts +2 -4
- package/src/api-manager/datasources/d365/utils/decode-jwt.ts +1 -2
- package/src/api-manager/datasources/d365/utils/parse-object.ts +1 -1
- package/src/api-manager/datasources/e4/address/e4-address.remaps.ts +2 -2
- package/src/api-manager/datasources/e4/address/e4-address.translator.ts +1 -1
- package/src/api-manager/datasources/e4/cart/e4-cart.datasource.ts +28 -27
- package/src/api-manager/datasources/e4/cart/e4-cart.remaps.ts +1 -1
- package/src/api-manager/datasources/e4/cart/e4-cart.translator.ts +1 -1
- package/src/api-manager/datasources/e4/categories/e4-categories.translator.ts +20 -20
- package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +471 -7
- package/src/api-manager/datasources/e4/order/e4-order.remaps.ts +2 -2
- package/src/api-manager/datasources/e4/organization/e4-organization.datasource.ts +2 -8
- package/src/api-manager/datasources/e4/product/e4-product.remaps.ts +3 -3
- package/src/api-manager/datasources/e4/product/e4-product.translator.ts +13 -29
- package/src/api-manager/datasources/e4/session/e4-session.datasource.ts +1 -3
- package/src/api-manager/datasources/e4/user/e4-user.remaps.ts +2 -4
- package/src/api-manager/datasources/e4/utils/unwrap-e4-variants.ts +4 -4
- package/src/api-manager/index.ts +1 -1
- package/src/api-manager/lib/category-helpers.ts +3 -2
- package/src/api-manager/lib/get-graphql-client.ts +1 -2
- package/src/api-manager/services/cart/mutations/add-discount-code.ts +2 -2
- package/src/api-manager/services/cart/mutations/add-to-cart.ts +2 -2
- package/src/api-manager/services/product/server/product.server.ts +1 -1
- package/src/api-manager/types/Datasource.ts +4 -9
- package/src/auth/auth-options.test.ts +2 -58
- package/src/auth/auth-options.ts +1 -2
- package/src/auth/next-auth-cookie-manager.ts +0 -2
- package/src/auth/refresh-token.ts +3 -8
- package/src/auth/signout.ts +1 -1
- package/src/cms/blocks/block-manager.tsx +3 -3
- package/src/cms/blocks/components/category-card.tsx +1 -1
- package/src/cms/blocks/components/category-carousel/index.tsx +3 -1
- package/src/cms/blocks/components/footer/index.tsx +2 -2
- package/src/cms/blocks/components/footer/sections/footer-links.tsx +2 -2
- package/src/cms/blocks/components/footer/sections/footer-logo.tsx +1 -1
- package/src/cms/blocks/components/footer/sections/footer-social-links.tsx +10 -2
- package/src/cms/blocks/components/hero-carousel/index.tsx +29 -25
- package/src/cms/blocks/components/media-card.tsx +1 -1
- package/src/cms/blocks/components/product-carousel/index.tsx +1 -1
- package/src/cms/blocks/components/product-section-fullwidth/index.tsx +2 -2
- package/src/cms/blocks/components/seo/index.tsx +5 -1
- package/src/cms/blocks/components/services/index.tsx +1 -1
- package/src/cms/blocks/components/services/service-card.tsx +1 -1
- package/src/cms/blocks/components/shared/featured-product-card.tsx +4 -1
- package/src/cms/blocks/components/shared/product-category-item.tsx +1 -1
- package/src/cms/blocks/components/shared/product-grid.tsx +4 -1
- package/src/cms/blocks/components/shared/top-categories-card.tsx +5 -1
- package/src/cms/blocks/components/shared/top-rating-product-card.tsx +1 -1
- package/src/components-v2/BazaarImage.tsx +1 -1
- package/src/components-v2/BazaarMenu.tsx +2 -2
- package/src/components-v2/BazaarTextField.tsx +3 -3
- package/src/components-v2/BorderShadowCard.stories.tsx +1 -1
- package/src/components-v2/DropZone.tsx +1 -2
- package/src/components-v2/Typography.tsx +12 -12
- package/src/components-v2/accordion/accordion-header.tsx +1 -1
- package/src/components-v2/categories/category-list/styles.ts +0 -7
- package/src/components-v2/categories/category-list-item/category-menu-item.tsx +1 -1
- package/src/components-v2/categories/category-menu.tsx +16 -13
- package/src/components-v2/categories/mega-menu/mega-menu-1.tsx +1 -1
- package/src/components-v2/categories/mega-menu/mega-menu-2.tsx +2 -2
- package/src/components-v2/header/components/user.tsx +5 -5
- package/src/components-v2/mini-cart/__tests__/cart-item.test.tsx +13 -13
- package/src/components-v2/mini-cart/components/bottom-actions.tsx +1 -1
- package/src/components-v2/mini-cart/components/cart-item.tsx +3 -3
- package/src/components-v2/mini-cart/components/top-header.tsx +2 -2
- package/src/components-v2/mobile-navigation/mobile-navigation-bar.tsx +1 -1
- package/src/components-v2/nav-link/nav-link-2.tsx +1 -1
- package/src/components-v2/nav-link/nav-link-3.tsx +1 -1
- package/src/components-v2/nav-link/nav-link.tsx +1 -1
- package/src/components-v2/navbar/categories.tsx +3 -1
- package/src/components-v2/navbar/category-based-menu/category-based-menu.tsx +2 -2
- package/src/components-v2/navbar/mobile-menu/mobile-menu.tsx +11 -3
- package/src/components-v2/navbar/nav-list/nav-item-child.tsx +1 -1
- package/src/components-v2/navbar/nav-list/nav-list.tsx +118 -80
- package/src/components-v2/navbar/navbar.tsx +1 -1
- package/src/components-v2/no-records.tsx +1 -1
- package/src/components-v2/product-cards/favorite-button.tsx +1 -1
- package/src/components-v2/product-cards/product-card/product-card.tsx +5 -7
- package/src/components-v2/product-cards/product-card-plp/product-card.tsx +7 -6
- package/src/components-v2/product-cards/product-card-plp-list/components/tags.tsx +1 -1
- package/src/components-v2/product-cards/product-card-plp-list/product-card.tsx +7 -4
- package/src/components-v2/product-cards/product-card-search/product-card.tsx +4 -2
- package/src/components-v2/product-cards/product-price.tsx +1 -1
- package/src/components-v2/product-cards/product-rating.tsx +1 -1
- package/src/components-v2/product-cards/product-title.tsx +1 -1
- package/src/components-v2/product-cards/quantity-buttons.tsx +1 -1
- package/src/components-v2/product-dialog/compound/product-dialog.tsx +2 -2
- package/src/components-v2/product-dialog/index.tsx +1 -1
- package/src/components-v2/product-dimensions/compound/dimensions-group-list.tsx +6 -1
- package/src/components-v2/product-dimensions/index.tsx +3 -3
- package/src/components-v2/product-quantity-buttons/compound/quantity-buttons-root.tsx +1 -1
- package/src/components-v2/product-quantity-variants/index.tsx +1 -2
- package/src/components-v2/products-view/ProductsList.stories.tsx +0 -2
- package/src/components-v2/products-view/compound/products-grid-view.tsx +2 -2
- package/src/components-v2/products-view/compound/products-list-view.tsx +3 -3
- package/src/components-v2/products-view/index.tsx +2 -2
- package/src/components-v2/progress/progress.tsx +1 -1
- package/src/components-v2/scrollbar/scrollbar.tsx +1 -1
- package/src/components-v2/search-bar/compound/results-suspense.tsx +1 -2
- package/src/components-v2/search-bar/compound/results.tsx +6 -3
- package/src/components-v2/search-bar/compound/search-bar-root.tsx +4 -2
- package/src/components-v2/search-bar/compound/textfield-adornment-category.tsx +25 -27
- package/src/components-v2/search-bar/compound/textfield.tsx +0 -1
- package/src/components-v2/search-bar/hooks/use-search.ts +1 -1
- package/src/components-v2/section/Section.stories.tsx +1 -1
- package/src/components-v2/side-nav/side-nav.tsx +1 -1
- package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +1 -1
- package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-list.tsx +2 -2
- package/src/components-v2/wishlist-dialogs/add-to-wishlist/hooks/use-add-to-wishlist.ts +2 -0
- package/src/components-v2/wishlist-dialogs/add-to-wishlist/index.tsx +11 -13
- package/src/components-v2/wishlist-dialogs/create-wishlist/compound/create-wishlist-dialog.tsx +2 -1
- package/src/hooks/use-variants.ts +25 -27
- package/src/lib/build-detail-url.ts +1 -1
- package/src/lib/category-helpers.ts +3 -3
- package/src/lib/get-palette-color.ts +35 -15
- package/src/lib/hexToLuma.ts +2 -2
- package/src/lib/store-hours-helper.ts +5 -5
- package/src/pages-v2/account/addresses/address-form.tsx +10 -8
- package/src/pages-v2/account/addresses/address-item.tsx +1 -1
- package/src/pages-v2/account/orders/order-progress.tsx +1 -1
- package/src/pages-v2/account/orders/ordered-products.tsx +1 -1
- package/src/pages-v2/account/profile/user-info.tsx +1 -2
- package/src/pages-v2/account/wishlist/create-new-list.tsx +1 -0
- package/src/pages-v2/account/wishlist/wishlist-item.tsx +2 -1
- package/src/pages-v2/blog/blog-card.tsx +1 -1
- package/src/pages-v2/cart/__tests__/cart-item.test.tsx +0 -1
- package/src/pages-v2/cart/__tests__/checkout-form.test.tsx +0 -8
- package/src/pages-v2/cart/cart-item.tsx +86 -77
- package/src/pages-v2/cart/checkout-form.tsx +4 -4
- package/src/pages-v2/cart/coupon-entry.tsx +3 -3
- package/src/pages-v2/cart/estimate-shipping.tsx +6 -6
- package/src/pages-v2/checkout/checkout-alt-form/checkout-form.tsx +2 -2
- package/src/pages-v2/checkout/checkout-alt-form/checkout-step.tsx +2 -2
- package/src/pages-v2/checkout/checkout-alt-form/step-heading.tsx +1 -1
- package/src/pages-v2/checkout/checkout-alt-form/steps/address/delivery-address.tsx +12 -10
- package/src/pages-v2/checkout/checkout-alt-form/steps/address/new-address-form.tsx +5 -6
- package/src/pages-v2/checkout/checkout-alt-form/steps/customer-info/customer-information.tsx +8 -9
- package/src/pages-v2/checkout/checkout-alt-form/steps/payment/payment-details.tsx +17 -20
- package/src/pages-v2/checkout/checkout-alt-form/steps/shipping/delivery-date.tsx +1 -1
- package/src/pages-v2/checkout/checkout-alt-form/steps/shipping/delivery-options.tsx +14 -15
- package/src/pages-v2/confirmation/confirmation-summary.tsx +8 -7
- package/src/pages-v2/confirmation/ordered-products.tsx +3 -3
- package/src/pages-v2/product-details/bopis/pickup-option-select.tsx +1 -1
- package/src/pages-v2/product-details/bopis/store-card/shop-card.tsx +1 -1
- package/src/pages-v2/product-details/checkbox-label.tsx +1 -1
- package/src/pages-v2/product-details/frequently-bought.tsx +1 -1
- package/src/pages-v2/product-details/frequently-product-card.tsx +1 -1
- package/src/pages-v2/product-details/product-comment.tsx +1 -1
- package/src/pages-v2/product-details/product-description.tsx +2 -2
- package/src/pages-v2/product-details/product-filter-card.tsx +4 -1
- package/src/pages-v2/product-details/product-intro/compound/product-intro-images.tsx +36 -31
- package/src/pages-v2/product-details/product-intro/compound/product-intro.tsx +1 -1
- package/src/pages-v2/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +1 -1
- package/src/pages-v2/product-details/product-intro/utils.ts +2 -4
- package/src/pages-v2/product-details/product-review.tsx +1 -1
- package/src/pages-v2/product-details/product-tabs.tsx +5 -2
- package/src/pages-v2/product-details/related-products.tsx +7 -4
- package/src/pages-v2/product-list/breadcrumbs.tsx +1 -1
- package/src/pages-v2/product-list/checkbox-label.tsx +1 -1
- package/src/pages-v2/product-list/facet-group.tsx +1 -1
- package/src/pages-v2/product-list/list-filter.tsx +2 -2
- package/src/pages-v2/product-list/product-list-context.tsx +5 -1
- package/src/pages-v2/product-list/product-list-view.tsx +4 -1
- package/src/pages-v2/product-list/product-rating.tsx +4 -1
- package/src/pages-v2/product-list/quick-view-dialog-content.tsx +2 -4
- package/src/pages-v2/product-list/quick-view-dialog.tsx +5 -1
- package/src/pages-v2/product-list/range-filter.tsx +0 -1
- package/src/pages-v2/product-list/scrollbar.tsx +1 -1
- package/src/pages-v2/product-list/side-nav.tsx +1 -1
- package/src/pages-v2/product-list/swatch.tsx +12 -7
- package/src/pages-v2/product-list/utils/getCategoryFilterFromSlug.ts +33 -68
- package/src/pages-v2/product-list/utils/product-list-helper.ts +1 -1
- package/src/pages-v2/product-list/utils/search-for-category.ts +2 -2
- package/src/pages-v2/quickorder/order-upload.tsx +90 -76
- package/src/pages-v2/quickorder/provider.tsx +6 -4
- package/src/pages-v2/quickorder/quick-order-row.tsx +1 -1
- package/src/pages-v2/quickorder/quick-order.tsx +28 -21
- package/src/providers/notifications/notification-context.tsx +0 -1
|
@@ -55,7 +55,7 @@ const salesLinesProduct = {
|
|
|
55
55
|
selectedDimensions: {
|
|
56
56
|
key: "Dimensions",
|
|
57
57
|
transform: (dimensions: SelectedDimension[]) => {
|
|
58
|
-
if (!
|
|
58
|
+
if (!dimensions?.length) return [];
|
|
59
59
|
|
|
60
60
|
const typeMap: Record<string, number> = {
|
|
61
61
|
Color: 1,
|
|
@@ -81,7 +81,7 @@ export const orderDetailsMap = {
|
|
|
81
81
|
lines: {
|
|
82
82
|
key: "SalesLines",
|
|
83
83
|
transform: (lines: OrderLine[]) => {
|
|
84
|
-
if (!
|
|
84
|
+
if (!lines?.length) return [];
|
|
85
85
|
return lines.map((line) => {
|
|
86
86
|
return {
|
|
87
87
|
Product: merge(line, salesLinesProduct),
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
|
|
3
1
|
import type { ChannelConfiguration } from "@evenicanpm/storefront-core/src/api-manager/schemas/organization.schema";
|
|
4
2
|
import type { OrganizationApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
|
|
5
3
|
|
|
6
|
-
// Todo: implement these stubs
|
|
7
|
-
|
|
8
4
|
const organization: OrganizationApi = {
|
|
9
5
|
getChannelConfiguration: async (): Promise<ChannelConfiguration> => {
|
|
10
|
-
|
|
11
|
-
return {};
|
|
6
|
+
return {} as ChannelConfiguration;
|
|
12
7
|
},
|
|
13
|
-
|
|
14
|
-
getStoresList(input) {
|
|
8
|
+
async getStoresList(input) {
|
|
15
9
|
console.log(input);
|
|
16
10
|
return [];
|
|
17
11
|
},
|
|
@@ -34,7 +34,7 @@ export const productMap = {
|
|
|
34
34
|
{
|
|
35
35
|
key: "RecordId",
|
|
36
36
|
transform: (id: string) => {
|
|
37
|
-
if (id !== null) return parseInt(id);
|
|
37
|
+
if (id !== null) return Number.parseInt(id);
|
|
38
38
|
},
|
|
39
39
|
},
|
|
40
40
|
],
|
|
@@ -54,13 +54,13 @@ export const productPricesMap = {
|
|
|
54
54
|
{
|
|
55
55
|
key: "ProductId",
|
|
56
56
|
transform: (id: string) => {
|
|
57
|
-
if (id !== null) return parseInt(id);
|
|
57
|
+
if (id !== null) return Number.parseInt(id);
|
|
58
58
|
},
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
key: "ListingId",
|
|
62
62
|
transform: (id: string) => {
|
|
63
|
-
if (id !== null) return parseInt(id);
|
|
63
|
+
if (id !== null) return Number.parseInt(id);
|
|
64
64
|
},
|
|
65
65
|
},
|
|
66
66
|
],
|
|
@@ -88,7 +88,7 @@ export const generateCategoryFilterString = async (
|
|
|
88
88
|
|
|
89
89
|
const categoryMap = new Map<string, E4Category>();
|
|
90
90
|
e4Categories.forEach((cat) => {
|
|
91
|
-
if (cat
|
|
91
|
+
if (cat?.id) {
|
|
92
92
|
categoryMap.set(cat.id, cat);
|
|
93
93
|
}
|
|
94
94
|
});
|
|
@@ -160,21 +160,22 @@ export const productListInputTransform = async (
|
|
|
160
160
|
let sortName =
|
|
161
161
|
input?.searchState?.queryResultSettings?.Sorting?.Columns?.[0]?.ColumnName?.toLowerCase();
|
|
162
162
|
// No relevance sort in e4 API.
|
|
163
|
-
sortName
|
|
164
|
-
sortName
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
163
|
+
if (sortName === "relevance") {
|
|
164
|
+
sortName = "";
|
|
165
|
+
} else if (sortDesc) {
|
|
166
|
+
sortName = `${sortName}-hl`;
|
|
167
|
+
} else {
|
|
168
|
+
sortName = `${sortName}-lh`;
|
|
169
|
+
}
|
|
169
170
|
|
|
170
171
|
return {
|
|
171
172
|
categoryId: input?.categoryId ? String(input?.categoryId) : "1",
|
|
172
173
|
categoryFilter,
|
|
173
174
|
top: input?.searchState?.queryResultSettings?.Paging?.Top || 20,
|
|
174
175
|
skip: input?.searchState?.queryResultSettings?.Paging?.Skip || 0,
|
|
175
|
-
sort: sortName
|
|
176
|
+
sort: sortName || null,
|
|
176
177
|
sortDesc,
|
|
177
|
-
filters: filters
|
|
178
|
+
filters: filters?.length ? filters : [],
|
|
178
179
|
search: input?.searchState?.searchTerm,
|
|
179
180
|
};
|
|
180
181
|
};
|
|
@@ -198,7 +199,7 @@ export const refinersTransformValidate = (
|
|
|
198
199
|
"#Microsoft.Dynamics.Commerce.Runtime.DataModel.ProductRefiner";
|
|
199
200
|
const ODATA_TYPE_REFINER_VALUE =
|
|
200
201
|
"#Microsoft.Dynamics.Commerce.Runtime.DataModel.ProductRefinerValue";
|
|
201
|
-
const DIMENSION_ATTRIBUTE_IDS = ["size", "color", "style"];
|
|
202
|
+
const DIMENSION_ATTRIBUTE_IDS = new Set(["size", "color", "style"]);
|
|
202
203
|
|
|
203
204
|
// Treat price filter as list filter (DisplayTemplateValue as 0) for now, until e4 API supports price bar filtering.
|
|
204
205
|
return rawFilters
|
|
@@ -253,7 +254,7 @@ export const refinersTransformValidate = (
|
|
|
253
254
|
})
|
|
254
255
|
.filter((value): value is TransformedRefinerValue => value !== null);
|
|
255
256
|
|
|
256
|
-
const isDimension = DIMENSION_ATTRIBUTE_IDS.
|
|
257
|
+
const isDimension = DIMENSION_ATTRIBUTE_IDS.has(
|
|
257
258
|
rawRefiner.AttributeId?.toLowerCase() ?? "",
|
|
258
259
|
);
|
|
259
260
|
|
|
@@ -310,7 +311,7 @@ export const productPricesTransformValidate = (
|
|
|
310
311
|
(p) => merge(p, productPricesMap) as ProductPrice,
|
|
311
312
|
);
|
|
312
313
|
try {
|
|
313
|
-
translatedData.
|
|
314
|
+
translatedData.forEach((pp: ProductPrice) => productPriceSchema.parse(pp));
|
|
314
315
|
} catch (e) {
|
|
315
316
|
console.warn("ProductPrice data does not match retail-proxy schema", e);
|
|
316
317
|
}
|
|
@@ -325,22 +326,5 @@ export const productWithCategoryTransformValidate = (
|
|
|
325
326
|
productMap,
|
|
326
327
|
) as ProductSearchResult;
|
|
327
328
|
|
|
328
|
-
// const productWithCategory = {
|
|
329
|
-
// ...productTransform,
|
|
330
|
-
// // Category: categoryTransformValidate([
|
|
331
|
-
// // productData?.category?.[0],
|
|
332
|
-
// // ] as E4Category[]),
|
|
333
|
-
// } as unknown as ProductSearchResult;
|
|
334
|
-
|
|
335
|
-
// try {
|
|
336
|
-
// productWCategorySchema.parse(productWithCategory);
|
|
337
|
-
// } catch (e) {
|
|
338
|
-
// console.warn(
|
|
339
|
-
// "Product data with category does not match retail-proxy schema",
|
|
340
|
-
// e
|
|
341
|
-
// );
|
|
342
|
-
// throw e;
|
|
343
|
-
// }
|
|
344
|
-
|
|
345
329
|
return productTransform;
|
|
346
330
|
};
|
|
@@ -19,9 +19,7 @@ import { merge } from "object-mapper";
|
|
|
19
19
|
export const userMap = {
|
|
20
20
|
id: {
|
|
21
21
|
key: "RecordId",
|
|
22
|
-
transform:
|
|
23
|
-
return Number(id);
|
|
24
|
-
},
|
|
22
|
+
transform: Number,
|
|
25
23
|
},
|
|
26
24
|
code: {
|
|
27
25
|
// we don't need AccountNumber in e4 storefront,
|
|
@@ -38,7 +36,7 @@ export const userMap = {
|
|
|
38
36
|
addresses: {
|
|
39
37
|
key: "Addresses",
|
|
40
38
|
transform: (addresses: E4Address[], source: Me) => {
|
|
41
|
-
if (!
|
|
39
|
+
if (!addresses?.length) return [];
|
|
42
40
|
const mapped = merge(addresses || [], addressMap) as Address[];
|
|
43
41
|
return mapped.map((address: Address) => ({
|
|
44
42
|
...address,
|
|
@@ -27,7 +27,7 @@ type UnwrappedAttribute = {
|
|
|
27
27
|
export default function unwrapE4Variants(
|
|
28
28
|
variants: Variants[],
|
|
29
29
|
): UnwrappedAttribute[][] {
|
|
30
|
-
if (!
|
|
30
|
+
if (!variants?.length) return [];
|
|
31
31
|
const attrs: UnwrappedAttribute[] = variants.flatMap(
|
|
32
32
|
(v) =>
|
|
33
33
|
v.Attributes?.map((attr: Maybe<Attributes>) => {
|
|
@@ -47,9 +47,7 @@ export default function unwrapE4Variants(
|
|
|
47
47
|
(group) => group[0]?.DimensionTypeValue === item.DimensionTypeValue,
|
|
48
48
|
);
|
|
49
49
|
|
|
50
|
-
if (
|
|
51
|
-
acc.push([item]);
|
|
52
|
-
} else {
|
|
50
|
+
if (group) {
|
|
53
51
|
const existingDimension = group.find(
|
|
54
52
|
(attribute) =>
|
|
55
53
|
attribute.DimensionValue.Value === item.DimensionValue.Value,
|
|
@@ -60,6 +58,8 @@ export default function unwrapE4Variants(
|
|
|
60
58
|
} else if (!existingDimension.ProductIds.includes(item.ProductIds[0])) {
|
|
61
59
|
existingDimension.ProductIds.push(item.ProductIds[0]);
|
|
62
60
|
}
|
|
61
|
+
} else {
|
|
62
|
+
acc.push([item]);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
return acc;
|
package/src/api-manager/index.ts
CHANGED
|
@@ -86,7 +86,8 @@ const generateUrl = (
|
|
|
86
86
|
);
|
|
87
87
|
}
|
|
88
88
|
categoryNames.unshift(currentCategory.RecordId.toString());
|
|
89
|
-
|
|
89
|
+
|
|
90
|
+
return categoryNames.slice().reverse().join("/");
|
|
90
91
|
};
|
|
91
92
|
|
|
92
93
|
const getFriendlyName = (
|
|
@@ -99,7 +100,7 @@ const getFriendlyName = (
|
|
|
99
100
|
(item) => item?.Language?.toLowerCase() === locale?.toLowerCase(),
|
|
100
101
|
);
|
|
101
102
|
}
|
|
102
|
-
return nameTranslation
|
|
103
|
+
return nameTranslation?.Text;
|
|
103
104
|
};
|
|
104
105
|
|
|
105
106
|
const normalizeCategoryName = (string_: string) =>
|
|
@@ -4,7 +4,6 @@ import { getSdk } from "@evenicanpm/storefront-core/src/api-manager/datasources/
|
|
|
4
4
|
import { GraphQLClient } from "graphql-request";
|
|
5
5
|
import { cookies } from "next/headers";
|
|
6
6
|
import { getServerSession } from "next-auth/next";
|
|
7
|
-
// TODO: Get auth inside client packages
|
|
8
7
|
import authOptions from "@/auth/auth-options";
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -30,7 +29,7 @@ const getGraphQLClient = async (withAuth = false) => {
|
|
|
30
29
|
|
|
31
30
|
if (withAuth) {
|
|
32
31
|
const session = await getServerSession(authOptions());
|
|
33
|
-
if (session
|
|
32
|
+
if (session?.accessToken) {
|
|
34
33
|
headers["Authorization"] = `Bearer ${session.accessToken}`;
|
|
35
34
|
}
|
|
36
35
|
}
|
|
@@ -16,8 +16,8 @@ const useAddDiscountCode = () => {
|
|
|
16
16
|
const { setNotification } = useNotification();
|
|
17
17
|
const t = useTranslations("Cart.Messages");
|
|
18
18
|
return useCreateMutation<ManageDiscountCodeInput, Cart>(apiPath, {
|
|
19
|
-
onSuccess:
|
|
20
|
-
|
|
19
|
+
onSuccess: (result) => {
|
|
20
|
+
getCart.setQueryData(queryClient, result);
|
|
21
21
|
},
|
|
22
22
|
onError: () => {
|
|
23
23
|
setNotification({
|
|
@@ -17,8 +17,8 @@ const useAddToCart = () => {
|
|
|
17
17
|
const queryClient = useQueryClient();
|
|
18
18
|
|
|
19
19
|
return useCreateMutation<AddToCartInput, Cart>(apiPath, {
|
|
20
|
-
onSuccess:
|
|
21
|
-
|
|
20
|
+
onSuccess: (result, input) => {
|
|
21
|
+
getCart.setQueryData(queryClient, result);
|
|
22
22
|
|
|
23
23
|
// Handle single cart line
|
|
24
24
|
if (input.cartLine.length === 1) {
|
|
@@ -11,6 +11,6 @@ export const searchProductsServer = async (
|
|
|
11
11
|
const apiManager = await createApiManager();
|
|
12
12
|
const searchProducts = apiManager.product.searchProducts
|
|
13
13
|
? apiManager.product.searchProducts(input)
|
|
14
|
-
: Promise.reject("product.searchProducts() is not defined");
|
|
14
|
+
: Promise.reject(new Error("product.searchProducts() is not defined"));
|
|
15
15
|
return parseObject(searchProducts);
|
|
16
16
|
};
|
|
@@ -23,10 +23,6 @@ import type {
|
|
|
23
23
|
SessionInitInput,
|
|
24
24
|
} from "@evenicanpm/storefront-core/src/api-manager/schemas/session.schema";
|
|
25
25
|
import type * as userSchemas from "@evenicanpm/storefront-core/src/api-manager/schemas/user.schema";
|
|
26
|
-
import type {
|
|
27
|
-
User,
|
|
28
|
-
UserCounts,
|
|
29
|
-
} from "@evenicanpm/storefront-core/src/api-manager/schemas/user.schema";
|
|
30
26
|
import type {
|
|
31
27
|
CardPaymentAcceptPoint,
|
|
32
28
|
CardPaymentAcceptResult,
|
|
@@ -92,7 +88,7 @@ export interface OrganizationApi {
|
|
|
92
88
|
export interface UserApi {
|
|
93
89
|
updateUser(user: userSchemas.User): Promise<userSchemas.User>;
|
|
94
90
|
getUser(): Promise<userSchemas.User | null>;
|
|
95
|
-
getUserCounts(userId: string): Promise<UserCounts>;
|
|
91
|
+
getUserCounts(userId: string): Promise<userSchemas.UserCounts>;
|
|
96
92
|
// wishlists
|
|
97
93
|
getWishlists(): Promise<userSchemas.Wishlist[]>;
|
|
98
94
|
getWishlistDetails(
|
|
@@ -115,9 +111,9 @@ export interface UserApi {
|
|
|
115
111
|
copyCartToWishlist(
|
|
116
112
|
input: userSchemas.CopyCartToWishlistInput,
|
|
117
113
|
): Promise<userSchemas.Wishlist>;
|
|
118
|
-
updateUser(user: User): Promise<User>;
|
|
119
|
-
getUser(): Promise<User | null>;
|
|
120
|
-
getUserCounts(userId: string): Promise<UserCounts>;
|
|
114
|
+
updateUser(user: userSchemas.User): Promise<userSchemas.User>;
|
|
115
|
+
getUser(): Promise<userSchemas.User | null>;
|
|
116
|
+
getUserCounts(userId: string): Promise<userSchemas.UserCounts>;
|
|
121
117
|
}
|
|
122
118
|
|
|
123
119
|
/**
|
|
@@ -129,7 +125,6 @@ export interface SessionApi {
|
|
|
129
125
|
getSession(): Promise<Session>;
|
|
130
126
|
}
|
|
131
127
|
|
|
132
|
-
// export interface Datasource extends Datasource {};
|
|
133
128
|
/**
|
|
134
129
|
* Cart Data Actions
|
|
135
130
|
*/
|
|
@@ -3,15 +3,6 @@ import type { JWT } from "next-auth/jwt";
|
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import authOptions, { type ExtendedJwtToken } from "@/auth/auth-options";
|
|
5
5
|
|
|
6
|
-
// vi.mock("./aadb2c-provider", () => ({
|
|
7
|
-
// azureADB2CProvider: {},
|
|
8
|
-
// refreshAccessToken: vi.fn(async (token) => ({
|
|
9
|
-
// ...token,
|
|
10
|
-
// accessToken: "newAccessToken",
|
|
11
|
-
// accessTokenExpiresAt: Date.now() + 3600 * 1000,
|
|
12
|
-
// })),
|
|
13
|
-
// }));
|
|
14
|
-
|
|
15
6
|
describe("next-auth option callbacks", () => {
|
|
16
7
|
describe("jwt callback", () => {
|
|
17
8
|
it("should store accessToken and refreshToken when account and profile are available", async () => {
|
|
@@ -33,11 +24,7 @@ describe("next-auth option callbacks", () => {
|
|
|
33
24
|
};
|
|
34
25
|
|
|
35
26
|
let newToken;
|
|
36
|
-
if (
|
|
37
|
-
authOptions &&
|
|
38
|
-
authOptions().callbacks &&
|
|
39
|
-
authOptions().callbacks.jwt
|
|
40
|
-
) {
|
|
27
|
+
if (authOptions()?.callbacks?.jwt) {
|
|
41
28
|
newToken = await authOptions().callbacks.jwt({
|
|
42
29
|
token,
|
|
43
30
|
user: { id: "123" },
|
|
@@ -51,45 +38,6 @@ describe("next-auth option callbacks", () => {
|
|
|
51
38
|
expect(newToken?.accessTokenExpiresAt).toBe(token.accessTokenExpiresAt); // We're not setting this or using it anymore
|
|
52
39
|
expect(newToken?.refreshTokenExpiresAt).toBe(token.refreshTokenExpiresAt); // We're not setting this or using it anymore);
|
|
53
40
|
});
|
|
54
|
-
|
|
55
|
-
// it("should return token if access token is not expired", async () => {
|
|
56
|
-
// const token: JWT = {
|
|
57
|
-
// accessToken: "validToken",
|
|
58
|
-
// accessTokenExpiresAt: Date.now() + 3600 * 1000,
|
|
59
|
-
// refreshToken: "",
|
|
60
|
-
// refreshTokenExpiresAt: 0
|
|
61
|
-
// };
|
|
62
|
-
|
|
63
|
-
// let newToken;
|
|
64
|
-
// if (authOptions && authOptions.callbacks && authOptions.callbacks.jwt) {
|
|
65
|
-
// newToken = await authOptions.callbacks.jwt({ token, user: { id: '123' }, account: null });
|
|
66
|
-
// }
|
|
67
|
-
|
|
68
|
-
// expect(newToken).toEqual(token);
|
|
69
|
-
// });
|
|
70
|
-
|
|
71
|
-
// it("should return error if refresh token is expired", async () => {
|
|
72
|
-
// const token = { accessTokenExpiresAt: Date.now() - 1000, refreshTokenExpiresAt: Date.now() - 1000 };
|
|
73
|
-
|
|
74
|
-
// let newToken;
|
|
75
|
-
// if (authOptions && authOptions.callbacks && authOptions.callbacks.jwt) {
|
|
76
|
-
// newToken = await authOptions.callbacks.jwt({ token, user: { id: '123' }, account: null });
|
|
77
|
-
// }
|
|
78
|
-
|
|
79
|
-
// expect(newToken?.error).toBe("RefreshTokenExpired");
|
|
80
|
-
// });
|
|
81
|
-
|
|
82
|
-
// it("should refresh access token if access token is expired but refresh token is valid", async () => {
|
|
83
|
-
// const token = { accessToken: "oldToken", accessTokenExpiresAt: Date.now() - 1000, refreshToken: "validRefreshToken", refreshTokenExpiresAt: Date.now() + 3600 * 1000 };
|
|
84
|
-
|
|
85
|
-
// let newToken;
|
|
86
|
-
// if (authOptions && authOptions.callbacks && authOptions.callbacks.jwt) {
|
|
87
|
-
// newToken = await authOptions.callbacks.jwt({ token, user: { id: '123' }, account: null });
|
|
88
|
-
// }
|
|
89
|
-
|
|
90
|
-
// // expect(refreshAccessToken).toHaveBeenCalledWith(token);
|
|
91
|
-
// expect(newToken?.accessToken).toBe("newAccessToken");
|
|
92
|
-
// });
|
|
93
41
|
});
|
|
94
42
|
|
|
95
43
|
describe("session callback", () => {
|
|
@@ -103,11 +51,7 @@ describe("next-auth option callbacks", () => {
|
|
|
103
51
|
error: "SomeError",
|
|
104
52
|
};
|
|
105
53
|
let newSession: Session | null;
|
|
106
|
-
if (
|
|
107
|
-
authOptions &&
|
|
108
|
-
authOptions().callbacks &&
|
|
109
|
-
authOptions().callbacks.session
|
|
110
|
-
) {
|
|
54
|
+
if (authOptions()?.callbacks?.session) {
|
|
111
55
|
newSession = await authOptions().callbacks.session({
|
|
112
56
|
session,
|
|
113
57
|
token,
|
package/src/auth/auth-options.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// import { getSecret } from "@evenicanpm/common-core/src/lib/keyvault";
|
|
2
1
|
import { deleteCartCookie } from "@evenicanpm/storefront-core/src/lib/cart-cookie-handler";
|
|
3
2
|
import type {
|
|
4
3
|
GetServerSidePropsContext,
|
|
@@ -20,7 +19,7 @@ export type ExtendedSession = Session & { token?: JWT };
|
|
|
20
19
|
|
|
21
20
|
const createAuthOptions = (): NextAuthOptions => {
|
|
22
21
|
const providers = getProviders();
|
|
23
|
-
const secret = process.env.NEXTAUTH_SECRET; //
|
|
22
|
+
const secret = process.env.NEXTAUTH_SECRET; // internally used by next-auth, shouldn't be from key vault.
|
|
24
23
|
return {
|
|
25
24
|
providers,
|
|
26
25
|
secret,
|
|
@@ -7,8 +7,6 @@ import type { ExtendedJwtToken } from "@/auth/auth-options";
|
|
|
7
7
|
const NEXT_AUTH_COOKIE_NAME = "next-auth.session-token";
|
|
8
8
|
const SECURE_COOKIE_PREFIX = "__Secure-";
|
|
9
9
|
|
|
10
|
-
// import { getSecret } from "@evenicanpm/common-core/src/lib/keyvault";
|
|
11
|
-
|
|
12
10
|
/**
|
|
13
11
|
* Retrieves and decodes the NextAuth session cookie from the request.
|
|
14
12
|
*
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
import jwt from "jsonwebtoken";
|
|
3
3
|
import type { JWT } from "next-auth/jwt";
|
|
4
|
-
|
|
5
|
-
import type {
|
|
6
|
-
AuthorizationEndpointHandler,
|
|
7
|
-
OAuthConfig,
|
|
8
|
-
} from "next-auth/providers";
|
|
4
|
+
import type { AuthorizationEndpointHandler } from "next-auth/providers";
|
|
9
5
|
import type { ExtendedJwtToken } from "@/auth/auth-options";
|
|
10
6
|
import { getProviders } from "@/auth/providers";
|
|
11
7
|
|
|
@@ -23,8 +19,7 @@ const checkAccessTokenExpiry = async (
|
|
|
23
19
|
token.error = "Token Invalid";
|
|
24
20
|
return { token, isRefreshed: false };
|
|
25
21
|
}
|
|
26
|
-
|
|
27
|
-
// console.log('Time now', new Date(Date.now()).toLocaleString());
|
|
22
|
+
|
|
28
23
|
if (!decryptedToken.exp) {
|
|
29
24
|
token.error = "noExpiryOnToken";
|
|
30
25
|
return {
|
|
@@ -57,7 +52,7 @@ const checkAccessTokenExpiry = async (
|
|
|
57
52
|
//Find the provider that matches the provider cookie
|
|
58
53
|
const matchedProvider = providers.find(
|
|
59
54
|
(p) => p.id.toLowerCase() === token.provider.toLowerCase(),
|
|
60
|
-
)
|
|
55
|
+
);
|
|
61
56
|
if (!matchedProvider) {
|
|
62
57
|
token.error = "NoProviderMatch";
|
|
63
58
|
return {
|
package/src/auth/signout.ts
CHANGED
|
@@ -37,11 +37,11 @@ const componentMap = {
|
|
|
37
37
|
const getBlockComponent: <B extends Block>(
|
|
38
38
|
block: B,
|
|
39
39
|
idx: number,
|
|
40
|
-
) => JSX.Element | null = (block: Block
|
|
40
|
+
) => JSX.Element | null = (block: Block) => {
|
|
41
41
|
const Component = componentMap[
|
|
42
42
|
block.__component as keyof typeof componentMap
|
|
43
43
|
] as React.ComponentType<Block>;
|
|
44
|
-
return <Component key={`block-${
|
|
44
|
+
return <Component key={`block-${block.id}`} {...block} />;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
interface BlockManagerProps {
|
|
@@ -55,7 +55,7 @@ const BlockManager: React.FC<BlockManagerProps> = ({ blocks }) => {
|
|
|
55
55
|
{blocks.map((block, idx) => {
|
|
56
56
|
const isLast = idx === blocks.length - 1;
|
|
57
57
|
return (
|
|
58
|
-
<Box key={`block-wrapper-${
|
|
58
|
+
<Box key={`block-wrapper-${block.id}`} mb={isLast ? 0 : 6}>
|
|
59
59
|
{getBlockComponent(block, idx)}
|
|
60
60
|
</Box>
|
|
61
61
|
);
|
|
@@ -13,7 +13,9 @@ import CategoryIcon from "@/cms/blocks/icons/components/category";
|
|
|
13
13
|
import type { CategoryCarouselBlock } from "@/cms/blocks/interfaces";
|
|
14
14
|
import { getImageUrl } from "@/cms/endpoints";
|
|
15
15
|
|
|
16
|
-
export default function CategoryCarousel(
|
|
16
|
+
export default function CategoryCarousel(
|
|
17
|
+
data: Readonly<CategoryCarouselBlock>,
|
|
18
|
+
) {
|
|
17
19
|
const responsive = [
|
|
18
20
|
{ breakpoint: 959, settings: { slidesToShow: 2 } },
|
|
19
21
|
{ breakpoint: 650, settings: { slidesToShow: 1 } },
|
|
@@ -59,7 +59,7 @@ const Footer = async () => {
|
|
|
59
59
|
const data = await fetchFooterData();
|
|
60
60
|
const sections = data?.data?.sections;
|
|
61
61
|
|
|
62
|
-
if (!
|
|
62
|
+
if (!sections?.length) {
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -77,7 +77,7 @@ const Footer = async () => {
|
|
|
77
77
|
py={{ sm: 10, xs: 4 }}
|
|
78
78
|
>
|
|
79
79
|
<Grid container spacing={6}>
|
|
80
|
-
{sections.map(getBlockComponent)}
|
|
80
|
+
{sections.map((block, index) => getBlockComponent(block, index))}
|
|
81
81
|
</Grid>
|
|
82
82
|
</Box>
|
|
83
83
|
</Box>
|
|
@@ -18,8 +18,8 @@ const Links = (props: Props) => {
|
|
|
18
18
|
<Grid size={{ lg: 3, md: 6, sm: 6, xs: 12 }}>
|
|
19
19
|
<Heading>{props.title}</Heading>
|
|
20
20
|
<Box>
|
|
21
|
-
{props?.links?.map((item
|
|
22
|
-
<Fragment key={
|
|
21
|
+
{props?.links?.map((item) => (
|
|
22
|
+
<Fragment key={item.slug}>
|
|
23
23
|
<StyledLink isDark={props.isDark} href={`pages/${item.slug}`}>
|
|
24
24
|
{item.title}
|
|
25
25
|
</StyledLink>
|
|
@@ -11,7 +11,7 @@ interface Props {
|
|
|
11
11
|
description: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export default function LogoSection(props: Props) {
|
|
14
|
+
export default function LogoSection(props: Readonly<Props>) {
|
|
15
15
|
const theme = useTheme();
|
|
16
16
|
const downMd = useMediaQuery(theme.breakpoints.down(1150)); // same like in header
|
|
17
17
|
return (
|
|
@@ -36,7 +36,10 @@ interface Props {
|
|
|
36
36
|
socials?: SocialLink[];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export default function SocialLinks({
|
|
39
|
+
export default function SocialLinks({
|
|
40
|
+
variant = "light",
|
|
41
|
+
socials,
|
|
42
|
+
}: Readonly<Props>) {
|
|
40
43
|
return (
|
|
41
44
|
<FlexBox className="flex" mx={-0.625}>
|
|
42
45
|
{socials?.map(({ name, link }, i) => {
|
|
@@ -58,7 +61,12 @@ export default function SocialLinks({ variant = "light", socials }: Props) {
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
return (
|
|
61
|
-
<a
|
|
64
|
+
<a
|
|
65
|
+
href={link}
|
|
66
|
+
target="_blank"
|
|
67
|
+
rel="noreferrer noopenner"
|
|
68
|
+
key={name + i}
|
|
69
|
+
>
|
|
62
70
|
<StyledIconButton variant={variant}>
|
|
63
71
|
{Icon && <Icon fontSize="inherit" className="icon" />}
|
|
64
72
|
</StyledIconButton>
|