@magento/peregrine 12.4.0-alpha.1 → 12.5.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Apollo/links/authLink.js +19 -0
- package/lib/Apollo/links/errorLink.js +64 -0
- package/lib/Apollo/links/gqlCacheLink.js +63 -0
- package/lib/Apollo/links/index.js +66 -0
- package/lib/Apollo/links/mutationQueueLink.js +5 -0
- package/lib/Apollo/links/retryLink.js +17 -0
- package/lib/Apollo/links/storeLink.js +22 -0
- package/lib/Apollo/magentoGqlCacheLink.js +3 -59
- package/lib/PeregrineContextProvider/peregrineContextProvider.js +2 -0
- package/lib/context/eventing.js +57 -0
- package/lib/hooks/useDelayedTransition.js +1 -1
- package/lib/talons/AccountInformationPage/useAccountInformationPage.js +15 -1
- package/lib/talons/Adapter/useAdapter.js +23 -200
- package/lib/talons/AddToCartDialog/addToCartDialog.gql.js +8 -0
- package/lib/talons/AddToCartDialog/useAddToCartDialog.js +46 -2
- package/lib/talons/AddressBookPage/useAddressBookPage.js +52 -17
- package/lib/talons/AuthModal/useAuthModal.js +12 -2
- package/lib/talons/Breadcrumbs/useBreadcrumbs.js +7 -2
- package/lib/talons/CartPage/ProductListing/EditModal/__fixtures__/configurableProduct.js +39 -4
- package/lib/talons/CartPage/ProductListing/EditModal/useProductForm.js +47 -3
- package/lib/talons/CartPage/ProductListing/productListingFragments.gql.js +6 -0
- package/lib/talons/CartPage/ProductListing/useProduct.js +49 -3
- package/lib/talons/CartPage/useCartPage.js +15 -0
- package/lib/talons/CheckoutPage/ItemsReview/itemsReviewFragments.gql.js +12 -14
- package/lib/talons/CheckoutPage/OrderConfirmationPage/orderConfirmationPageFragments.gql.js +21 -1
- package/lib/talons/CheckoutPage/OrderConfirmationPage/useCreateAccount.js +15 -1
- package/lib/talons/CheckoutPage/PaymentInformation/useEditModal.js +10 -1
- package/lib/talons/CheckoutPage/PaymentInformation/usePaymentInformation.js +14 -0
- package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/useCustomerForm.js +38 -1
- package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/useGuestForm.js +15 -1
- package/lib/talons/CheckoutPage/ShippingInformation/useShippingInformation.js +13 -0
- package/lib/talons/CheckoutPage/ShippingMethod/useShippingMethod.js +28 -6
- package/lib/talons/CheckoutPage/checkoutPage.gql.js +3 -0
- package/lib/talons/CheckoutPage/useCheckoutPage.js +79 -3
- package/lib/talons/Cms/useCmsPage.js +14 -0
- package/lib/talons/CreateAccount/useCreateAccount.js +15 -1
- package/lib/talons/FilterModal/helpers.js +8 -2
- package/lib/talons/FilterModal/useFilterModal.js +1 -0
- package/lib/talons/FilterSidebar/useFilterSidebar.js +1 -0
- package/lib/talons/Gallery/useAddToCartButton.js +24 -12
- package/lib/talons/Gallery/useGalleryItem.js +85 -1
- package/lib/talons/Header/storeSwitcher.gql.js +1 -16
- package/lib/talons/Header/useAccountMenu.js +21 -2
- package/lib/talons/Header/useStoreSwitcher.js +40 -93
- package/lib/talons/MagentoRoute/useMagentoRoute.js +7 -7
- package/lib/talons/MiniCart/ProductList/productListFragments.gql.js +4 -0
- package/lib/talons/MiniCart/useMiniCart.js +46 -3
- package/lib/talons/ProductFullDetail/useProductFullDetail.js +37 -9
- package/lib/talons/RootComponents/Category/categoryContent.gql.js +2 -0
- package/lib/talons/RootComponents/Category/categoryFragments.gql.js +7 -0
- package/lib/talons/RootComponents/Category/useCategory.js +4 -1
- package/lib/talons/RootComponents/Category/useCategoryContent.js +35 -13
- package/lib/talons/RootComponents/Product/productDetailFragment.gql.js +6 -0
- package/lib/talons/RootComponents/Product/useProduct.js +27 -0
- package/lib/talons/SearchBar/index.js +1 -0
- package/lib/talons/SearchBar/useAutocomplete.js +18 -1
- package/lib/talons/SearchBar/useSuggestedProduct.js +99 -0
- package/lib/talons/SearchPage/searchPage.gql.js +7 -0
- package/lib/talons/SearchPage/useSearchPage.js +56 -28
- package/lib/talons/SignIn/useSignIn.js +19 -2
- package/lib/talons/WishlistPage/useWishlistItem.js +36 -1
- package/lib/talons/WishlistPage/wishlistItemFragments.gql.js +3 -0
- package/lib/util/makeUrl.js +1 -1
- package/package.json +5 -4
|
@@ -2,6 +2,7 @@ import { useQuery } from '@apollo/client';
|
|
|
2
2
|
import { useCallback, useMemo } from 'react';
|
|
3
3
|
import { useLocation } from 'react-router-dom';
|
|
4
4
|
import { useDropdown } from '@magento/peregrine/lib/hooks/useDropdown';
|
|
5
|
+
import { useAwaitQuery } from '@magento/peregrine/lib/hooks/useAwaitQuery';
|
|
5
6
|
import { BrowserPersistence } from '@magento/peregrine/lib/util';
|
|
6
7
|
import mergeOperations from '../../util/shallowMerge';
|
|
7
8
|
import DEFAULT_OPERATIONS from './storeSwitcher.gql';
|
|
@@ -13,11 +14,8 @@ const mapAvailableOptions = (config, stores) => {
|
|
|
13
14
|
|
|
14
15
|
return stores.reduce((map, store) => {
|
|
15
16
|
const {
|
|
16
|
-
category_url_suffix,
|
|
17
|
-
store_code: code,
|
|
18
17
|
default_display_currency_code: currency,
|
|
19
18
|
locale,
|
|
20
|
-
product_url_suffix,
|
|
21
19
|
secure_base_media_url,
|
|
22
20
|
store_code: storeCode,
|
|
23
21
|
store_group_code: storeGroupCode,
|
|
@@ -26,14 +24,11 @@ const mapAvailableOptions = (config, stores) => {
|
|
|
26
24
|
store_sort_order: sortOrder
|
|
27
25
|
} = store;
|
|
28
26
|
|
|
29
|
-
const isCurrent =
|
|
27
|
+
const isCurrent = storeCode === configCode;
|
|
30
28
|
const option = {
|
|
31
|
-
category_url_suffix,
|
|
32
|
-
code,
|
|
33
29
|
currency,
|
|
34
30
|
isCurrent,
|
|
35
31
|
locale,
|
|
36
|
-
product_url_suffix,
|
|
37
32
|
secure_base_media_url,
|
|
38
33
|
sortOrder,
|
|
39
34
|
storeCode,
|
|
@@ -42,14 +37,15 @@ const mapAvailableOptions = (config, stores) => {
|
|
|
42
37
|
storeName
|
|
43
38
|
};
|
|
44
39
|
|
|
45
|
-
return map.set(
|
|
40
|
+
return map.set(storeCode, option);
|
|
46
41
|
}, new Map());
|
|
47
42
|
};
|
|
48
43
|
|
|
49
44
|
/**
|
|
50
45
|
* The useStoreSwitcher talon complements the StoreSwitcher component.
|
|
51
46
|
*
|
|
52
|
-
* @param {
|
|
47
|
+
* @param {Array<Object>} [props.availableRoutes] - Hardcoded app routes.
|
|
48
|
+
* @param {Object} [props.operations] - GraphQL operations to be run by the hook.
|
|
53
49
|
*
|
|
54
50
|
* @returns {Map} talonProps.availableStores - Details about the available store views.
|
|
55
51
|
* @returns {String} talonProps.currentStoreName - Name of the current store view.
|
|
@@ -62,12 +58,21 @@ const mapAvailableOptions = (config, stores) => {
|
|
|
62
58
|
|
|
63
59
|
export const useStoreSwitcher = (props = {}) => {
|
|
64
60
|
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
61
|
+
const { availableRoutes = [] } = props;
|
|
62
|
+
const internalRoutes = useMemo(() => {
|
|
63
|
+
return availableRoutes.map(path => {
|
|
64
|
+
if (path.exact) {
|
|
65
|
+
return path.pattern;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}, [availableRoutes]);
|
|
69
|
+
|
|
65
70
|
const {
|
|
66
71
|
getStoreConfigData,
|
|
67
72
|
getRouteData,
|
|
68
73
|
getAvailableStoresData
|
|
69
74
|
} = operations;
|
|
70
|
-
const { pathname } = useLocation();
|
|
75
|
+
const { pathname, search: searchParams } = useLocation();
|
|
71
76
|
const {
|
|
72
77
|
elementRef: storeMenuRef,
|
|
73
78
|
expanded: storeMenuIsOpen,
|
|
@@ -80,10 +85,7 @@ export const useStoreSwitcher = (props = {}) => {
|
|
|
80
85
|
nextFetchPolicy: 'cache-first'
|
|
81
86
|
});
|
|
82
87
|
|
|
83
|
-
const
|
|
84
|
-
fetchPolicy: 'cache-first',
|
|
85
|
-
variables: { url: pathname }
|
|
86
|
-
});
|
|
88
|
+
const fetchRouteData = useAwaitQuery(getRouteData);
|
|
87
89
|
|
|
88
90
|
const { data: availableStoresData } = useQuery(getAvailableStoresData, {
|
|
89
91
|
fetchPolicy: 'cache-and-network',
|
|
@@ -102,18 +104,6 @@ export const useStoreSwitcher = (props = {}) => {
|
|
|
102
104
|
}
|
|
103
105
|
}, [storeConfigData]);
|
|
104
106
|
|
|
105
|
-
const currentStoreCode = useMemo(() => {
|
|
106
|
-
if (storeConfigData) {
|
|
107
|
-
return storeConfigData.storeConfig.store_code;
|
|
108
|
-
}
|
|
109
|
-
}, [storeConfigData]);
|
|
110
|
-
|
|
111
|
-
const pageType = useMemo(() => {
|
|
112
|
-
if (routeData && routeData.route) {
|
|
113
|
-
return routeData.route.type;
|
|
114
|
-
}
|
|
115
|
-
}, [routeData]);
|
|
116
|
-
|
|
117
107
|
// availableStores => mapped options or empty map if undefined.
|
|
118
108
|
const availableStores = useMemo(() => {
|
|
119
109
|
return (
|
|
@@ -146,51 +136,35 @@ export const useStoreSwitcher = (props = {}) => {
|
|
|
146
136
|
return groups;
|
|
147
137
|
}, [availableStores]);
|
|
148
138
|
|
|
149
|
-
// Get pathname with suffix based on page type
|
|
150
139
|
const getPathname = useCallback(
|
|
151
|
-
storeCode => {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (pageType === 'PRODUCT') {
|
|
168
|
-
const currentSuffix =
|
|
169
|
-
availableStores.get(currentStoreCode).product_url_suffix ||
|
|
170
|
-
'';
|
|
171
|
-
const newSuffix =
|
|
172
|
-
availableStores.get(storeCode).product_url_suffix || '';
|
|
173
|
-
|
|
174
|
-
return currentSuffix
|
|
175
|
-
? pathname.replace(currentSuffix, newSuffix)
|
|
176
|
-
: `${pathname}${newSuffix}`;
|
|
140
|
+
async storeCode => {
|
|
141
|
+
if (pathname === '' || pathname === '/') return '';
|
|
142
|
+
let newPath = '';
|
|
143
|
+
if (internalRoutes.includes(pathname)) {
|
|
144
|
+
newPath = pathname;
|
|
145
|
+
} else {
|
|
146
|
+
const { data: routeData } = await fetchRouteData({
|
|
147
|
+
fetchPolicy: 'no-cache',
|
|
148
|
+
variables: {
|
|
149
|
+
url: pathname
|
|
150
|
+
},
|
|
151
|
+
context: { headers: { store: storeCode } }
|
|
152
|
+
});
|
|
153
|
+
if (routeData.route) {
|
|
154
|
+
newPath = routeData.route.relative_url;
|
|
155
|
+
}
|
|
177
156
|
}
|
|
178
|
-
|
|
179
|
-
// search.html ...etc
|
|
180
|
-
return pathname;
|
|
157
|
+
return newPath.startsWith('/') ? newPath.substr(1) : newPath;
|
|
181
158
|
},
|
|
182
|
-
[
|
|
159
|
+
[pathname, fetchRouteData, internalRoutes]
|
|
183
160
|
);
|
|
184
161
|
|
|
185
162
|
const handleSwitchStore = useCallback(
|
|
186
163
|
// Change store view code and currency to be used in Apollo link request headers
|
|
187
|
-
storeCode => {
|
|
164
|
+
async storeCode => {
|
|
188
165
|
// Do nothing when store view is not present in available stores
|
|
189
166
|
if (!availableStores.has(storeCode)) return;
|
|
190
167
|
|
|
191
|
-
const pathName = getPathname(storeCode);
|
|
192
|
-
const params = globalThis.location.search || '';
|
|
193
|
-
|
|
194
168
|
storage.setItem('store_view_code', storeCode);
|
|
195
169
|
storage.setItem(
|
|
196
170
|
'store_view_currency',
|
|
@@ -200,43 +174,16 @@ export const useStoreSwitcher = (props = {}) => {
|
|
|
200
174
|
'store_view_secure_base_media_url',
|
|
201
175
|
availableStores.get(storeCode).secure_base_media_url
|
|
202
176
|
);
|
|
177
|
+
const pathName = await getPathname(storeCode);
|
|
178
|
+
const newPath = pathName ? `/${pathName}${searchParams}` : '';
|
|
203
179
|
|
|
204
|
-
// Handle updating the URL if the store code should be present.
|
|
205
|
-
// In this block we use `globalThis.location.assign` to work around the
|
|
206
|
-
// static React Router basename, which is changed on initialization.
|
|
207
180
|
if (process.env.USE_STORE_CODE_IN_URL === 'true') {
|
|
208
|
-
|
|
209
|
-
if (pathName !== '' && pathName !== '/') {
|
|
210
|
-
const [, pathStoreCode] = pathName.split('/');
|
|
211
|
-
|
|
212
|
-
// If the current store code is in the url, replace it with
|
|
213
|
-
// the new one.
|
|
214
|
-
if (
|
|
215
|
-
availableStores.has(pathStoreCode) &&
|
|
216
|
-
availableStores.get(pathStoreCode).isCurrent
|
|
217
|
-
) {
|
|
218
|
-
const newPath = `${pathName.replace(
|
|
219
|
-
`/${pathStoreCode}`,
|
|
220
|
-
`/${storeCode}`
|
|
221
|
-
)}${params}`;
|
|
222
|
-
|
|
223
|
-
globalThis.location.assign(newPath);
|
|
224
|
-
} else {
|
|
225
|
-
// Otherwise include it and reload.
|
|
226
|
-
const newPath = `/${storeCode}${pathName}${params}`;
|
|
227
|
-
|
|
228
|
-
globalThis.location.assign(newPath);
|
|
229
|
-
}
|
|
230
|
-
} else {
|
|
231
|
-
globalThis.location.assign(`/${storeCode}`);
|
|
232
|
-
}
|
|
181
|
+
globalThis.location.assign(`/${storeCode}${newPath || ''}`);
|
|
233
182
|
} else {
|
|
234
|
-
|
|
235
|
-
// are saved in local storage.
|
|
236
|
-
globalThis.location.assign(`${pathName}${params}`);
|
|
183
|
+
globalThis.location.assign(`${newPath || '/'}`);
|
|
237
184
|
}
|
|
238
185
|
},
|
|
239
|
-
[availableStores, getPathname]
|
|
186
|
+
[availableStores, getPathname, searchParams]
|
|
240
187
|
);
|
|
241
188
|
|
|
242
189
|
const handleTriggerClick = useCallback(() => {
|
|
@@ -109,6 +109,13 @@ export const useMagentoRoute = (props = {}) => {
|
|
|
109
109
|
} else if (routeError) {
|
|
110
110
|
// ERROR
|
|
111
111
|
routeData = { hasError: true, routeError };
|
|
112
|
+
} else if (empty && fetchedPathname.current === pathname && !loading) {
|
|
113
|
+
// NOT FOUND
|
|
114
|
+
routeData = { isNotFound: true };
|
|
115
|
+
} else if (nextRootComponent) {
|
|
116
|
+
// LOADING with full page shimmer
|
|
117
|
+
showPageLoader = true;
|
|
118
|
+
routeData = { isLoading: true, shimmer: nextRootComponent };
|
|
112
119
|
} else if (redirect) {
|
|
113
120
|
// REDIRECT
|
|
114
121
|
routeData = {
|
|
@@ -117,13 +124,6 @@ export const useMagentoRoute = (props = {}) => {
|
|
|
117
124
|
? relative_url
|
|
118
125
|
: '/' + relative_url
|
|
119
126
|
};
|
|
120
|
-
} else if (empty && fetchedPathname.current === pathname && !loading) {
|
|
121
|
-
// NOT FOUND
|
|
122
|
-
routeData = { isNotFound: true };
|
|
123
|
-
} else if (nextRootComponent) {
|
|
124
|
-
// LOADING with full page shimmer
|
|
125
|
-
showPageLoader = true;
|
|
126
|
-
routeData = { isLoading: true, shimmer: nextRootComponent };
|
|
127
127
|
} else {
|
|
128
128
|
// LOADING
|
|
129
129
|
const isInitialLoad = !isInitialized;
|
|
@@ -10,6 +10,7 @@ export const ProductListFragment = gql`
|
|
|
10
10
|
product {
|
|
11
11
|
uid
|
|
12
12
|
name
|
|
13
|
+
sku
|
|
13
14
|
url_key
|
|
14
15
|
thumbnail {
|
|
15
16
|
url
|
|
@@ -36,6 +37,9 @@ export const ProductListFragment = gql`
|
|
|
36
37
|
currency
|
|
37
38
|
value
|
|
38
39
|
}
|
|
40
|
+
total_item_discount {
|
|
41
|
+
value
|
|
42
|
+
}
|
|
39
43
|
}
|
|
40
44
|
quantity
|
|
41
45
|
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useMemo } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
2
2
|
import { useHistory } from 'react-router-dom';
|
|
3
3
|
import { useQuery, useMutation } from '@apollo/client';
|
|
4
4
|
|
|
@@ -6,9 +6,11 @@ import { useCartContext } from '../../context/cart';
|
|
|
6
6
|
import { deriveErrorMessage } from '../../util/deriveErrorMessage';
|
|
7
7
|
import mergeOperations from '../../util/shallowMerge';
|
|
8
8
|
import DEFAULT_OPERATIONS from './miniCart.gql';
|
|
9
|
+
import { useEventingContext } from '../../context/eventing';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
*
|
|
13
|
+
* @param {Boolean} props.isOpen - True if the mini cart is open
|
|
12
14
|
* @param {Function} props.setIsOpen - Function to toggle the mini cart
|
|
13
15
|
* @param {DocumentNode} props.operations.miniCartQuery - Query to fetch mini cart data
|
|
14
16
|
* @param {DocumentNode} props.operations.removeItemMutation - Mutation to remove an item from cart
|
|
@@ -27,7 +29,9 @@ import DEFAULT_OPERATIONS from './miniCart.gql';
|
|
|
27
29
|
* }
|
|
28
30
|
*/
|
|
29
31
|
export const useMiniCart = props => {
|
|
30
|
-
const { setIsOpen } = props;
|
|
32
|
+
const { isOpen, setIsOpen } = props;
|
|
33
|
+
|
|
34
|
+
const [, { dispatch }] = useEventingContext();
|
|
31
35
|
|
|
32
36
|
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
33
37
|
const {
|
|
@@ -106,11 +110,38 @@ export const useMiniCart = props => {
|
|
|
106
110
|
itemId: id
|
|
107
111
|
}
|
|
108
112
|
});
|
|
113
|
+
|
|
114
|
+
const [product] = productList.filter(
|
|
115
|
+
p => (p.uid || p.id) === id
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const selectedOptionsLabels =
|
|
119
|
+
product.configurable_options?.map(
|
|
120
|
+
({ option_label, value_label }) => ({
|
|
121
|
+
attribute: option_label,
|
|
122
|
+
value: value_label
|
|
123
|
+
})
|
|
124
|
+
) || null;
|
|
125
|
+
|
|
126
|
+
dispatch({
|
|
127
|
+
type: 'CART_REMOVE_ITEM',
|
|
128
|
+
payload: {
|
|
129
|
+
cartId,
|
|
130
|
+
sku: product.product.sku,
|
|
131
|
+
name: product.product.name,
|
|
132
|
+
priceTotal: product.prices.price.value,
|
|
133
|
+
currencyCode: product.prices.price.currency,
|
|
134
|
+
discountAmount:
|
|
135
|
+
product.prices.total_item_discount.value,
|
|
136
|
+
selectedOptions: selectedOptionsLabels,
|
|
137
|
+
quantity: product.quantity
|
|
138
|
+
}
|
|
139
|
+
});
|
|
109
140
|
} catch (e) {
|
|
110
141
|
// Error is logged by apollo link - no need to double log.
|
|
111
142
|
}
|
|
112
143
|
},
|
|
113
|
-
[cartId,
|
|
144
|
+
[removeItem, cartId, dispatch, productList]
|
|
114
145
|
);
|
|
115
146
|
|
|
116
147
|
const handleProceedToCheckout = useCallback(() => {
|
|
@@ -128,6 +159,18 @@ export const useMiniCart = props => {
|
|
|
128
159
|
[removeItemError]
|
|
129
160
|
);
|
|
130
161
|
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (isOpen) {
|
|
164
|
+
dispatch({
|
|
165
|
+
type: 'MINI_CART_VIEW',
|
|
166
|
+
payload: {
|
|
167
|
+
cartId: cartId,
|
|
168
|
+
products: productList
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}, [isOpen, cartId, productList, dispatch]);
|
|
173
|
+
|
|
131
174
|
return {
|
|
132
175
|
closeMiniCart,
|
|
133
176
|
errorMessage: derivedErrorMessage,
|
|
@@ -11,6 +11,7 @@ import { isSupportedProductType as isSupported } from '@magento/peregrine/lib/ut
|
|
|
11
11
|
import { deriveErrorMessage } from '../../util/deriveErrorMessage';
|
|
12
12
|
import mergeOperations from '../../util/shallowMerge';
|
|
13
13
|
import defaultOperations from './productFullDetail.gql';
|
|
14
|
+
import { useEventingContext } from '../../context/eventing';
|
|
14
15
|
|
|
15
16
|
const INITIAL_OPTION_CODES = new Map();
|
|
16
17
|
const INITIAL_OPTION_SELECTIONS = new Map();
|
|
@@ -155,7 +156,7 @@ const getConfigPrice = (product, optionCodes, optionSelections) => {
|
|
|
155
156
|
0;
|
|
156
157
|
|
|
157
158
|
if (!isConfigurable || !optionsSelected) {
|
|
158
|
-
value = product.price_range?.maximum_price
|
|
159
|
+
value = product.price_range?.maximum_price;
|
|
159
160
|
} else {
|
|
160
161
|
const item = findMatchingVariant({
|
|
161
162
|
optionCodes,
|
|
@@ -164,8 +165,8 @@ const getConfigPrice = (product, optionCodes, optionSelections) => {
|
|
|
164
165
|
});
|
|
165
166
|
|
|
166
167
|
value = item
|
|
167
|
-
? item.product.price_range?.maximum_price
|
|
168
|
-
: product.price_range?.maximum_price
|
|
168
|
+
? item.product.price_range?.maximum_price
|
|
169
|
+
: product.price_range?.maximum_price;
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
return value;
|
|
@@ -229,6 +230,8 @@ export const useProductFullDetail = props => {
|
|
|
229
230
|
product
|
|
230
231
|
} = props;
|
|
231
232
|
|
|
233
|
+
const [, { dispatch }] = useEventingContext();
|
|
234
|
+
|
|
232
235
|
const hasDeprecatedOperationProp = !!(
|
|
233
236
|
addConfigurableProductToCartMutation || addSimpleProductToCartMutation
|
|
234
237
|
);
|
|
@@ -314,6 +317,11 @@ export const useProductFullDetail = props => {
|
|
|
314
317
|
[product, optionCodes, optionSelections]
|
|
315
318
|
);
|
|
316
319
|
|
|
320
|
+
const productPrice = useMemo(
|
|
321
|
+
() => getConfigPrice(product, optionCodes, optionSelections),
|
|
322
|
+
[product, optionCodes, optionSelections]
|
|
323
|
+
);
|
|
324
|
+
|
|
317
325
|
// The map of ids to values (and their uids)
|
|
318
326
|
// For example:
|
|
319
327
|
// { "179" => [{ uid: "abc", value_index: 1 }, { uid: "def", value_index: 2 }]}
|
|
@@ -421,6 +429,29 @@ export const useProductFullDetail = props => {
|
|
|
421
429
|
|
|
422
430
|
try {
|
|
423
431
|
await addProductToCart({ variables });
|
|
432
|
+
|
|
433
|
+
const selectedOptionsLabels =
|
|
434
|
+
selectedOptionsArray?.map((uid, i) => ({
|
|
435
|
+
attribute: product.configurable_options[i].label,
|
|
436
|
+
value:
|
|
437
|
+
product.configurable_options[i].values.findLast(
|
|
438
|
+
x => x.uid === uid
|
|
439
|
+
)?.label || null
|
|
440
|
+
})) || null;
|
|
441
|
+
|
|
442
|
+
dispatch({
|
|
443
|
+
type: 'CART_ADD_ITEM',
|
|
444
|
+
payload: {
|
|
445
|
+
cartId,
|
|
446
|
+
sku: product.sku,
|
|
447
|
+
name: product.name,
|
|
448
|
+
priceTotal: productPrice.final_price.value,
|
|
449
|
+
currencyCode: productPrice.final_price.currency,
|
|
450
|
+
discountAmount: productPrice.discount.amount_off,
|
|
451
|
+
selectedOptions: selectedOptionsLabels,
|
|
452
|
+
quantity
|
|
453
|
+
}
|
|
454
|
+
});
|
|
424
455
|
} catch {
|
|
425
456
|
return;
|
|
426
457
|
}
|
|
@@ -431,11 +462,13 @@ export const useProductFullDetail = props => {
|
|
|
431
462
|
addProductToCart,
|
|
432
463
|
addSimpleProductToCart,
|
|
433
464
|
cartId,
|
|
465
|
+
dispatch,
|
|
434
466
|
hasDeprecatedOperationProp,
|
|
435
467
|
isSupportedProductType,
|
|
436
468
|
optionCodes,
|
|
437
469
|
optionSelections,
|
|
438
470
|
product,
|
|
471
|
+
productPrice,
|
|
439
472
|
productType,
|
|
440
473
|
selectedOptionsArray
|
|
441
474
|
]
|
|
@@ -452,17 +485,12 @@ export const useProductFullDetail = props => {
|
|
|
452
485
|
[optionSelections]
|
|
453
486
|
);
|
|
454
487
|
|
|
455
|
-
const productPrice = useMemo(
|
|
456
|
-
() => getConfigPrice(product, optionCodes, optionSelections),
|
|
457
|
-
[product, optionCodes, optionSelections]
|
|
458
|
-
);
|
|
459
|
-
|
|
460
488
|
// Normalization object for product details we need for rendering.
|
|
461
489
|
const productDetails = {
|
|
462
490
|
description: product.description,
|
|
463
491
|
shortDescription: product.short_description,
|
|
464
492
|
name: product.name,
|
|
465
|
-
price: productPrice,
|
|
493
|
+
price: productPrice?.final_price,
|
|
466
494
|
sku: product.sku
|
|
467
495
|
};
|
|
468
496
|
|
|
@@ -200,6 +200,8 @@ export const useCategory = props => {
|
|
|
200
200
|
}, [currentSort, previousSearch, search, setCurrentPage]);
|
|
201
201
|
|
|
202
202
|
const categoryData = categoryLoading && !data ? null : data;
|
|
203
|
+
const categoryNotFound =
|
|
204
|
+
!categoryLoading && data && data.categories.items.length === 0;
|
|
203
205
|
const metaDescription =
|
|
204
206
|
data &&
|
|
205
207
|
data.categories.items[0] &&
|
|
@@ -222,6 +224,7 @@ export const useCategory = props => {
|
|
|
222
224
|
metaDescription,
|
|
223
225
|
pageControl,
|
|
224
226
|
sortProps,
|
|
225
|
-
pageSize
|
|
227
|
+
pageSize,
|
|
228
|
+
categoryNotFound
|
|
226
229
|
};
|
|
227
230
|
};
|
|
@@ -2,6 +2,7 @@ import { useEffect } from 'react';
|
|
|
2
2
|
import { useLazyQuery, useQuery } from '@apollo/client';
|
|
3
3
|
|
|
4
4
|
import mergeOperations from '../../../util/shallowMerge';
|
|
5
|
+
import { useEventingContext } from '../../../context/eventing';
|
|
5
6
|
|
|
6
7
|
import DEFAULT_OPERATIONS from './categoryContent.gql';
|
|
7
8
|
|
|
@@ -46,14 +47,19 @@ export const useCategoryContent = props => {
|
|
|
46
47
|
}
|
|
47
48
|
);
|
|
48
49
|
|
|
49
|
-
const { data: categoryData } = useQuery(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
const { data: categoryData, loading: categoryLoading } = useQuery(
|
|
51
|
+
getCategoryContentQuery,
|
|
52
|
+
{
|
|
53
|
+
fetchPolicy: 'cache-and-network',
|
|
54
|
+
nextFetchPolicy: 'cache-first',
|
|
55
|
+
skip: !categoryId,
|
|
56
|
+
variables: {
|
|
57
|
+
id: categoryId
|
|
58
|
+
}
|
|
55
59
|
}
|
|
56
|
-
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const [, { dispatch }] = useEventingContext();
|
|
57
63
|
|
|
58
64
|
useEffect(() => {
|
|
59
65
|
if (categoryId) {
|
|
@@ -85,16 +91,32 @@ export const useCategoryContent = props => {
|
|
|
85
91
|
? data.products.page_info.total_pages
|
|
86
92
|
: null;
|
|
87
93
|
const totalCount = data ? data.products.total_count : null;
|
|
88
|
-
const categoryName =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
const categoryName =
|
|
95
|
+
categoryData && categoryData.categories.items.length
|
|
96
|
+
? categoryData.categories.items[0].name
|
|
97
|
+
: null;
|
|
98
|
+
const categoryDescription =
|
|
99
|
+
categoryData && categoryData.categories.items.length
|
|
100
|
+
? categoryData.categories.items[0].description
|
|
101
|
+
: null;
|
|
94
102
|
const availableSortMethods = sortData
|
|
95
103
|
? sortData.products.sort_fields.options
|
|
96
104
|
: null;
|
|
97
105
|
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (!categoryLoading && categoryData.categories.items.length > 0) {
|
|
108
|
+
dispatch({
|
|
109
|
+
type: 'CATEGORY_PAGE_VIEW',
|
|
110
|
+
payload: {
|
|
111
|
+
id: categoryData.categories.items[0].uid,
|
|
112
|
+
name: categoryData.categories.items[0].name,
|
|
113
|
+
url_key: categoryData.categories.items[0].url_key,
|
|
114
|
+
url_path: categoryData.categories.items[0].url_path
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}, [categoryData, dispatch, categoryLoading]);
|
|
119
|
+
|
|
98
120
|
return {
|
|
99
121
|
availableSortMethods,
|
|
100
122
|
categoryName,
|
|
@@ -42,6 +42,9 @@ export const ProductDetailsFragment = gql`
|
|
|
42
42
|
currency
|
|
43
43
|
value
|
|
44
44
|
}
|
|
45
|
+
discount {
|
|
46
|
+
amount_off
|
|
47
|
+
}
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
sku
|
|
@@ -136,6 +139,9 @@ export const ProductDetailsFragment = gql`
|
|
|
136
139
|
currency
|
|
137
140
|
value
|
|
138
141
|
}
|
|
142
|
+
discount {
|
|
143
|
+
amount_off
|
|
144
|
+
}
|
|
139
145
|
}
|
|
140
146
|
}
|
|
141
147
|
custom_attributes {
|
|
@@ -5,6 +5,7 @@ import { useAppContext } from '@magento/peregrine/lib/context/app';
|
|
|
5
5
|
|
|
6
6
|
import mergeOperations from '../../../util/shallowMerge';
|
|
7
7
|
import DEFAULT_OPERATIONS from './product.gql';
|
|
8
|
+
import { useEventingContext } from '../../../context/eventing';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* A [React Hook]{@link https://reactjs.org/docs/hooks-intro.html} that
|
|
@@ -76,11 +77,37 @@ export const useProduct = props => {
|
|
|
76
77
|
return mapProduct(product);
|
|
77
78
|
}, [data, mapProduct, urlKey]);
|
|
78
79
|
|
|
80
|
+
const [, { dispatch }] = useEventingContext();
|
|
81
|
+
|
|
79
82
|
// Update the page indicator if the GraphQL query is in flight.
|
|
80
83
|
useEffect(() => {
|
|
81
84
|
setPageLoading(isBackgroundLoading);
|
|
82
85
|
}, [isBackgroundLoading, setPageLoading]);
|
|
83
86
|
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (!error && !loading && product) {
|
|
89
|
+
dispatch({
|
|
90
|
+
type: 'PRODUCT_PAGE_VIEW',
|
|
91
|
+
payload: {
|
|
92
|
+
id: product.id,
|
|
93
|
+
name: product.name,
|
|
94
|
+
sku: product.sku,
|
|
95
|
+
currency_code:
|
|
96
|
+
product?.price_range?.maximum_price?.final_price
|
|
97
|
+
?.currency,
|
|
98
|
+
price_range: {
|
|
99
|
+
maximum_price: {
|
|
100
|
+
final_price:
|
|
101
|
+
product?.price_range?.maximum_price?.final_price
|
|
102
|
+
?.value
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
url_key: product.url_key
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}, [error, loading, product, dispatch]);
|
|
110
|
+
|
|
84
111
|
return {
|
|
85
112
|
error,
|
|
86
113
|
loading,
|
|
@@ -3,3 +3,4 @@ export { useSearchBar } from './useSearchBar';
|
|
|
3
3
|
export { useSearchField } from './useSearchField';
|
|
4
4
|
export { useSuggestedCategory } from './useSuggestedCategory';
|
|
5
5
|
export { useSuggestions } from './useSuggestions';
|
|
6
|
+
export { useSuggestedProduct } from './useSuggestedProduct';
|