@evenicanpm/storefront-core 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.ts +2 -3
- package/.storybook/preview.tsx +0 -2
- package/__mocks__/create-mutation.ts +16 -8
- package/__mocks__/next-image.tsx +12 -3
- package/package.json +2 -2
- package/src/api-manager/datasources/d365/d365-categories.datasource.ts +2 -3
- package/src/api-manager/datasources/d365/d365-order.datasource.ts +4 -4
- package/src/api-manager/datasources/d365/d365-organization.datasource.ts +15 -8
- package/src/api-manager/datasources/d365/d365-product.datasource.ts +31 -19
- package/src/api-manager/datasources/d365/d365-user.datasource.ts +47 -19
- package/src/api-manager/datasources/e4/address/e4-address.remaps.ts +2 -2
- package/src/api-manager/datasources/e4/cart/e4-cart.remaps.ts +1 -1
- package/src/api-manager/datasources/e4/product/e4-product.remaps.ts +3 -3
- package/src/api-manager/datasources/e4/user/e4-user.datasource.ts +4 -8
- package/src/api-manager/index.ts +10 -127
- package/src/api-manager/lib/get-graphql-client.ts +2 -2
- package/src/api-manager/schemas/product.schema.ts +9 -0
- package/src/api-manager/services/create-extension-query.ts +1 -1
- package/src/api-manager/services/create-query.ts +106 -266
- package/src/api-manager/types/Datasource.ts +0 -3
- package/src/cms/blocks/components/category-card.tsx +0 -1
- package/src/cms/blocks/components/category-carousel/index.tsx +0 -1
- package/src/cms/blocks/components/featured-categories.tsx +1 -2
- package/src/cms/blocks/components/featured-products.tsx +1 -2
- package/src/cms/blocks/components/footer/index.tsx +0 -1
- package/src/cms/blocks/components/footer/sections/footer-app-store.tsx +1 -1
- package/src/cms/blocks/components/footer/sections/footer-contact.tsx +0 -1
- package/src/cms/blocks/components/footer/sections/footer-links.tsx +1 -1
- package/src/cms/blocks/components/footer/sections/footer-logo.tsx +0 -1
- package/src/cms/blocks/components/footer/sections/footer-social-links.tsx +0 -1
- package/src/cms/blocks/components/footer.tsx +0 -1
- package/src/cms/blocks/components/hero-carousel/index.tsx +0 -1
- package/src/cms/blocks/components/hero.tsx +0 -1
- package/src/cms/blocks/components/image.tsx +0 -1
- package/src/cms/blocks/components/media-card.tsx +0 -1
- package/src/cms/blocks/components/product-carousel/index.tsx +0 -1
- package/src/cms/blocks/components/product-section-fullwidth/index.tsx +0 -1
- package/src/cms/blocks/components/rich-text.tsx +0 -1
- package/src/cms/blocks/components/seo/index.tsx +1 -2
- package/src/cms/blocks/components/services/index.tsx +0 -1
- package/src/cms/blocks/components/services/service-card.tsx +0 -1
- package/src/cms/blocks/components/shared/featured-product-card.tsx +0 -1
- package/src/cms/blocks/components/shared/product-category-item.tsx +1 -1
- package/src/cms/blocks/components/shared/product-grid.tsx +0 -1
- package/src/cms/blocks/components/shared/top-categories-card.tsx +0 -1
- package/src/cms/blocks/components/shared/top-rating-product-card.tsx +0 -1
- package/src/cms/blocks/icons/components/category.tsx +0 -1
- package/src/cms/blocks/icons/components/dotted-star.tsx +0 -1
- package/src/cms/blocks/icons/components/fallback-icon.tsx +0 -1
- package/src/cms/blocks/icons/components/gift-box.tsx +0 -1
- package/src/cms/blocks/icons/components/light.tsx +0 -1
- package/src/cms/blocks/icons/components/new-arrival.tsx +0 -1
- package/src/cms/blocks/icons/components/rank-badge.tsx +0 -1
- package/src/components/BazaarTextField.tsx +7 -7
- package/src/components/CountryInput.stories.tsx +4 -2
- package/src/components/T/__tests__/T.test.tsx +9 -13
- package/src/components/T/index.tsx +0 -1
- package/src/components/Typography.tsx +0 -1
- package/src/components/_tests_/site-logo.test.tsx +4 -5
- package/src/components/categories/category-list/category-list.tsx +1 -1
- package/src/components/countries-input.tsx +1 -1
- package/src/components/nav-link/nav-link.tsx +2 -8
- package/src/components/navbar/mega-menu/mega-menu.tsx +7 -1
- package/src/components/navbar/mobile-menu/mobile-menu.test.tsx +2 -1
- package/src/components/navbar/nav-list/cms-nav.test.tsx +0 -1
- package/src/components/no-records.tsx +0 -1
- package/src/components/product-cards/favorite-button.tsx +0 -1
- package/src/components/product-cards/product-card/styles/index.ts +2 -2
- package/src/components/product-cards/product-card-plp-list/ProductCardPlpList.stories.tsx +0 -1
- package/src/components/product-cards/product-card-search/ProductCardSearch.stories.tsx +0 -1
- package/src/components/product-dimensions/Dimensions.stories.tsx +2 -4
- package/src/components/product-dimensions/compound/dimensions-group-list-chip.tsx +1 -1
- package/src/components/product-dimensions/compound/dimensions.tsx +1 -1
- package/src/components/product-quantity-buttons/ProductQuantity.stories.tsx +1 -3
- package/src/components/product-quantity-buttons/__tests__/product-quantity-buttons.test.tsx +3 -2
- package/src/components/product-quantity-buttons/compound/quantity-buttons-root.tsx +1 -1
- package/src/components/product-quantity-buttons/index.tsx +0 -1
- package/src/components/product-quantity-variants/ProductQuantity.stories.tsx +1 -3
- package/src/components/products-view/ProductsGrid.stories.tsx +1 -3
- package/src/components/products-view/ProductsList.stories.tsx +1 -3
- package/src/components/products-view/__tests__/products-view.test.tsx +20 -9
- package/src/components/products-view/index.tsx +1 -1
- package/src/components/scrollbar/Scrollbar.stories.tsx +1 -3
- package/src/components/scrollbar/__tests__/scrollbar.test.tsx +7 -1
- package/src/components/search-bar/SearchBarLarge.stories.tsx +1 -3
- package/src/components/search-bar/compound/results-item-lg.tsx +0 -1
- package/src/components/search-bar/compound/results-item-sm.tsx +0 -1
- package/src/components/search-bar/compound/textfield-adornment-category.tsx +0 -1
- package/src/components/search-bar/compound/textfield.tsx +0 -1
- package/src/components/search-bar/utils/findCategoryNameById.ts +3 -3
- package/src/components/section/__tests__/section.test.tsx +14 -7
- package/src/components/side-nav/SideNav.stories.tsx +1 -1
- package/src/components/site-logo.tsx +8 -1
- package/src/components/wishlist-dialogs/add-to-wishlist/add-to-wishlist-button.tsx +1 -1
- package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-bottom-ui.tsx +1 -1
- package/src/components/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-header.tsx +0 -1
- package/src/components/wishlist-dialogs/create-wishlist/compound/create-wishlist-bottom-ui.tsx +1 -1
- package/src/hooks/use-nextauth-session.ts +4 -1
- package/src/lib/lib.ts +1 -1
- package/src/lib/page-slugs.ts +0 -2
- package/src/lib/refiner-utils.ts +3 -3
- package/src/pages/account/account-navigation.tsx +17 -20
- package/src/pages/account/addresses/address-form.tsx +15 -7
- package/src/pages/account/dashboard-header.tsx +1 -1
- package/src/pages/cart/checkout-form.tsx +1 -1
- package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +1 -1
- package/src/pages/checkout/checkout-alt-form/steps/address/new-address-form.tsx +1 -1
- package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +5 -2
- package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +1 -1
- package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +1 -1
- package/src/pages/confirmation/address.tsx +1 -1
- package/src/pages/confirmation/confirmation-summary.tsx +1 -1
- package/src/pages/product-details/currency.ts +1 -1
- package/src/pages/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +4 -1
- package/src/pages/product-list/breadcrumbs.tsx +0 -1
- package/src/pages/product-list/list-filter.tsx +0 -1
- package/src/pages/product-list/product-list-context.tsx +1 -1
- package/src/pages/product-list/product-list-view.tsx +2 -2
- package/src/pages/product-list/selected-facets.tsx +1 -1
- package/src/pages/quickorder/context.ts +8 -2
- package/src/pages/quickorder/order-upload.tsx +26 -8
- package/src/pages/quickorder/provider.tsx +8 -9
- package/src/pages/quickorder/quick-order.tsx +17 -11
- package/src/providers/nav-provider/index.tsx +1 -1
- package/tsconfig.json +0 -1
package/.storybook/main.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import { dirname, join, resolve } from "node:path";
|
|
1
2
|
import type { StorybookConfig } from "@storybook/react-webpack5";
|
|
2
3
|
|
|
3
|
-
import { dirname, join, resolve } from "path";
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* This function is used to resolve the absolute path of a package.
|
|
7
6
|
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
|
|
8
7
|
*/
|
|
9
|
-
function getAbsolutePath(value: string):
|
|
8
|
+
function getAbsolutePath(value: string): string {
|
|
10
9
|
return dirname(require.resolve(join(value, "package.json")));
|
|
11
10
|
}
|
|
12
11
|
const config: StorybookConfig = {
|
package/.storybook/preview.tsx
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { Preview } from "@storybook/react-webpack5";
|
|
2
2
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
3
3
|
import { NextIntlClientProvider } from "next-intl";
|
|
4
|
-
import React from "react";
|
|
5
|
-
import { sb } from "storybook/test";
|
|
6
4
|
import messages from "../../../../../storefront/messages/en-US.json";
|
|
7
5
|
import { queryClient } from "../__mocks__/query-client.ts";
|
|
8
6
|
import { NotificationProvider } from "../src/providers/notifications/notification-context.tsx";
|
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
import { queryClient } from "./query-client";
|
|
2
2
|
|
|
3
|
+
type MutationInput = {
|
|
4
|
+
cartLine?: Array<{ Quantity?: number }>;
|
|
5
|
+
} & Record<string, unknown>;
|
|
6
|
+
|
|
7
|
+
type CartMockData = {
|
|
8
|
+
CartLines: Array<Record<string, unknown>>;
|
|
9
|
+
} & Record<string, unknown>;
|
|
10
|
+
|
|
3
11
|
/**
|
|
4
12
|
*
|
|
5
13
|
* Mocking up at the react-query level because our components
|
|
6
14
|
* rely on react-query for triggering re-renders and state subscriptions.
|
|
7
15
|
*
|
|
8
16
|
*/
|
|
9
|
-
export const useCreateMutation = (apiManagerPath:
|
|
17
|
+
export const useCreateMutation = (apiManagerPath: readonly unknown[]) => {
|
|
10
18
|
switch (apiManagerPath[1]) {
|
|
11
19
|
case "updateCartLines":
|
|
12
20
|
return {
|
|
13
|
-
mutate: (input:
|
|
21
|
+
mutate: (input: MutationInput) => {
|
|
14
22
|
console.log(input);
|
|
15
|
-
queryClient.setQueryData(["cart"], (oldData:
|
|
23
|
+
queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
|
|
16
24
|
...oldData,
|
|
17
25
|
CartLines: [
|
|
18
26
|
{
|
|
19
27
|
...oldData.CartLines[0],
|
|
20
|
-
Quantity: input?.
|
|
28
|
+
Quantity: input.cartLine?.[0]?.Quantity,
|
|
21
29
|
},
|
|
22
30
|
],
|
|
23
31
|
}));
|
|
@@ -25,9 +33,9 @@ export const useCreateMutation = (apiManagerPath: any) => {
|
|
|
25
33
|
};
|
|
26
34
|
case "deleteCartLine":
|
|
27
35
|
return {
|
|
28
|
-
mutate: (input:
|
|
36
|
+
mutate: (input: MutationInput) => {
|
|
29
37
|
console.log(input);
|
|
30
|
-
queryClient.setQueryData(["cart"], (oldData:
|
|
38
|
+
queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
|
|
31
39
|
...oldData,
|
|
32
40
|
CartLines: [],
|
|
33
41
|
}));
|
|
@@ -35,9 +43,9 @@ export const useCreateMutation = (apiManagerPath: any) => {
|
|
|
35
43
|
};
|
|
36
44
|
case "addToCart":
|
|
37
45
|
return {
|
|
38
|
-
mutate: (input:
|
|
46
|
+
mutate: (input: MutationInput) => {
|
|
39
47
|
console.log(input);
|
|
40
|
-
queryClient.setQueryData(["cart"], (oldData:
|
|
48
|
+
queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
|
|
41
49
|
...oldData,
|
|
42
50
|
CartLines: [
|
|
43
51
|
{
|
package/__mocks__/next-image.tsx
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
// __mocks__/next/image.js
|
|
2
|
-
|
|
2
|
+
type ImageProps = {
|
|
3
|
+
alt?: string;
|
|
4
|
+
} & Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
const Image = (props: ImageProps) => {
|
|
3
7
|
const { alt = "", ...rest } = props;
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
return {
|
|
10
|
+
$$typeof: Symbol.for("react.element"),
|
|
11
|
+
type: "img",
|
|
12
|
+
key: null,
|
|
13
|
+
ref: null,
|
|
14
|
+
props: { alt, ...rest },
|
|
15
|
+
};
|
|
7
16
|
};
|
|
8
17
|
|
|
9
18
|
export default Image;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evenicanpm/storefront-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Core module for D365/e4 Headless Storefront",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"esbuild": "0.19.11"
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "8227589295d3d63586f9b26fff8d643b070989b3"
|
|
53
53
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
1
|
import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
|
|
4
2
|
import type { CategoryApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
|
|
5
3
|
import {
|
|
@@ -7,6 +5,7 @@ import {
|
|
|
7
5
|
mapCategoryToHierarchy,
|
|
8
6
|
} from "@evenicanpm/storefront-core/src/lib/category-helpers";
|
|
9
7
|
import { getCategoriesAsync } from "@msdyn365-commerce/retail-proxy/dist/DataActions/CategoriesDataActions.g";
|
|
8
|
+
import type { IContext } from "@msdyn365-commerce/retail-proxy/dist/Interfaces/ICallerContext";
|
|
10
9
|
|
|
11
10
|
class D365CategoriesDatasource
|
|
12
11
|
extends D365DatasourceBase
|
|
@@ -16,7 +15,7 @@ class D365CategoriesDatasource
|
|
|
16
15
|
const context = this.context;
|
|
17
16
|
try {
|
|
18
17
|
const categories = await getCategoriesAsync(
|
|
19
|
-
context as
|
|
18
|
+
context as unknown as IContext,
|
|
20
19
|
context.requestContext.apiSettings.channelId,
|
|
21
20
|
0,
|
|
22
21
|
);
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
1
|
import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
|
|
4
2
|
import { D365ProductDatasource } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-product.datasource";
|
|
5
3
|
import type {
|
|
6
4
|
GetSalesOrderDetailsInput,
|
|
7
5
|
SalesOrder,
|
|
6
|
+
SalesOrderInput,
|
|
8
7
|
} from "@evenicanpm/storefront-core/src/api-manager/schemas/order.schema";
|
|
9
8
|
import type { OrderApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
|
|
10
9
|
import {
|
|
@@ -13,10 +12,11 @@ import {
|
|
|
13
12
|
SearchLocation,
|
|
14
13
|
} from "@msdyn365-commerce/retail-proxy";
|
|
15
14
|
import { getSalesOrderDetailsByTransactionIdAsync } from "@msdyn365-commerce/retail-proxy/dist/DataActions/SalesOrdersDataActions.g";
|
|
15
|
+
import type { IContext } from "@msdyn365-commerce/retail-proxy/dist/Interfaces/ICallerContext";
|
|
16
16
|
import uniq from "lodash/uniq";
|
|
17
17
|
|
|
18
18
|
class D365OrderDatasource extends D365DatasourceBase implements OrderApi {
|
|
19
|
-
getSalesOrders(): Promise<
|
|
19
|
+
getSalesOrders(_input: SalesOrderInput): Promise<SalesOrder[]> {
|
|
20
20
|
throw new Error("Method not implemented.");
|
|
21
21
|
}
|
|
22
22
|
async getSalesOrderDetails(
|
|
@@ -26,7 +26,7 @@ class D365OrderDatasource extends D365DatasourceBase implements OrderApi {
|
|
|
26
26
|
const { transactionId } = input;
|
|
27
27
|
try {
|
|
28
28
|
const data = await getSalesOrderDetailsByTransactionIdAsync(
|
|
29
|
-
context as
|
|
29
|
+
context as unknown as IContext,
|
|
30
30
|
transactionId,
|
|
31
31
|
SearchLocation.All,
|
|
32
32
|
);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
1
|
import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
|
|
4
2
|
import type {
|
|
5
3
|
ChannelConfiguration,
|
|
@@ -11,6 +9,7 @@ import type { OrganizationApi } from "@evenicanpm/storefront-core/src/api-manage
|
|
|
11
9
|
import type {
|
|
12
10
|
ChannelDeliveryOption,
|
|
13
11
|
ICallerContext,
|
|
12
|
+
IContext,
|
|
14
13
|
} from "@msdyn365-commerce/retail-proxy";
|
|
15
14
|
import {
|
|
16
15
|
getChannelDeliveryOptionsAsync,
|
|
@@ -23,15 +22,23 @@ class D365OrganizationDatasource
|
|
|
23
22
|
extends D365DatasourceBase
|
|
24
23
|
implements OrganizationApi
|
|
25
24
|
{
|
|
25
|
+
private getRetailContext(context: ICallerContext): IContext {
|
|
26
|
+
return context as unknown as IContext;
|
|
27
|
+
}
|
|
28
|
+
|
|
26
29
|
async getStoresList(
|
|
27
30
|
input: GetOrgUnitLocationsByAreaInput,
|
|
28
31
|
): Promise<OrgUnitLocationReturnType[]> {
|
|
29
32
|
const context = this.context;
|
|
30
33
|
const { queryResultSettings, searchArea, deliveryModeTypeFilter } = input;
|
|
31
|
-
const ctx
|
|
32
|
-
|
|
34
|
+
const ctx: IContext & {
|
|
35
|
+
queryResultSettings?: GetOrgUnitLocationsByAreaInput["queryResultSettings"];
|
|
36
|
+
} = {
|
|
37
|
+
...this.getRetailContext(context),
|
|
38
|
+
queryResultSettings,
|
|
39
|
+
};
|
|
33
40
|
const locations = await getOrgUnitLocationsByAreaAsync(
|
|
34
|
-
ctx
|
|
41
|
+
ctx,
|
|
35
42
|
searchArea,
|
|
36
43
|
deliveryModeTypeFilter,
|
|
37
44
|
);
|
|
@@ -56,7 +63,7 @@ class D365OrganizationDatasource
|
|
|
56
63
|
|
|
57
64
|
async getChannelConfiguration(): Promise<ChannelConfiguration> {
|
|
58
65
|
const context = this.context;
|
|
59
|
-
return getOrgUnitConfigurationAsync(context
|
|
66
|
+
return getOrgUnitConfigurationAsync(this.getRetailContext(context));
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
async getStoreHours(
|
|
@@ -65,7 +72,7 @@ class D365OrganizationDatasource
|
|
|
65
72
|
context: ICallerContext,
|
|
66
73
|
): Promise<OrgUnitLocationReturnType> {
|
|
67
74
|
const storeHours = await getStoreHoursAsync(
|
|
68
|
-
context
|
|
75
|
+
this.getRetailContext(context),
|
|
69
76
|
store.OrgUnitNumber,
|
|
70
77
|
);
|
|
71
78
|
return {
|
|
@@ -81,7 +88,7 @@ class D365OrganizationDatasource
|
|
|
81
88
|
): Promise<ChannelDeliveryOption[] | undefined> {
|
|
82
89
|
const DELIVERY_OPTION_TYPE = 4; //This is to get all pickup modes, not sure where the number comes from.
|
|
83
90
|
const channelDeliveryOptions = await getChannelDeliveryOptionsAsync(
|
|
84
|
-
context
|
|
91
|
+
this.getRetailContext(context),
|
|
85
92
|
channelList,
|
|
86
93
|
DELIVERY_OPTION_TYPE,
|
|
87
94
|
);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
1
|
import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
|
|
4
2
|
import D365CategoriesDatasource from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-categories.datasource";
|
|
5
3
|
import { createDimSearchCriteria } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/utils/create-search-criteria";
|
|
@@ -7,6 +5,7 @@ import {
|
|
|
7
5
|
testMedia,
|
|
8
6
|
validateAndSetMediaUrls,
|
|
9
7
|
} from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/utils/product-images-utils";
|
|
8
|
+
import type { CategoriesQuery } from "@evenicanpm/storefront-core/src/api-manager/datasources/e4/graphqlRequestSdk";
|
|
10
9
|
import type {
|
|
11
10
|
GetProductByIdInput,
|
|
12
11
|
GetProductByIdsInput,
|
|
@@ -20,6 +19,8 @@ import type {
|
|
|
20
19
|
import type { ProductApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
|
|
21
20
|
import {
|
|
22
21
|
executeAction,
|
|
22
|
+
type ICallerContext,
|
|
23
|
+
type IContext,
|
|
23
24
|
type InventoryAvailabilitySearchCriteria,
|
|
24
25
|
type IQueryResultSettings,
|
|
25
26
|
type ProductPrice,
|
|
@@ -56,6 +57,10 @@ const setFullProductImgUrl = async (
|
|
|
56
57
|
};
|
|
57
58
|
|
|
58
59
|
class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
60
|
+
private getRetailContext(context: ICallerContext): IContext {
|
|
61
|
+
return context as unknown as IContext;
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
/**
|
|
60
65
|
* Gets product by id, sets full image url and dimension values
|
|
61
66
|
* @params input GetProductByIdInput
|
|
@@ -66,7 +71,7 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
66
71
|
const channelId = context.requestContext.apiSettings.channelId;
|
|
67
72
|
|
|
68
73
|
const product = await getByIdAsync(
|
|
69
|
-
context
|
|
74
|
+
this.getRetailContext(context),
|
|
70
75
|
Number(input.id),
|
|
71
76
|
channelId,
|
|
72
77
|
);
|
|
@@ -79,14 +84,14 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
79
84
|
const dimensionPromises =
|
|
80
85
|
product?.Dimensions?.map(async ({ DimensionTypeValue }) => {
|
|
81
86
|
return getDimensionValuesWithEstimatedAvailabilitiesAsync(
|
|
82
|
-
context
|
|
87
|
+
this.getRetailContext(context),
|
|
83
88
|
product?.RecordId,
|
|
84
89
|
createDimSearchCriteria(DimensionTypeValue),
|
|
85
90
|
);
|
|
86
91
|
}) || [];
|
|
87
92
|
|
|
88
93
|
const attributesPromise = getAttributeValuesAsync(
|
|
89
|
-
context
|
|
94
|
+
this.getRetailContext(context),
|
|
90
95
|
product?.RecordId,
|
|
91
96
|
channelId,
|
|
92
97
|
0,
|
|
@@ -107,7 +112,7 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
107
112
|
async getProductByIds(input: GetProductByIdsInput) {
|
|
108
113
|
const context = this.context;
|
|
109
114
|
const products = await getByIdsAsync(
|
|
110
|
-
context
|
|
115
|
+
this.getRetailContext(context),
|
|
111
116
|
context.requestContext.apiSettings.channelId,
|
|
112
117
|
input?.id.map(Number),
|
|
113
118
|
);
|
|
@@ -159,8 +164,10 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
159
164
|
(r) => r.RefinerSourceValue !== ProductRefinerSource.Price,
|
|
160
165
|
);
|
|
161
166
|
|
|
162
|
-
const ctx
|
|
163
|
-
|
|
167
|
+
const ctx: IContext & { queryResultSettings?: IQueryResultSettings } = {
|
|
168
|
+
...this.getRetailContext(context),
|
|
169
|
+
queryResultSettings: queryResultSettingsWithCount,
|
|
170
|
+
};
|
|
164
171
|
const searchRequest = createSearchByCriteriaInput(
|
|
165
172
|
queryResultSettingsWithCount,
|
|
166
173
|
searchCriteria,
|
|
@@ -178,7 +185,9 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
178
185
|
refinersPromise,
|
|
179
186
|
]);
|
|
180
187
|
|
|
181
|
-
const count =
|
|
188
|
+
const count =
|
|
189
|
+
(searchRequest as { _totalCount?: number })._totalCount ??
|
|
190
|
+
productResults.length;
|
|
182
191
|
const promises = [];
|
|
183
192
|
for (const product of productResults) {
|
|
184
193
|
promises.push(
|
|
@@ -198,7 +207,9 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
198
207
|
|
|
199
208
|
async searchProducts(input: searchProductsInput) {
|
|
200
209
|
const context = this.context;
|
|
201
|
-
const ctx
|
|
210
|
+
const ctx: IContext & { queryResultSettings?: IQueryResultSettings } = {
|
|
211
|
+
...this.getRetailContext(context),
|
|
212
|
+
};
|
|
202
213
|
const queryResultSettings: IQueryResultSettings = {
|
|
203
214
|
Paging: {
|
|
204
215
|
Skip: 0,
|
|
@@ -222,11 +233,10 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
222
233
|
ItemId: id,
|
|
223
234
|
}));
|
|
224
235
|
}
|
|
225
|
-
const observableResults = await searchByCriteriaAsync(
|
|
226
|
-
|
|
227
|
-
|
|
236
|
+
const observableResults = await searchByCriteriaAsync(ctx, searchCriteria);
|
|
237
|
+
const results: ProductSearchResult[] = Array.from(
|
|
238
|
+
observableResults as Iterable<ProductSearchResult>,
|
|
228
239
|
);
|
|
229
|
-
const results: ProductSearchResult[] = Array.from(observableResults as any);
|
|
230
240
|
const promises = [];
|
|
231
241
|
for (const product of results) {
|
|
232
242
|
promises.push(
|
|
@@ -240,18 +250,20 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
240
250
|
return results;
|
|
241
251
|
}
|
|
242
252
|
|
|
243
|
-
async getCategoryList(): Promise<
|
|
253
|
+
async getCategoryList(): Promise<CategoriesQuery> {
|
|
244
254
|
// TODO ? Why is the get category list here and calling the category data source. Can we decide if we want to have a separate one for categories or remove this?
|
|
245
255
|
// agree with above statement - sam
|
|
246
256
|
const categoriesDatasource = new D365CategoriesDatasource(this.context);
|
|
247
257
|
const categories = await categoriesDatasource.getCategories();
|
|
248
|
-
return {
|
|
258
|
+
return {
|
|
259
|
+
categories: { category: categories },
|
|
260
|
+
} as unknown as CategoriesQuery;
|
|
249
261
|
}
|
|
250
262
|
|
|
251
263
|
async getRelatedProducts(input: GetRelatedProductsInput) {
|
|
252
264
|
const context = this.context;
|
|
253
265
|
return getRelatedProductsAsync(
|
|
254
|
-
context
|
|
266
|
+
this.getRetailContext(context),
|
|
255
267
|
context.requestContext.apiSettings.channelId,
|
|
256
268
|
input.id,
|
|
257
269
|
0,
|
|
@@ -270,7 +282,7 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
270
282
|
FilterByChannelFulfillmentGroup: true,
|
|
271
283
|
};
|
|
272
284
|
const data = await getEstimatedAvailabilityAsync(
|
|
273
|
-
context
|
|
285
|
+
this.getRetailContext(context),
|
|
274
286
|
searchCriteria,
|
|
275
287
|
);
|
|
276
288
|
return data;
|
|
@@ -290,7 +302,7 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
|
|
|
290
302
|
|
|
291
303
|
try {
|
|
292
304
|
const data = await getActivePricesAsync(
|
|
293
|
-
context
|
|
305
|
+
this.getRetailContext(context),
|
|
294
306
|
projectionDomain,
|
|
295
307
|
input.productIds,
|
|
296
308
|
new Date(),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
1
|
import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
|
|
4
2
|
import { D365ProductDatasource } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-product.datasource";
|
|
5
3
|
import type { UserApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
|
|
6
4
|
import {
|
|
7
5
|
type CommerceProperty,
|
|
8
6
|
type Customer,
|
|
7
|
+
type ICallerContext,
|
|
8
|
+
type IContext,
|
|
9
9
|
type ProductList,
|
|
10
10
|
type ProductListSearchCriteria,
|
|
11
11
|
ProductListType,
|
|
@@ -39,10 +39,29 @@ import type {
|
|
|
39
39
|
WishlistLinesInput,
|
|
40
40
|
} from "@/schemas/user.schema";
|
|
41
41
|
|
|
42
|
+
type IdTokenClaims = jwt.JwtPayload & {
|
|
43
|
+
emails?: string[];
|
|
44
|
+
email?: string;
|
|
45
|
+
given_name?: string;
|
|
46
|
+
family_name?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function decodeIdTokenClaims(token: string): IdTokenClaims {
|
|
50
|
+
const decoded = jwt.decode(token);
|
|
51
|
+
return decoded as IdTokenClaims;
|
|
52
|
+
}
|
|
53
|
+
|
|
42
54
|
class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
55
|
+
private getRetailContext(context: ICallerContext): IContext {
|
|
56
|
+
return context as unknown as IContext;
|
|
57
|
+
}
|
|
58
|
+
|
|
43
59
|
async updateUser(user: User): Promise<User> {
|
|
44
60
|
const context = this.context;
|
|
45
|
-
const existing: Customer = await readAsync(
|
|
61
|
+
const existing: Customer = await readAsync(
|
|
62
|
+
this.getRetailContext(context),
|
|
63
|
+
"",
|
|
64
|
+
);
|
|
46
65
|
existing.Addresses ??= [];
|
|
47
66
|
existing.Addresses = existing.Addresses.map((addr) => ({
|
|
48
67
|
...addr,
|
|
@@ -55,11 +74,13 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
55
74
|
);
|
|
56
75
|
existing.ExtensionProperties.push({
|
|
57
76
|
Key: propKey,
|
|
58
|
-
Value: { StringValue: user.DefaultShippingAddressId } as
|
|
77
|
+
Value: { StringValue: user.DefaultShippingAddressId } as NonNullable<
|
|
78
|
+
CommerceProperty["Value"]
|
|
79
|
+
>,
|
|
59
80
|
});
|
|
60
81
|
const updated: Customer = await updateAsync(
|
|
61
|
-
context
|
|
62
|
-
existing
|
|
82
|
+
this.getRetailContext(context),
|
|
83
|
+
existing,
|
|
63
84
|
);
|
|
64
85
|
const mappedAddresses = (updated.Addresses ?? []).map((a) => ({
|
|
65
86
|
...a,
|
|
@@ -76,10 +97,10 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
76
97
|
if (!context.requestContext.user.isAuthenticated) return null;
|
|
77
98
|
let customer: Customer;
|
|
78
99
|
try {
|
|
79
|
-
customer = await readAsync(context
|
|
100
|
+
customer = await readAsync(this.getRetailContext(context), "");
|
|
80
101
|
} catch {
|
|
81
|
-
const token =
|
|
82
|
-
customer = await createCustomerAsync(context
|
|
102
|
+
const token = decodeIdTokenClaims(context.requestContext.user.token);
|
|
103
|
+
customer = await createCustomerAsync(this.getRetailContext(context), {
|
|
83
104
|
AccountNumber: "",
|
|
84
105
|
Email: Array.isArray(token.emails) ? token.emails[0] : token.email,
|
|
85
106
|
FirstName: token.given_name,
|
|
@@ -113,11 +134,11 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
113
134
|
|
|
114
135
|
try {
|
|
115
136
|
const wishlists = await searchAsync(
|
|
116
|
-
context
|
|
137
|
+
this.getRetailContext(context),
|
|
117
138
|
productListSearchCriteria,
|
|
118
139
|
);
|
|
119
140
|
const wishlistProductDataPromises = wishlists.map((wishlist) => {
|
|
120
|
-
return wishlistReadAsync(context
|
|
141
|
+
return wishlistReadAsync(this.getRetailContext(context), wishlist.Id);
|
|
121
142
|
});
|
|
122
143
|
const wishlistProductData = await Promise.all(
|
|
123
144
|
wishlistProductDataPromises,
|
|
@@ -134,7 +155,10 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
134
155
|
const context = this.context;
|
|
135
156
|
let wishlist: Wishlist;
|
|
136
157
|
try {
|
|
137
|
-
wishlist = await wishlistReadAsync(
|
|
158
|
+
wishlist = await wishlistReadAsync(
|
|
159
|
+
this.getRetailContext(context),
|
|
160
|
+
input.Id,
|
|
161
|
+
);
|
|
138
162
|
} catch (error) {
|
|
139
163
|
throw new Error(
|
|
140
164
|
`Error getting wishlist details: ${JSON.stringify(error)}`,
|
|
@@ -182,7 +206,7 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
182
206
|
};
|
|
183
207
|
try {
|
|
184
208
|
const wishlist = await createProductListAsync(
|
|
185
|
-
context
|
|
209
|
+
this.getRetailContext(context),
|
|
186
210
|
productList,
|
|
187
211
|
);
|
|
188
212
|
return wishlist;
|
|
@@ -197,7 +221,7 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
197
221
|
throw new Error("Wishlist ID is required for deletion.");
|
|
198
222
|
}
|
|
199
223
|
try {
|
|
200
|
-
await deleteAsync(context
|
|
224
|
+
await deleteAsync(this.getRetailContext(context), { Id: input.Id });
|
|
201
225
|
} catch (error) {
|
|
202
226
|
throw new Error(`Error deleting wishlist: ${error}`);
|
|
203
227
|
}
|
|
@@ -210,7 +234,7 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
210
234
|
|
|
211
235
|
try {
|
|
212
236
|
const updatedProductListLines = await updateProductListLinesAsync(
|
|
213
|
-
context
|
|
237
|
+
this.getRetailContext(context),
|
|
214
238
|
id,
|
|
215
239
|
productListLines,
|
|
216
240
|
);
|
|
@@ -228,7 +252,7 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
228
252
|
|
|
229
253
|
try {
|
|
230
254
|
const updatedProductListLines = await addProductListLinesAsync(
|
|
231
|
-
context
|
|
255
|
+
this.getRetailContext(context),
|
|
232
256
|
id,
|
|
233
257
|
productListLines,
|
|
234
258
|
);
|
|
@@ -245,7 +269,11 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
245
269
|
const id = input.Id;
|
|
246
270
|
const productListLines = input.ProductListLines;
|
|
247
271
|
try {
|
|
248
|
-
await removeProductListLinesAsync(
|
|
272
|
+
await removeProductListLinesAsync(
|
|
273
|
+
this.getRetailContext(context),
|
|
274
|
+
id,
|
|
275
|
+
productListLines,
|
|
276
|
+
);
|
|
249
277
|
} catch (error) {
|
|
250
278
|
throw new Error(`Error removing from wishlist: ${error}`);
|
|
251
279
|
}
|
|
@@ -259,8 +287,8 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
|
|
|
259
287
|
const isQuantityAggregate = true;
|
|
260
288
|
|
|
261
289
|
try {
|
|
262
|
-
const wishlist = copyCartToProductListAsync(
|
|
263
|
-
context
|
|
290
|
+
const wishlist = await copyCartToProductListAsync(
|
|
291
|
+
this.getRetailContext(context),
|
|
264
292
|
productListId,
|
|
265
293
|
cartId,
|
|
266
294
|
isRewrite,
|
|
@@ -16,7 +16,7 @@ export const addressMap = {
|
|
|
16
16
|
{
|
|
17
17
|
key: "[].RecordId",
|
|
18
18
|
transform: (id: string) => {
|
|
19
|
-
if (id !== null) return Number.parseInt(id);
|
|
19
|
+
if (id !== null) return Number.parseInt(id, 10);
|
|
20
20
|
},
|
|
21
21
|
},
|
|
22
22
|
],
|
|
@@ -25,7 +25,7 @@ export const addressMap = {
|
|
|
25
25
|
"[].addressType.id": {
|
|
26
26
|
key: "[].AddressTypeValue",
|
|
27
27
|
transform: (id: string) => {
|
|
28
|
-
if (id !== null) return Number.parseInt(id);
|
|
28
|
+
if (id !== null) return Number.parseInt(id, 10);
|
|
29
29
|
},
|
|
30
30
|
},
|
|
31
31
|
"[].address1": "[].Street",
|
|
@@ -34,7 +34,7 @@ export const productMap = {
|
|
|
34
34
|
{
|
|
35
35
|
key: "RecordId",
|
|
36
36
|
transform: (id: string) => {
|
|
37
|
-
if (id !== null) return Number.parseInt(id);
|
|
37
|
+
if (id !== null) return Number.parseInt(id, 10);
|
|
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 Number.parseInt(id);
|
|
57
|
+
if (id !== null) return Number.parseInt(id, 10);
|
|
58
58
|
},
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
key: "ListingId",
|
|
62
62
|
transform: (id: string) => {
|
|
63
|
-
if (id !== null) return Number.parseInt(id);
|
|
63
|
+
if (id !== null) return Number.parseInt(id, 10);
|
|
64
64
|
},
|
|
65
65
|
},
|
|
66
66
|
],
|
|
@@ -123,23 +123,19 @@ const user: UserApi = {
|
|
|
123
123
|
throw new Error("Error creating wishlist");
|
|
124
124
|
}
|
|
125
125
|
},
|
|
126
|
-
|
|
127
|
-
async updateWishlist(input: WishlistLinesInput): Promise<Wishlist> {
|
|
126
|
+
async updateWishlist(_input: WishlistLinesInput): Promise<Wishlist> {
|
|
128
127
|
throw new Error("updateWishlist not implemented");
|
|
129
128
|
},
|
|
130
129
|
|
|
131
|
-
|
|
132
|
-
async addProductsToWishlist(input: WishlistLinesInput): Promise<Wishlist> {
|
|
130
|
+
async addProductsToWishlist(_input: WishlistLinesInput): Promise<Wishlist> {
|
|
133
131
|
throw new Error("addProductsToWishlist not implemented");
|
|
134
132
|
},
|
|
135
133
|
|
|
136
|
-
|
|
137
|
-
async removeProductsFromWishlist(input: WishlistLinesInput): Promise<void> {
|
|
134
|
+
async removeProductsFromWishlist(_input: WishlistLinesInput): Promise<void> {
|
|
138
135
|
throw new Error("removeProductsFromWishlist not implemented");
|
|
139
136
|
},
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
async copyCartToWishlist(input: CopyCartToWishlistInput): Promise<Wishlist> {
|
|
138
|
+
async copyCartToWishlist(_input: CopyCartToWishlistInput): Promise<Wishlist> {
|
|
143
139
|
throw new Error("copyCartToWishlist not implemented");
|
|
144
140
|
},
|
|
145
141
|
};
|