@riosst100/pwa-marketplace 2.4.7 → 2.4.9
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/package.json +1 -1
- package/src/componentOverrideMapping.js +3 -0
- package/src/components/Seller/seller.js +8 -0
- package/src/components/SellerDetail/sellerDetail.js +4 -3
- package/src/components/SellerMegaMenu/index.js +1 -0
- package/src/components/SellerMegaMenu/sellerMegaMenu.js +92 -0
- package/src/components/SellerMegaMenu/sellerMegaMenu.module.css +12 -0
- package/src/components/SellerMegaMenu/sellerMegaMenuItem.js +164 -0
- package/src/components/SellerMegaMenu/sellerMegaMenuItem.module.css +31 -0
- package/src/components/SellerMegaMenu/sellerSubmenu.js +106 -0
- package/src/components/SellerMegaMenu/sellerSubmenu.module.css +57 -0
- package/src/components/SellerMegaMenu/sellerSubmenuColumn.js +173 -0
- package/src/components/SellerMegaMenu/sellerSubmenuColumn.module.css +33 -0
- package/src/components/SellerProducts/productContent.js +70 -11
- package/src/components/SellerProducts/sellerProducts.js +51 -12
- package/src/intercept.js +14 -0
- package/src/overwrites/peregrine/lib/context/cart.js +136 -0
- package/src/overwrites/peregrine/lib/store/actions/cart/asyncActions.js +589 -0
- package/src/overwrites/peregrine/lib/talons/CartPage/PriceSummary/usePriceSummary.js +4 -1
- package/src/overwrites/peregrine/lib/talons/CheckoutPage/useCheckoutPage.js +434 -0
- package/src/overwrites/venia-ui/lib/components/Adapter/adapter.js +0 -9
- package/src/overwrites/venia-ui/lib/components/CartPage/ProductListing/product.js +2 -2
- package/src/overwrites/venia-ui/lib/components/CartPage/ProductListing/productListing.js +8 -8
- package/src/overwrites/venia-ui/lib/components/Header/header.js +1 -1
- package/src/talons/Seller/useSeller.js +1 -1
- package/src/talons/SellerMegaMenu/megaMenu.gql.js +96 -0
- package/src/talons/SellerMegaMenu/useSellerMegaMenu.js +199 -0
- package/src/talons/SellerMegaMenu/useSellerMegaMenuItem.js +66 -0
- package/src/talons/SellerMegaMenu/useSellerSubMenu.js +21 -0
- package/src/talons/SellerProducts/sellerProducts.gql.js +24 -1
- package/src/talons/SellerProducts/useProductContent.js +17 -23
- package/src/talons/SellerProducts/useSellerProducts.js +44 -28
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Link } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
|
|
5
|
+
|
|
6
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
7
|
+
import defaultClasses from './sellerSubmenuColumn.module.css';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
import cn from 'classnames';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The SellerSubmenuColumn component displays columns with categories in submenu
|
|
13
|
+
*
|
|
14
|
+
* @param {MegaMenuCategory} props.category
|
|
15
|
+
* @param {function} props.onNavigate - function called when clicking on Link
|
|
16
|
+
*/
|
|
17
|
+
const SellerSubmenuColumn = props => {
|
|
18
|
+
const {
|
|
19
|
+
category,
|
|
20
|
+
categoryUrlSuffix,
|
|
21
|
+
onNavigate,
|
|
22
|
+
seller,
|
|
23
|
+
handleCloseSellerSubMenu,
|
|
24
|
+
arrow
|
|
25
|
+
} = props;
|
|
26
|
+
|
|
27
|
+
const classes = useStyle(defaultClasses, props.classes);
|
|
28
|
+
|
|
29
|
+
const categoryUrl = resourceUrl(
|
|
30
|
+
'/seller/' + seller?.url_key + `/${category.url_path}${categoryUrlSuffix || ''}`
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
let children = null;
|
|
34
|
+
let childrenItems = '';
|
|
35
|
+
|
|
36
|
+
if (category.children.length) {
|
|
37
|
+
childrenItems = category.children.map((subCategory, index) => {
|
|
38
|
+
const { url_path, isActive, name } = subCategory;
|
|
39
|
+
const categoryUrl = resourceUrl(
|
|
40
|
+
`/${url_path}${categoryUrlSuffix || ''}`
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// setting keyboardProps if it is last child of that category
|
|
44
|
+
const keyboardProps =
|
|
45
|
+
index === category.children.length - 1
|
|
46
|
+
? props.keyboardProps
|
|
47
|
+
: {};
|
|
48
|
+
|
|
49
|
+
const customMenuItems = subCategory.custom_submenu;
|
|
50
|
+
|
|
51
|
+
let result = [];
|
|
52
|
+
if (customMenuItems && customMenuItems.length) {
|
|
53
|
+
customMenuItems.map((submenu, index) => {
|
|
54
|
+
const {name} = submenu;
|
|
55
|
+
result.push(
|
|
56
|
+
<li key={index} className={cn(classes.submenuChildItem, 'mt-0')}>
|
|
57
|
+
<Link
|
|
58
|
+
{...keyboardProps}
|
|
59
|
+
className={isActive ? classes.linkActive : classes.link}
|
|
60
|
+
data-cy="SellerMegaMenu-SellerSubmenuColumn-link"
|
|
61
|
+
to={categoryUrl}
|
|
62
|
+
onClick={() => {
|
|
63
|
+
handleCloseSellerSubMenu();
|
|
64
|
+
onNavigate();
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
{name}
|
|
68
|
+
</Link>
|
|
69
|
+
</li>
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
} else {
|
|
73
|
+
result.push(
|
|
74
|
+
<li key={index} className={cn(classes.submenuChildItem, 'mt-0')}>
|
|
75
|
+
<Link
|
|
76
|
+
{...keyboardProps}
|
|
77
|
+
className={isActive ? classes.linkActive : classes.link}
|
|
78
|
+
data-cy="SellerMegaMenu-SellerSubmenuColumn-link"
|
|
79
|
+
to={categoryUrl}
|
|
80
|
+
onClick={() => {
|
|
81
|
+
handleCloseSellerSubMenu();
|
|
82
|
+
onNavigate();
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
{name}
|
|
86
|
+
</Link>
|
|
87
|
+
</li>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return result;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (category.custom_submenu && category.custom_submenu.length) {
|
|
96
|
+
childrenItems = category.custom_submenu.map((subCategory, index) => {
|
|
97
|
+
const { path, isActive, name } = subCategory;
|
|
98
|
+
const categoryUrl = resourceUrl(
|
|
99
|
+
'/seller/' + seller?.url_key + `/${path}`
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// setting keyboardProps if it is last child of that category
|
|
103
|
+
const keyboardProps =
|
|
104
|
+
index === category.children.length - 1
|
|
105
|
+
? props.keyboardProps
|
|
106
|
+
: {};
|
|
107
|
+
|
|
108
|
+
let result = [];
|
|
109
|
+
|
|
110
|
+
result.push(
|
|
111
|
+
<li key={index} className={cn(classes.submenuChildItem, 'mt-0')}>
|
|
112
|
+
<Link
|
|
113
|
+
{...keyboardProps}
|
|
114
|
+
className={isActive ? classes.linkActive : classes.link}
|
|
115
|
+
data-cy="SellerMegaMenu-SellerSubmenuColumn-link"
|
|
116
|
+
to={categoryUrl}
|
|
117
|
+
onClick={() => {
|
|
118
|
+
handleCloseSellerSubMenu();
|
|
119
|
+
onNavigate();
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
{name}
|
|
123
|
+
</Link>
|
|
124
|
+
</li>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return result;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (childrenItems) {
|
|
132
|
+
children = <ul className={classes.submenuChild}>{childrenItems}</ul>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// setting keyboardProps if category does not have any sub-category
|
|
136
|
+
const keyboardProps = category.children.length ? {} : props.keyboardProps;
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div className={classes.sellerSubmenuColumn}>
|
|
140
|
+
<Link
|
|
141
|
+
{...keyboardProps}
|
|
142
|
+
className={classes.link}
|
|
143
|
+
data-cy="SellerMegaMenu-SellerSubmenuColumn-link"
|
|
144
|
+
to={categoryUrl}
|
|
145
|
+
onClick={() => {
|
|
146
|
+
handleCloseSellerSubMenu();
|
|
147
|
+
onNavigate();
|
|
148
|
+
}}
|
|
149
|
+
>
|
|
150
|
+
<span className={classes.heading}>{category.name}</span>
|
|
151
|
+
</Link>
|
|
152
|
+
{children}
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export default SellerSubmenuColumn;
|
|
158
|
+
|
|
159
|
+
SellerSubmenuColumn.propTypes = {
|
|
160
|
+
category: PropTypes.shape({
|
|
161
|
+
children: PropTypes.array,
|
|
162
|
+
uid: PropTypes.string.isRequired,
|
|
163
|
+
include_in_menu: PropTypes.number,
|
|
164
|
+
isActive: PropTypes.bool.isRequired,
|
|
165
|
+
name: PropTypes.string.isRequired,
|
|
166
|
+
path: PropTypes.array.isRequired,
|
|
167
|
+
position: PropTypes.number.isRequired,
|
|
168
|
+
url_path: PropTypes.string.isRequired
|
|
169
|
+
}).isRequired,
|
|
170
|
+
categoryUrlSuffix: PropTypes.string,
|
|
171
|
+
onNavigate: PropTypes.func.isRequired,
|
|
172
|
+
handleCloseSellerSubMenu: PropTypes.func.isRequired
|
|
173
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.sellerSubmenuColumn {
|
|
2
|
+
/* composes: max-w-[235px] from global; */
|
|
3
|
+
composes: pl-4 pt-2 from global;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.heading {
|
|
7
|
+
composes: font-medium from global;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.link {
|
|
11
|
+
composes: whitespace-nowrap from global;
|
|
12
|
+
|
|
13
|
+
composes: focus_underline from global;
|
|
14
|
+
|
|
15
|
+
composes: hover_underline from global;
|
|
16
|
+
|
|
17
|
+
align-items: center;
|
|
18
|
+
composes: inline-flex from global;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.linkActive {
|
|
22
|
+
composes: underline from global;
|
|
23
|
+
align-items: center;
|
|
24
|
+
composes: inline-flex from global;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.submenuChild {
|
|
28
|
+
composes: mt-0 from global;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.submenuChildItem {
|
|
32
|
+
composes: mb-0 from global;
|
|
33
|
+
}
|
|
@@ -23,6 +23,12 @@ import { FormattedMessage } from 'react-intl';
|
|
|
23
23
|
import NoProductsFound from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/RootComponents/Category/NoProductsFound';
|
|
24
24
|
import Gallery, { GalleryShimmer } from '@magento/venia-ui/lib/components/Gallery';
|
|
25
25
|
import Pagination from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/Pagination';
|
|
26
|
+
import SellerMegaMenu from '@riosst100/pwa-marketplace/src/components/SellerMegaMenu';
|
|
27
|
+
import AlphaFilter from '@riosst100/pwa-marketplace/src/components/AlphaFilter';
|
|
28
|
+
import CustomSubCategory from '@riosst100/pwa-marketplace/src/components/SubCategory/customSubCategory';
|
|
29
|
+
import SubCategory from '@riosst100/pwa-marketplace/src/components/SubCategory/subCategory';
|
|
30
|
+
import { useLocation, useHistory } from 'react-router-dom';
|
|
31
|
+
import { getFiltersFromSearch } from '@magento/peregrine/lib/talons/FilterModal/helpers';
|
|
26
32
|
|
|
27
33
|
const FilterSidebar = React.lazy(() =>
|
|
28
34
|
import('@magento/venia-ui/lib/components/FilterSidebar')
|
|
@@ -41,6 +47,7 @@ const ProductContent = props => {
|
|
|
41
47
|
shopby,
|
|
42
48
|
pageControl,
|
|
43
49
|
sortProps,
|
|
50
|
+
seller,
|
|
44
51
|
isLoading,
|
|
45
52
|
pageSize
|
|
46
53
|
} = props;
|
|
@@ -70,6 +77,7 @@ const ProductContent = props => {
|
|
|
70
77
|
items,
|
|
71
78
|
category,
|
|
72
79
|
children,
|
|
80
|
+
categoryName,
|
|
73
81
|
totalPagesFromData
|
|
74
82
|
} = talonProps;
|
|
75
83
|
|
|
@@ -114,13 +122,14 @@ const ProductContent = props => {
|
|
|
114
122
|
<SortedByContainerShimmer />
|
|
115
123
|
) : null;
|
|
116
124
|
|
|
117
|
-
console.log('filters')
|
|
118
|
-
console.log(filters)
|
|
119
|
-
|
|
120
125
|
const filtersModal = shouldShowFilterButtons ? (
|
|
121
126
|
<FilterModal filters={filters} />
|
|
122
127
|
) : null;
|
|
123
128
|
|
|
129
|
+
const handleActiveLetter = (val) => {
|
|
130
|
+
setActiveLetter(val);
|
|
131
|
+
}
|
|
132
|
+
|
|
124
133
|
const galleryItems = [];
|
|
125
134
|
|
|
126
135
|
useMemo(() => {
|
|
@@ -199,20 +208,70 @@ const ProductContent = props => {
|
|
|
199
208
|
totalPagesFromData,
|
|
200
209
|
activeLetter
|
|
201
210
|
]);
|
|
211
|
+
|
|
212
|
+
const { search } = useLocation();
|
|
213
|
+
const allActiveFilters = getFiltersFromSearch(search);
|
|
214
|
+
|
|
215
|
+
const activeFilters = [];
|
|
216
|
+
|
|
217
|
+
let isSingles = false;
|
|
218
|
+
|
|
219
|
+
const urlKey = category?.url_key;
|
|
220
|
+
|
|
221
|
+
if (allActiveFilters && allActiveFilters.size > 0) {
|
|
222
|
+
allActiveFilters.forEach((value, key) => {
|
|
223
|
+
value.forEach((value) => {
|
|
224
|
+
const filterArr = value.split(',');
|
|
225
|
+
|
|
226
|
+
const label = filterArr[0];
|
|
227
|
+
const optionId = filterArr[1];
|
|
228
|
+
if (label == "Singles") {
|
|
229
|
+
isSingles = true;
|
|
230
|
+
}
|
|
231
|
+
if (key == "bricks_categories" || key == "sc_sports_categories" || key == "card_game" || key == "product_line" && urlKey == "brands" || key == "product_line" && urlKey == "franchise" || key == "franchise" && urlKey == "brands" || key == "franchise" && urlKey == "franchise" || key == "brands" && urlKey == "franchise" || key == "brands" && urlKey == "brands" || virtualCategoryFilters && virtualCategoryFilters.includes(key)) {
|
|
232
|
+
activeFilters.push(
|
|
233
|
+
{
|
|
234
|
+
'label': label,
|
|
235
|
+
'code': key,
|
|
236
|
+
'option_id': optionId
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const currentFilter = activeFilters && activeFilters.length ? activeFilters[activeFilters.length - 1].label : '';
|
|
245
|
+
|
|
246
|
+
let title = categoryName;
|
|
202
247
|
|
|
203
248
|
return (
|
|
204
249
|
<>
|
|
205
250
|
<div className='w-full mb-[30px]'>
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
</Link>
|
|
213
|
-
</div>
|
|
251
|
+
<SellerMegaMenu
|
|
252
|
+
rootClassName={cn('nav-menu w-full max-w-[1210px] px-[15px] mx-[auto] flex-row gap-x-[40px]')}
|
|
253
|
+
sellerMegaMenuItemClassname={cn('px-0 py-[10px] leading-[20px]')}
|
|
254
|
+
seller={seller}
|
|
255
|
+
titleClassName={cn('font-normal leading-[20px]')}
|
|
256
|
+
/>
|
|
214
257
|
</div>
|
|
215
258
|
<div className='w-full mb-[0px]'>
|
|
259
|
+
<div className={classes.categoryHeader}>
|
|
260
|
+
<h1 aria-live="polite" className={classes.title}>
|
|
261
|
+
<span
|
|
262
|
+
className={classes.categoryTitle}
|
|
263
|
+
data-cy="CategoryContent-categoryTitle"
|
|
264
|
+
>
|
|
265
|
+
{title}
|
|
266
|
+
</span>
|
|
267
|
+
</h1>
|
|
268
|
+
</div>
|
|
269
|
+
<>
|
|
270
|
+
{currentFilter && <AlphaFilter isSingles={isSingles} items={items} handleActiveLetter={handleActiveLetter} activeLetter={activeLetter} />}
|
|
271
|
+
{!currentFilter && <CustomSubCategory categoryName={category ? category.name : null} customSubCategory={category ? category.custom_subcategory : null} />}
|
|
272
|
+
{shopby != "gauge" && category?.name != "Collectible Card Games" ? <SubCategory parent={parent} children={children} /> : ''}
|
|
273
|
+
|
|
274
|
+
</>
|
|
216
275
|
<FilterTop shopby={shopby} filters={filters} category={category} children={children} allowedFilters={category ? category.allowed_filters : []} />
|
|
217
276
|
</div>
|
|
218
277
|
<div className='w-full flex items-start gap-x-[30px]'>
|
|
@@ -26,7 +26,7 @@ const MESSAGES = new Map().set(
|
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
const SellerProducts = props => {
|
|
29
|
-
const { sellerId } = props;
|
|
29
|
+
const { sellerId, seller } = props;
|
|
30
30
|
|
|
31
31
|
const uid = null;
|
|
32
32
|
|
|
@@ -36,6 +36,7 @@ const SellerProducts = props => {
|
|
|
36
36
|
|
|
37
37
|
const query = new URLSearchParams(location.search);
|
|
38
38
|
const shopby = query.get('shopby') || null;
|
|
39
|
+
const showSubcategory = query.get('show_subcategory') || null;
|
|
39
40
|
|
|
40
41
|
const talonProps = useSellerProducts({
|
|
41
42
|
sellerId: sellerId,
|
|
@@ -50,6 +51,7 @@ const SellerProducts = props => {
|
|
|
50
51
|
productData,
|
|
51
52
|
pageControl,
|
|
52
53
|
sortProps,
|
|
54
|
+
categoryId,
|
|
53
55
|
pageSize,
|
|
54
56
|
categoryNotFound
|
|
55
57
|
} = talonProps;
|
|
@@ -77,19 +79,56 @@ const SellerProducts = props => {
|
|
|
77
79
|
);
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
const allowedShopby = ['card_artist'];
|
|
83
|
+
|
|
80
84
|
return (
|
|
81
85
|
<Fragment>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
{showSubcategory ? (
|
|
87
|
+
<SubCategoryPage
|
|
88
|
+
categoryId={uid}
|
|
89
|
+
isLoading={loading} />
|
|
90
|
+
) : (
|
|
91
|
+
allowedShopby.includes(shopby) ? <ShopBy
|
|
92
|
+
categoryId={uid}
|
|
93
|
+
shopby={shopby}
|
|
94
|
+
isLoading={loading} /> : shopby == "release_year" || shopby == "secret_lair" || shopby == "expansion_sets" || shopby == "sets" || shopby == "release" ? <SetsData
|
|
95
|
+
categoryId={uid}
|
|
96
|
+
shopby={shopby}
|
|
97
|
+
isLoading={loading} /> : shopby == "singles" || shopby == "lego_set_name" && parentCategoryUrlKey == "lego" ? <LegoSets
|
|
98
|
+
categoryId={uid}
|
|
99
|
+
shopby={shopby}
|
|
100
|
+
isLoading={loading} /> : shopby && parentCategoryUrlKey == "non-sports-cards" ? <NonSportCardsSets
|
|
101
|
+
categoryId={uid}
|
|
102
|
+
shopby={shopby}
|
|
103
|
+
isLoading={loading} /> : shopby == "sc_baseball_players" || shopby == "sc_baseball_teams" || shopby == "release" && parentCategoryUrlKey == "sport-cards" ? <SportCardsSets
|
|
104
|
+
categoryId={uid}
|
|
105
|
+
shopby={shopby}
|
|
106
|
+
isLoading={loading} /> : shopby == "release_year" ? <ShopBySets
|
|
107
|
+
categoryId={uid}
|
|
108
|
+
shopby={shopby}
|
|
109
|
+
isLoading={loading} /> : shopby == "card" ? <ShopByCard
|
|
110
|
+
categoryId={uid}
|
|
111
|
+
shopby={shopby}
|
|
112
|
+
isLoading={loading} /> : (shopby && shopby != "gauge" ? (
|
|
113
|
+
<ShopBy
|
|
114
|
+
categoryId={uid}
|
|
115
|
+
shopby={shopby}
|
|
116
|
+
isLoading={loading} />
|
|
117
|
+
) : (
|
|
118
|
+
<ProductContent
|
|
119
|
+
categoryId={categoryId}
|
|
120
|
+
sellerId={sellerId}
|
|
121
|
+
seller={seller}
|
|
122
|
+
classes={classes}
|
|
123
|
+
data={productData?.productsBySellerId}
|
|
124
|
+
shopby={shopby}
|
|
125
|
+
isLoading={loading}
|
|
126
|
+
pageControl={pageControl}
|
|
127
|
+
sortProps={sortProps}
|
|
128
|
+
pageSize={pageSize}
|
|
129
|
+
/>
|
|
130
|
+
))
|
|
131
|
+
)}
|
|
93
132
|
</Fragment>
|
|
94
133
|
);
|
|
95
134
|
};
|
package/src/intercept.js
CHANGED
|
@@ -101,6 +101,20 @@ module.exports = targets => {
|
|
|
101
101
|
path: require.resolve("./components/SellerPage/index.js"),
|
|
102
102
|
authed: false,
|
|
103
103
|
},
|
|
104
|
+
{
|
|
105
|
+
exact: true,
|
|
106
|
+
name: "SellerProductCategoriesPage",
|
|
107
|
+
pattern: "/seller/:urlKey/:categoryUrlKey",
|
|
108
|
+
path: require.resolve("./components/SellerPage/index.js"),
|
|
109
|
+
authed: false,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
exact: true,
|
|
113
|
+
name: "SellerProductCategoriesPage",
|
|
114
|
+
pattern: "/seller/:urlKey/:categoryUrlKey/:childCategoryUrlKey",
|
|
115
|
+
path: require.resolve("./components/SellerPage/index.js"),
|
|
116
|
+
authed: false,
|
|
117
|
+
},
|
|
104
118
|
{
|
|
105
119
|
exact: true,
|
|
106
120
|
name: "CollectibleGameSetsPage",
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
useCallback
|
|
7
|
+
} from 'react';
|
|
8
|
+
import { connect } from 'react-redux';
|
|
9
|
+
import { useMutation } from '@apollo/client';
|
|
10
|
+
import gql from 'graphql-tag';
|
|
11
|
+
|
|
12
|
+
import { useAwaitQuery } from '@magento/peregrine/lib/hooks/useAwaitQuery';
|
|
13
|
+
import actions from '@magento/peregrine/lib/store/actions/cart/actions';
|
|
14
|
+
import * as asyncActions from '@magento/peregrine/lib/store/actions/cart/asyncActions';
|
|
15
|
+
import bindActionCreators from '@magento/peregrine/lib/util/bindActionCreators';
|
|
16
|
+
import { useEventListener } from '@magento/peregrine/lib/hooks/useEventListener';
|
|
17
|
+
import BrowserPersistence from '@magento/peregrine/lib/util/simplePersistence';
|
|
18
|
+
|
|
19
|
+
const CartContext = createContext();
|
|
20
|
+
|
|
21
|
+
const isCartEmpty = cart =>
|
|
22
|
+
!cart || !cart.details.items || cart.details.items.length === 0;
|
|
23
|
+
|
|
24
|
+
const getTotalQuantity = items =>
|
|
25
|
+
items.reduce((total, item) => total + item.quantity, 0);
|
|
26
|
+
|
|
27
|
+
const CartContextProvider = props => {
|
|
28
|
+
const { actions, asyncActions, cartState, children } = props;
|
|
29
|
+
|
|
30
|
+
// Make deeply nested details easier to retrieve and provide empty defaults
|
|
31
|
+
const derivedDetails = useMemo(() => {
|
|
32
|
+
if (isCartEmpty(cartState)) {
|
|
33
|
+
return {
|
|
34
|
+
currencyCode: 'USD',
|
|
35
|
+
numItems: 0,
|
|
36
|
+
subtotal: 0
|
|
37
|
+
};
|
|
38
|
+
} else {
|
|
39
|
+
return {
|
|
40
|
+
currencyCode: cartState.details.prices.grand_total.currency,
|
|
41
|
+
numItems: getTotalQuantity(cartState.details.items),
|
|
42
|
+
subtotal: cartState.details.prices.grand_total.value
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}, [cartState]);
|
|
46
|
+
|
|
47
|
+
const cartApi = useMemo(
|
|
48
|
+
() => ({
|
|
49
|
+
actions,
|
|
50
|
+
...asyncActions
|
|
51
|
+
}),
|
|
52
|
+
[actions, asyncActions]
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const contextValue = useMemo(() => {
|
|
56
|
+
const derivedCartState = {
|
|
57
|
+
...cartState,
|
|
58
|
+
isEmpty: isCartEmpty(cartState),
|
|
59
|
+
derivedDetails
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return [derivedCartState, cartApi];
|
|
63
|
+
}, [cartApi, cartState, derivedDetails]);
|
|
64
|
+
|
|
65
|
+
const [fetchCartId] = useMutation(CREATE_CART_MUTATION);
|
|
66
|
+
const [removeSplitCart] = useMutation(REMOVE_SPLIT_CART_MUTATION);
|
|
67
|
+
const fetchCartDetails = useAwaitQuery(CART_DETAILS_QUERY);
|
|
68
|
+
|
|
69
|
+
// Storage listener to force a state update if cartId changes from another browser tab.
|
|
70
|
+
const storageListener = useCallback(() => {
|
|
71
|
+
const storage = new BrowserPersistence();
|
|
72
|
+
const currentCartId = storage.getItem('cartId');
|
|
73
|
+
const { cartId } = cartState;
|
|
74
|
+
if (cartId && currentCartId && cartId !== currentCartId) {
|
|
75
|
+
globalThis.location && globalThis.location.reload();
|
|
76
|
+
}
|
|
77
|
+
}, [cartState]);
|
|
78
|
+
|
|
79
|
+
useEventListener(globalThis, 'storage', storageListener);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
// cartApi.getCartDetails initializes the cart if there isn't one.
|
|
83
|
+
cartApi.getCartDetails({
|
|
84
|
+
fetchCartId,
|
|
85
|
+
fetchCartDetails,
|
|
86
|
+
removeSplitCart
|
|
87
|
+
});
|
|
88
|
+
}, [cartApi, fetchCartDetails, fetchCartId]);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<CartContext.Provider value={contextValue}>
|
|
92
|
+
{children}
|
|
93
|
+
</CartContext.Provider>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const mapStateToProps = ({ cart }) => ({ cartState: cart });
|
|
98
|
+
|
|
99
|
+
const mapDispatchToProps = dispatch => ({
|
|
100
|
+
actions: bindActionCreators(actions, dispatch),
|
|
101
|
+
asyncActions: bindActionCreators(asyncActions, dispatch)
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export default connect(
|
|
105
|
+
mapStateToProps,
|
|
106
|
+
mapDispatchToProps
|
|
107
|
+
)(CartContextProvider);
|
|
108
|
+
|
|
109
|
+
export const useCartContext = () => useContext(CartContext);
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* We normally do not keep GQL queries in Peregrine. All components should pass
|
|
113
|
+
* queries to talons/hooks. This is an exception to the rule because it would
|
|
114
|
+
* be unecessarily complex to pass these queries to the context provider.
|
|
115
|
+
*/
|
|
116
|
+
const CREATE_CART_MUTATION = gql`
|
|
117
|
+
mutation createCart {
|
|
118
|
+
cartId: createEmptyCart
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
|
|
122
|
+
const REMOVE_SPLIT_CART_MUTATION = gql`
|
|
123
|
+
mutation removeSplitCart($input: removeSplitCartInput!) {
|
|
124
|
+
removeSplitCart(input: $input)
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
|
|
128
|
+
const CART_DETAILS_QUERY = gql`
|
|
129
|
+
query checkUserIsAuthed($cartId: String!) {
|
|
130
|
+
cart(cart_id: $cartId) {
|
|
131
|
+
# The purpose of this query is to check that the user is authorized
|
|
132
|
+
# to query on the current cart. Just fetch "id" to keep it small.
|
|
133
|
+
id
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
`;
|