@riosst100/pwa-marketplace 1.5.5 → 1.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/package.json +1 -1
  2. package/src/componentOverrideMapping.js +1 -0
  3. package/src/components/CollectibleGameSets/collectibleGameSets.js +38 -21
  4. package/src/components/CustomSortBy/customSortBy.js +198 -0
  5. package/src/components/CustomSortBy/customSortBy.module.css +68 -0
  6. package/src/components/CustomSortBy/customSortItem.js +57 -0
  7. package/src/components/CustomSortBy/customSortItem.module.css +23 -0
  8. package/src/components/CustomSortBy/index.js +1 -0
  9. package/src/components/FilterTop/CustomFilters/customFilters.module.css +0 -1
  10. package/src/components/ProductListTab/productListTab.js +1 -1
  11. package/src/components/SubCategory/customSubCategory.js +35 -0
  12. package/src/components/SubCategory/customSubCategory.module.css +22 -0
  13. package/src/components/SubCategory/subCategory.js +24 -6
  14. package/src/overwrites/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js +252 -0
  15. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js +7 -1
  16. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js +34 -5
  17. package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +33 -9
  18. package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.js +30 -5
  19. package/src/overwrites/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilters.js +12 -10
  20. package/src/overwrites/venia-ui/lib/components/FilterModal/FilterList/filterList.js +1 -1
  21. package/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js +22 -14
  22. package/src/talons/CollectibleGameSets/collectibleGameSets.gql.js +1 -0
  23. package/src/talons/CollectibleGameSets/useCollectibleGameSets.js +2 -6
  24. package/src/talons/SubCategory/useCustomSubCategory.js +49 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@riosst100/pwa-marketplace",
3
3
  "author": "riosst100@gmail.com",
4
- "version": "1.5.5",
4
+ "version": "1.5.7",
5
5
  "main": "src/index.js",
6
6
  "pwa-studio": {
7
7
  "targets": {
@@ -24,6 +24,7 @@ module.exports = componentOverrideMapping = {
24
24
  [`@magento/peregrine/lib/talons/RootComponents/Category/useCategory.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategory.js',
25
25
  [`@magento/peregrine/lib/talons/Breadcrumbs/useBreadcrumbs.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/Breadcrumbs/useBreadcrumbs.js',
26
26
  [`@magento/peregrine/lib/talons/FilterModal/useFilterList.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/FilterModal/useFilterList.js',
27
+ [`@magento/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js',
27
28
  [`@magento/venia-ui/lib/components/FilterSidebar/filterSidebar.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js',
28
29
  [`@magento/venia-ui/lib/components/TextInput/index.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/TextInput/index.js',
29
30
  [`@magento/venia-ui/lib/components/Pagination/index.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/Pagination/index.js',
@@ -11,16 +11,20 @@ import { useStyle } from '@magento/venia-ui/lib/classify';
11
11
  import cn from 'classnames';
12
12
  import Divider from '@riosst100/pwa-marketplace/src/components/Divider';
13
13
  import { CollectibleGameSetsShimmer } from '@riosst100/pwa-marketplace/src/components/CollectibleGameSets';
14
+ import ProductSort from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/ProductSort';
15
+ import CustomSortBy from '@riosst100/pwa-marketplace/src/components/CustomSortBy';
14
16
 
15
17
  const CollectibleGameSets = props => {
16
-
17
- const { product_type } = props;
18
-
19
18
  const [active, setActive] = useState('all');
20
19
 
20
+ const [sortBy, setSortBy] = useState({
21
+ sortText: 'All (A-Z)',
22
+ value: 'all'
23
+ });
24
+
21
25
  const classes = useStyle(defaultClasses);
22
26
 
23
- const talonProps = useCollectibleGameSets({ product_type });
27
+ const talonProps = useCollectibleGameSets();
24
28
 
25
29
  const { error, loading, collectibleGameSets, categoryUrlSuffix, categoryUrlKey, productType } = talonProps;
26
30
 
@@ -28,20 +32,12 @@ const CollectibleGameSets = props => {
28
32
  return <CollectibleGameSetsShimmer />;
29
33
  if (error && !collectibleGameSets) return <ErrorView />;
30
34
 
31
- if (!collectibleGameSets) {
32
- return (
33
- <h1>
34
- <FormattedMessage
35
- id={'seller.notFound'}
36
- defaultMessage={
37
- 'Seller Not Found.'
38
- }
39
- />
40
- </h1>
41
- );
35
+ if (!collectibleGameSets && !loading && !error) {
36
+ return <CollectibleGameSetsShimmer />;
42
37
  }
43
38
 
44
39
  const setsLengthArr = [];
40
+ const groupByYear = [];
45
41
 
46
42
  const setRelases = collectibleGameSets.map((setRelease, index) => {
47
43
  const { release_type, sets } = setRelease;
@@ -52,7 +48,9 @@ const CollectibleGameSets = props => {
52
48
 
53
49
  if (sets.length) {
54
50
  sets.map((set, index) => {
55
- const { set_name, option_id, set_abbreviation } = set;
51
+ const { set_name, option_id, set_abbreviation, release_year } = set;
52
+
53
+ groupByYear[release_year] = set;
56
54
 
57
55
  const categoryUrl = resourceUrl(
58
56
  `/games/collectible-game/${categoryUrlKey}${categoryUrlSuffix || ''}?card_set[filter]=${set_name},${option_id}`
@@ -79,20 +77,39 @@ const CollectibleGameSets = props => {
79
77
  );
80
78
  });
81
79
 
80
+ console.log(groupByYear)
81
+
82
82
  const handleActive = (val) => {
83
83
  setActive(val);
84
84
  }
85
85
 
86
86
  const alpha = ['#', 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
87
87
 
88
+ const availableSortBy = [
89
+ {
90
+ 'label': 'All (A-Z)',
91
+ 'value': 'all'
92
+ },
93
+ {
94
+ 'label': 'Newest',
95
+ 'value': 'newest'
96
+ },
97
+ {
98
+ 'label': 'All (Date)',
99
+ 'value': 'all-date'
100
+ }
101
+ ];
102
+
88
103
  return (
89
- <>
104
+ <Fragment>
105
+ <StoreTitle>{"Magic: The Gathering" + " | " + (productType == "expansion-sets" ? "Expansion Sets" : "All Sets")}</StoreTitle>
90
106
  <h1 className='mx-auto relative block text-xl font-bold text-center pt-10 pb-4'>
91
- {productType == "sealed-products" ? "Expansion Sets" : "All Sets"}
107
+ {productType == "expansion-sets" ? "Expansion Sets" : "All Sets"}
92
108
  </h1>
93
109
  <div className='border border-gray-100 px-6'>
94
- {productType != "sealed-products" ? (
110
+ {productType != "expansion-sets" ? (
95
111
  <>
112
+ <CustomSortBy sortProps={[sortBy, setSortBy]} availableSortMethods={availableSortBy} />
96
113
  <section className='single_list-indexing-container relative m-auto py-10'>
97
114
  <ul className='flex gap-2 justify-center flex-wrap'>
98
115
  <li>
@@ -136,8 +153,8 @@ const CollectibleGameSets = props => {
136
153
  </div>
137
154
  </section>
138
155
  </div>
139
- </>
156
+ </Fragment>
140
157
  );
141
- };
158
+ }
142
159
 
143
160
  export default CollectibleGameSets;
@@ -0,0 +1,198 @@
1
+ import React, { useMemo, useCallback } from 'react';
2
+ import { ChevronDown as ArrowDown } from 'react-feather';
3
+ import { FormattedMessage, useIntl } from 'react-intl';
4
+ import { array, arrayOf, shape, string } from 'prop-types';
5
+ import { useDropdown } from '@magento/peregrine/lib/hooks/useDropdown';
6
+
7
+ import { useStyle } from '@magento/venia-ui/lib/classify';
8
+ import SortItem from './customSortItem';
9
+ import defaultClasses from './customSortBy.module.css';
10
+ import Button from '@magento/venia-ui/lib/components/Button';
11
+ // import Icon from '@magento/venia-ui/lib/components/Icon';
12
+ import { ArrowUp2 } from 'iconsax-react';
13
+ import cn from 'classnames';
14
+
15
+ const CustomSortBy = props => {
16
+ const classes = useStyle(defaultClasses, props.classes);
17
+ const { availableSortMethods, sortProps } = props;
18
+ const [currentSort, setSort] = sortProps;
19
+ const { elementRef, expanded, setExpanded } = useDropdown();
20
+ const { formatMessage, locale } = useIntl();
21
+
22
+ const orderSortingList = useCallback(
23
+ list => {
24
+ return list.sort((a, b) => {
25
+ return a.text.localeCompare(b.text, locale, {
26
+ sensitivity: 'base'
27
+ });
28
+ });
29
+ },
30
+ [locale]
31
+ );
32
+
33
+ const sortMethodsFromConfig = availableSortMethods
34
+ ? availableSortMethods
35
+ .map(method => {
36
+ const { value, label } = method;
37
+ if (value !== 'price' && value !== 'position') {
38
+ return {
39
+ id: `sortItem.${value}`,
40
+ text: label,
41
+ attribute: value,
42
+ sortDirection: 'ASC'
43
+ };
44
+ }
45
+ })
46
+ .filter(method => !!method)
47
+ : null;
48
+
49
+ // click event for menu items
50
+ const handleItemClick = useCallback(
51
+ sortAttribute => {
52
+ setSort(prevSort => {
53
+ return {
54
+ sortText: sortAttribute.text,
55
+ value: sortAttribute.attribute,
56
+ };
57
+ });
58
+ setExpanded(false);
59
+ },
60
+ [setExpanded, setSort]
61
+ );
62
+
63
+ const sortElements = useMemo(() => {
64
+ // should be not render item in collapsed mode.
65
+ if (!expanded) {
66
+ return null;
67
+ }
68
+
69
+ const defaultSortMethods = [
70
+ {
71
+ id: 'sortItem.relevance',
72
+ text: formatMessage({
73
+ id: 'sortItem.relevance',
74
+ defaultMessage: 'Best Match'
75
+ }),
76
+ attribute: 'relevance',
77
+ sortDirection: 'DESC'
78
+ },
79
+ {
80
+ id: 'sortItem.priceDesc',
81
+ text: formatMessage({
82
+ id: 'sortItem.priceDesc',
83
+ defaultMessage: 'Price: High to Low'
84
+ }),
85
+ attribute: 'price',
86
+ sortDirection: 'DESC'
87
+ },
88
+ {
89
+ id: 'sortItem.priceAsc',
90
+ text: formatMessage({
91
+ id: 'sortItem.priceAsc',
92
+ defaultMessage: 'Price: Low to High'
93
+ }),
94
+ attribute: 'price',
95
+ sortDirection: 'ASC'
96
+ }
97
+ ];
98
+
99
+ const allSortingMethods = sortMethodsFromConfig
100
+ ? sortMethodsFromConfig
101
+ : defaultSortMethods;
102
+
103
+ const itemElements = Array.from(allSortingMethods, sortItem => {
104
+ const { attribute, sortDirection } = sortItem;
105
+ const isActive =
106
+ currentSort.value === attribute;
107
+
108
+ const key = `${attribute}--${sortDirection}`;
109
+ return (
110
+ <li key={key} className={classes.menuItem}>
111
+ <SortItem
112
+ sortItem={sortItem}
113
+ active={isActive}
114
+ onClick={handleItemClick}
115
+ />
116
+ </li>
117
+ );
118
+ });
119
+
120
+ return (
121
+ <div className={classes.menu}>
122
+ <ul className='flex flex-col gap-2'>{itemElements}</ul>
123
+ </div>
124
+ );
125
+ }, [
126
+ classes.menu,
127
+ classes.menuItem,
128
+ currentSort.sortAttribute,
129
+ currentSort.sortDirection,
130
+ currentSort.sortFromSearch,
131
+ expanded,
132
+ formatMessage,
133
+ handleItemClick,
134
+ orderSortingList,
135
+ sortMethodsFromConfig
136
+ ]);
137
+
138
+ // expand or collapse on click
139
+ const handleSortClick = () => {
140
+ setExpanded(!expanded);
141
+ };
142
+
143
+ const handleKeypress = e => {
144
+ if (e.code == 'Enter') {
145
+ setExpanded(expanded);
146
+ }
147
+ };
148
+ const result = expanded
149
+ ? formatMessage({
150
+ id: 'productSort.sortButtonExpanded',
151
+ defaultMessage: 'Sort Button Expanded'
152
+ })
153
+ : formatMessage({
154
+ id: 'productSort.sortButtonCollapsed',
155
+ defaultMessage: 'Sort Button Collapsed'
156
+ });
157
+
158
+ return (
159
+ <div
160
+ ref={elementRef}
161
+ className={classes.root}
162
+ data-cy="CustomSortBy-root"
163
+ aria-busy="false"
164
+ >
165
+ <Button
166
+ priority={'low'}
167
+ classes={{
168
+ root_lowPriority: classes.sortButton
169
+ }}
170
+ onClick={handleSortClick}
171
+ data-cy="CustomSortBy-sortButton"
172
+ onKeyDown={handleKeypress}
173
+ aria-label={result}
174
+ className='border border-gray-100 border-solid rounded-[5px] p-2.5 flex gap-x-[15px]'
175
+ >
176
+ <span className={classes.mobileText}>
177
+ <FormattedMessage
178
+ id={'productSort.sortButton'}
179
+ defaultMessage={'Sort'}
180
+ />
181
+ </span>
182
+ <span className={cn(classes.desktopText, 'flex items-center gap-[15px]')}>
183
+ <span className={classes.sortText}>
184
+ <FormattedMessage
185
+ id={'productSort.sortByButton'}
186
+ defaultMessage={'Sort by'}
187
+ />
188
+ &nbsp;{currentSort.sortText}
189
+ </span>
190
+ <ArrowUp2 size={15} className={cn('text-gray-900 transition-all stroke-[#292D32]', expanded ? 'rotate-0' : 'rotate-180')} />
191
+ </span>
192
+ </Button>
193
+ {sortElements}
194
+ </div>
195
+ );
196
+ };
197
+
198
+ export default CustomSortBy;
@@ -0,0 +1,68 @@
1
+ .root {
2
+ composes: relative from global;
3
+ composes: ml-2xs from global;
4
+ display: flex;
5
+ flex-direction: row-reverse;
6
+ margin-top: 20px;
7
+ }
8
+
9
+ .menu {
10
+ composes: absolute from global;
11
+ composes: bg-clip-padding from global;
12
+ composes: bg-white from global;
13
+ composes: border from global;
14
+ composes: border-solid from global;
15
+ composes: border-gray-100 from global;
16
+ composes: list-none from global;
17
+ composes: mb-0 from global;
18
+ composes: min-w-[10rem] from global;
19
+ composes: mt-0.5 from global;
20
+ composes: mx-0 from global;
21
+ composes: right-0 from global;
22
+ composes: rounded from global;
23
+ composes: shadow-menu from global;
24
+ composes: text-colorDefault from global;
25
+ composes: text-left from global;
26
+ composes: top-[110%] from global;
27
+ composes: z-menu from global;
28
+ composes: p-2.5 from global;
29
+ }
30
+
31
+ .menuItem {
32
+ composes: hover_bg-gray-100 from global;
33
+ composes: rounded-[5px] from global;
34
+ composes: relative from global;
35
+ }
36
+
37
+ .sortButton {
38
+ composes: rounded-[5px] from global;
39
+ composes: p-2.5 from global;
40
+
41
+ composes: min-w-[154px] from global;
42
+ }
43
+
44
+ .desktopText {
45
+ composes: hidden from global;
46
+
47
+ composes: lg_inline-flex from global;
48
+ }
49
+
50
+ .sortText {
51
+ composes: leading-normal from global;
52
+ composes: text-colorDefault from global;
53
+ composes: text-base from global;
54
+ }
55
+
56
+ .desktopIconWrapper {
57
+ transform: translateX(10px);
58
+ }
59
+
60
+ .desktopIcon {
61
+ /* composes: icon from '../Icon/icon.module.css'; */
62
+
63
+ composes: stroke-gray-500 from global;
64
+ }
65
+
66
+ .mobileText {
67
+ composes: lg_hidden from global;
68
+ }
@@ -0,0 +1,57 @@
1
+ import React, { useCallback } from 'react';
2
+ import { Check } from 'react-feather';
3
+ import { bool, func, shape, string } from 'prop-types';
4
+
5
+ import { useStyle } from '@magento/venia-ui/lib/classify';
6
+ import defaultClasses from './customSortItem.module.css';
7
+ import cn from 'classnames';
8
+
9
+ const CustomSortItem = props => {
10
+ const { active, onClick, sortItem } = props;
11
+ const classes = useStyle(defaultClasses, props.classes);
12
+
13
+ const handleClick = useCallback(
14
+ e => {
15
+ // use only left click for selection
16
+ if (e.button === 0) {
17
+ onClick(sortItem);
18
+ }
19
+ },
20
+ [sortItem, onClick]
21
+ );
22
+
23
+ const handleKeyDown = useCallback(
24
+ e => {
25
+ if (e.key === 'Enter' || e.key === ' ') {
26
+ e.preventDefault();
27
+ onClick(sortItem);
28
+ }
29
+ },
30
+ [onClick, sortItem]
31
+ );
32
+
33
+ return (
34
+ <button
35
+ className={cn(classes.root, active ? `before_block before_h-[100%] before_w-[3px] before_bg-blue-700 before_left-[-10px] before_absolute` : '')}
36
+ data-cy={active ? 'CustomSortItem-activeButton' : 'CustomSortItem-button'}
37
+ onMouseDown={handleClick}
38
+ onKeyDown={handleKeyDown}
39
+ >
40
+ <span className={classes.content}>
41
+ <span className={classes.text}>{sortItem.text}</span>
42
+ </span>
43
+ </button>
44
+ );
45
+ };
46
+
47
+ CustomSortItem.propTypes = {
48
+ active: bool,
49
+ classes: shape({
50
+ content: string,
51
+ root: string,
52
+ text: string
53
+ }),
54
+ onClick: func
55
+ };
56
+
57
+ export default CustomSortItem;
@@ -0,0 +1,23 @@
1
+ .root {
2
+ composes: flex from global;
3
+ composes: items-center from global;
4
+ composes: w-full from global;
5
+ }
6
+
7
+ .content {
8
+ composes: items-center from global;
9
+ composes: grid from global;
10
+ composes: grid-cols-[1fr] from global;
11
+ composes: grid-flow-col from global;
12
+ composes: gap-3 from global;
13
+ composes: h-[30px] from global;
14
+ composes: px-3 from global;
15
+ composes: py-0 from global;
16
+ composes: w-full from global;
17
+ }
18
+
19
+ .text {
20
+ composes: text-left from global;
21
+ composes: whitespace-nowrap from global;
22
+ composes: text-base from global;
23
+ }
@@ -0,0 +1 @@
1
+ export { default } from './customSortBy';
@@ -20,5 +20,4 @@
20
20
  composes: bg-white from global;
21
21
  composes: border-gray-100 from global;
22
22
  border-radius: 5px;
23
- composes: shadow-type-1 from global;
24
23
  }
@@ -29,7 +29,7 @@ const ProductListTab = props => {
29
29
  </span> */}
30
30
  <nav>
31
31
  <ul className={classes.buttonContainer}>
32
- <li><button className={allProductClass}>All Products</button></li>
32
+ <li><button className={classes.activeFirstButton}>All Products</button></li>
33
33
  <li><button className={classes.middleButton}>Pre Order</button></li>
34
34
  <li><button className={classes.lastButton}>Auctions</button></li>
35
35
  </ul>
@@ -0,0 +1,35 @@
1
+ import { useCustomSubCategory } from '@riosst100/pwa-marketplace/src/talons/SubCategory/useCustomSubCategory';
2
+ import React, { useState } from 'react';
3
+ import { Link } from 'react-router-dom';
4
+ import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
5
+ import defaultClasses from './customSubCategory.module.css';
6
+ import { useStyle } from '@magento/venia-ui/lib/classify';
7
+
8
+ const CustomSubCategory = props => {
9
+ const { customSubCategory } = props;
10
+
11
+ const talonProps = useCustomSubCategory({ customSubCategory });
12
+
13
+ const classes = useStyle(defaultClasses, props.classes);
14
+
15
+ const {
16
+ normalizedData
17
+ } = talonProps;
18
+
19
+ const subCategory = [];
20
+
21
+ normalizedData && normalizedData.map(({ text, path }, index) => {
22
+ subCategory.push(
23
+ <Link
24
+ key={index}
25
+ to={resourceUrl(path)}
26
+ >
27
+ <li className={classes.item}>{text}</li>
28
+ </Link>
29
+ )
30
+ });
31
+
32
+ return subCategory.length ? <ul className={classes.root}>{subCategory}</ul> : '';
33
+ };
34
+
35
+ export default CustomSubCategory;
@@ -0,0 +1,22 @@
1
+ .root {
2
+ composes: flex from global;
3
+ composes: flex-wrap from global;
4
+ composes: mt-3 from global;
5
+ composes: gap-[15px] from global;
6
+ margin-bottom: 10px;
7
+ }
8
+
9
+ .item {
10
+ composes: px-4 from global;
11
+ composes: py-2 from global;
12
+ composes: transition-colors from global;
13
+ composes: duration-150 from global;
14
+ composes: border from global;
15
+ composes: border-solid from global;
16
+ composes: leading-normal from global;
17
+ composes: text-base from global;
18
+ composes: text-colorDefault from global;
19
+ composes: bg-white from global;
20
+ composes: border-gray-100 from global;
21
+ border-radius: 5px;
22
+ }
@@ -8,21 +8,39 @@ import { useFilterSidebar } from '@magento/peregrine/lib/talons/FilterSidebar';
8
8
  import CollectibleGameSets from '@riosst100/pwa-marketplace/src/components/CollectibleGameSets/collectibleGameSets';
9
9
 
10
10
  const SubCategory = props => {
11
- // const { children } = props;
11
+ const { children } = props;
12
12
 
13
- // const talonProps = useSubCategory({ children });
13
+ const talonProps = useSubCategory({ children });
14
14
 
15
15
  const classes = useStyle(defaultClasses, props.classes);
16
16
 
17
- const [activeTab, setActiveTab] = useState('singles')
17
+ const {
18
+ normalizedData
19
+ } = talonProps;
20
+
21
+ const subCategory = [];
22
+
23
+ normalizedData && normalizedData.map(({ text, path }, index) => {
24
+ subCategory.push(
25
+ <Link
26
+ key={index}
27
+ to={resourceUrl(path)}
28
+ >
29
+ <li className={classes.item}>{text}</li>
30
+ </Link>
31
+ )
32
+ });
33
+
34
+ return subCategory.length ? <ul className={classes.root}>{subCategory}</ul> : '';
35
+
36
+ // const [activeTab, setActiveTab] = useState('singles')
18
37
 
19
38
  return (
20
39
  <>
21
40
  <ul className={classes.root}>
22
- <li className={classes.item} onClick={() => setActiveTab('singles')}>All Sets</li>
23
- <li className={classes.item} onClick={() => setActiveTab('sealed-products')}>Expansion Sets</li>
41
+ <li className={classes.item}>All Sets</li>
42
+ <li className={classes.item}>Expansion Sets</li>
24
43
  </ul>
25
- {activeTab == "singles" ? <CollectibleGameSets product_type={"singles"} /> : <CollectibleGameSets product_type={"sealed-products"} />}
26
44
  </>
27
45
  )
28
46
  };
@@ -0,0 +1,252 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useQuery } from '@apollo/client';
3
+ import { useHistory, useLocation } from 'react-router-dom';
4
+
5
+ import { useAppContext } from '@magento/peregrine/lib/context/app';
6
+
7
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
8
+ import { useFilterState } from '@magento/peregrine/lib/talons/FilterModal';
9
+ import {
10
+ getSearchFromState,
11
+ getStateFromSearch,
12
+ sortFiltersArray,
13
+ stripHtml
14
+ } from '@magento/peregrine/lib/talons/FilterModal/helpers';
15
+
16
+ import DEFAULT_OPERATIONS from '@magento/peregrine/lib/talons/FilterModal/filterModal.gql';
17
+
18
+ const DRAWER_NAME = 'filter';
19
+
20
+ export const useFilterSidebar = props => {
21
+ const { filters } = props;
22
+
23
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
24
+ const { getFilterInputsQuery } = operations;
25
+
26
+ const [isApplying, setIsApplying] = useState(false);
27
+ const [{ drawer }, { toggleDrawer, closeDrawer }] = useAppContext();
28
+ const [filterState, filterApi] = useFilterState();
29
+ const prevDrawer = useRef(null);
30
+ const isOpen = drawer === DRAWER_NAME;
31
+
32
+ const history = useHistory();
33
+ const { pathname, search } = useLocation();
34
+
35
+ const { data: introspectionData } = useQuery(getFilterInputsQuery);
36
+
37
+ const attributeCodes = useMemo(
38
+ () => filters.map(({ attribute_code }) => attribute_code),
39
+ [filters]
40
+ );
41
+
42
+ // Create a set of disabled filters.
43
+ const DISABLED_FILTERS = useMemo(() => {
44
+ const disabled = new Set();
45
+ // Disable category filtering when not on a search page.
46
+ if (pathname !== '/search.html') {
47
+ // disabled.add('category_id');
48
+ // disabled.add('category_uid');
49
+ }
50
+
51
+ return disabled;
52
+ }, [pathname]);
53
+
54
+ // Get "allowed" filters by intersection of filter attribute codes and
55
+ // schema input field types. This restricts the displayed filters to those
56
+ // that the api will understand.
57
+ const possibleFilters = useMemo(() => {
58
+ const nextFilters = new Set();
59
+ const inputFields = introspectionData
60
+ ? introspectionData.__type.inputFields
61
+ : [];
62
+
63
+ // perform mapping and filtering in the same cycle
64
+ for (const { name } of inputFields) {
65
+ const isValid = attributeCodes.includes(name);
66
+ const isEnabled = !DISABLED_FILTERS.has(name);
67
+
68
+ if (isValid && isEnabled) {
69
+ nextFilters.add(name);
70
+ }
71
+ }
72
+
73
+ return nextFilters;
74
+ }, [DISABLED_FILTERS, attributeCodes, introspectionData]);
75
+
76
+ const isBooleanFilter = options => {
77
+ const optionsString = JSON.stringify(options);
78
+ return (
79
+ options.length <= 2 &&
80
+ (optionsString.includes(
81
+ JSON.stringify({
82
+ __typename: 'AggregationOption',
83
+ label: '0',
84
+ value: '0'
85
+ })
86
+ ) ||
87
+ optionsString.includes(
88
+ JSON.stringify({
89
+ __typename: 'AggregationOption',
90
+ label: '1',
91
+ value: '1'
92
+ })
93
+ ))
94
+ );
95
+ };
96
+
97
+ // iterate over filters once to set up all the collections we need
98
+ const [
99
+ filterNames,
100
+ filterKeys,
101
+ filterItems,
102
+ filterFrontendInput
103
+ ] = useMemo(() => {
104
+ const names = new Map();
105
+ const keys = new Set();
106
+ const frontendInput = new Map();
107
+ const itemsByGroup = new Map();
108
+
109
+ const sortedFilters = sortFiltersArray([...filters]);
110
+
111
+ for (const filter of sortedFilters) {
112
+ const { options, label: name, attribute_code: group } = filter;
113
+
114
+ // If this aggregation is not a possible filter, just back out.
115
+ if (possibleFilters.has(group)) {
116
+ const items = [];
117
+
118
+ // add filter name
119
+ names.set(group, name);
120
+
121
+ // add filter key permutations
122
+ keys.add(`${group}[filter]`);
123
+
124
+ // TODO: Get all frontend input type from gql if other filter input types are needed
125
+ // See https://github.com/magento-commerce/magento2-pwa/pull/26
126
+ if (isBooleanFilter(options)) {
127
+ frontendInput.set(group, 'boolean');
128
+ // add items
129
+ items.push({
130
+ title: 'No',
131
+ value: '0',
132
+ label: name + ':' + 'No'
133
+ });
134
+ items.push({
135
+ title: 'Yes',
136
+ value: '1',
137
+ label: name + ':' + 'Yes'
138
+ });
139
+ } else {
140
+ // Add frontend input type
141
+ frontendInput.set(group, null);
142
+ // add items
143
+ for (const { label, value } of options) {
144
+ items.push({ title: stripHtml(label), value });
145
+ }
146
+ }
147
+ itemsByGroup.set(group, items);
148
+ }
149
+ }
150
+
151
+ return [names, keys, itemsByGroup, frontendInput];
152
+ }, [filters, possibleFilters]);
153
+
154
+ // on apply, write filter state to location
155
+ useEffect(() => {
156
+ if (isApplying) {
157
+ const nextSearch = getSearchFromState(
158
+ search,
159
+ filterKeys,
160
+ filterState
161
+ );
162
+
163
+ // write filter state to history
164
+ history.push({ pathname, search: nextSearch });
165
+
166
+ // mark the operation as complete
167
+ setIsApplying(false);
168
+ }
169
+ }, [filterKeys, filterState, history, isApplying, pathname, search]);
170
+
171
+ const handleOpen = useCallback(() => {
172
+ toggleDrawer(DRAWER_NAME);
173
+ }, [toggleDrawer]);
174
+
175
+ const handleClose = useCallback(() => {
176
+ closeDrawer();
177
+ }, [closeDrawer]);
178
+
179
+ const handleApply = useCallback(() => {
180
+ setIsApplying(true);
181
+ handleClose();
182
+ }, [handleClose]);
183
+
184
+ const handleReset = useCallback(() => {
185
+ filterApi.clear();
186
+ setIsApplying(true);
187
+ }, [filterApi, setIsApplying]);
188
+
189
+ const handleKeyDownActions = useCallback(
190
+ event => {
191
+ // do not handle keyboard actions when the modal is closed
192
+ if (!isOpen) {
193
+ return;
194
+ }
195
+
196
+ switch (event.keyCode) {
197
+ // when "Esc" key fired -> close the modal
198
+ case 27:
199
+ handleClose();
200
+ break;
201
+ }
202
+ },
203
+ [isOpen, handleClose]
204
+ );
205
+
206
+ useEffect(() => {
207
+ const justOpened =
208
+ prevDrawer.current === null && drawer === DRAWER_NAME;
209
+ const justClosed =
210
+ prevDrawer.current === DRAWER_NAME && drawer === null;
211
+
212
+ // on drawer toggle, read filter state from location
213
+ if (justOpened || justClosed) {
214
+ const nextState = getStateFromSearch(
215
+ search,
216
+ filterKeys,
217
+ filterItems
218
+ );
219
+
220
+ filterApi.setItems(nextState);
221
+ }
222
+
223
+ // on drawer close, update the modal visibility state
224
+ if (justClosed) {
225
+ handleClose();
226
+ }
227
+
228
+ prevDrawer.current = drawer;
229
+ }, [drawer, filterApi, filterItems, filterKeys, search, handleClose]);
230
+
231
+ useEffect(() => {
232
+ const nextState = getStateFromSearch(search, filterKeys, filterItems);
233
+
234
+ filterApi.setItems(nextState);
235
+ }, [filterApi, filterItems, filterKeys, search]);
236
+
237
+ return {
238
+ filterApi,
239
+ filterItems,
240
+ filterKeys,
241
+ filterNames,
242
+ filterFrontendInput,
243
+ filterState,
244
+ handleApply,
245
+ handleClose,
246
+ handleKeyDownActions,
247
+ handleOpen,
248
+ handleReset,
249
+ isApplying,
250
+ isOpen
251
+ };
252
+ };
@@ -29,7 +29,13 @@ export const GET_CATEGORY_CONTENT = gql`
29
29
  description
30
30
  url_key
31
31
  url_path
32
- custom_landing_page
32
+ allowed_filters {
33
+ code
34
+ }
35
+ custom_subcategory {
36
+ name
37
+ path
38
+ }
33
39
  attributes_block {
34
40
  label
35
41
  code
@@ -85,12 +85,44 @@ export const useCategoryContent = props => {
85
85
  }
86
86
  }, [categoryId, getSortMethods]);
87
87
 
88
- const filters = filterData ? filterData.products.aggregations : null;
88
+ const rawFilters = filterData ? filterData.products.aggregations : null;
89
89
  const items = data ? data.products.items : placeholderItems;
90
+ const category =
91
+ categoryData && categoryData.categories.items.length
92
+ ? categoryData.categories.items[0]
93
+ : null;
90
94
  const children =
91
95
  categoryData && categoryData.categories.items.length
92
96
  ? categoryData.categories.items[0].children
93
97
  : null;
98
+
99
+ const filters = [];
100
+
101
+ rawFilters && rawFilters.map((filter, index) => {
102
+
103
+ const filterOptions = [];
104
+ let label = filter.label;
105
+ if (filter.attribute_code == "category_uid") {
106
+ children && children.map((category, index) => {
107
+ filterOptions.push({
108
+ 'label': category.name,
109
+ 'value': category.uid
110
+ });
111
+ });
112
+
113
+ label = category ? category.name : label;
114
+ }
115
+
116
+ const newFilter = {
117
+ attribute_code: filter.attribute_code,
118
+ count: filter.count,
119
+ label: label,
120
+ options: filterOptions.length ? filterOptions : filter.options
121
+ };
122
+
123
+ filters.push(newFilter);
124
+ });
125
+
94
126
  const attributesBlock = categoryData && categoryData.categories.items.length
95
127
  ? categoryData.categories.items[0].attributes_block
96
128
  : null;
@@ -102,10 +134,7 @@ export const useCategoryContent = props => {
102
134
  ? data.products.page_info.total_pages
103
135
  : null;
104
136
  const totalCount = data ? data.products.total_count : null;
105
- const category =
106
- categoryData && categoryData.categories.items.length
107
- ? categoryData.categories.items[0]
108
- : null;
137
+
109
138
  const categoryName =
110
139
  categoryData && categoryData.categories.items.length
111
140
  ? categoryData.categories.items[0].name
@@ -31,6 +31,7 @@ import BrandSlider from '@riosst100/pwa-marketplace/src/components/BrandSlider';
31
31
  import CollectibleGameSets from '@riosst100/pwa-marketplace/src/components/CollectibleGameSets/collectibleGameSets';
32
32
  import { useLocation } from 'react-router-dom';
33
33
  import { getFiltersFromSearch } from '@magento/peregrine/lib/talons/FilterModal/helpers';
34
+ import CustomSubCategory from '@riosst100/pwa-marketplace/src/components/SubCategory/customSubCategory';
34
35
 
35
36
  const FilterModal = React.lazy(() => import('@magento/venia-ui/lib/components/FilterModal'));
36
37
  const FilterSidebar = React.lazy(() =>
@@ -94,7 +95,7 @@ const CategoryContent = props => {
94
95
  ) : null;
95
96
 
96
97
  const sidebar = shouldShowFilterButtons ? (
97
- <FilterSidebar filters={filters} />
98
+ <FilterSidebar children={children} filters={filters} allowedFilters={category ? category.allowed_filters : []} />
98
99
  ) : shouldShowFilterShimmer ? (
99
100
  <FilterSidebarShimmer />
100
101
  ) : null;
@@ -174,12 +175,34 @@ const CategoryContent = props => {
174
175
  const categoryTitle = categoryName ? categoryName : <Shimmer width={5} />;
175
176
 
176
177
  const { search } = useLocation();
177
- const activeFilters = getFiltersFromSearch(search);
178
+ const allActiveFilters = getFiltersFromSearch(search);
179
+
180
+ const activeFilters = [];
181
+
182
+ if (allActiveFilters && allActiveFilters.size > 0) {
183
+ allActiveFilters.forEach((value, key) => {
184
+ value.forEach((value) => {
185
+ const filterArr = value.split(',');
186
+
187
+ const label = filterArr[0];
188
+ const optionId = filterArr[1];
189
+ if (key == "card_set") {
190
+ activeFilters.push(
191
+ {
192
+ 'label': label
193
+ }
194
+ )
195
+ }
196
+ })
197
+ });
198
+ }
199
+
200
+ const currentFilter = activeFilters && activeFilters.length ? activeFilters[activeFilters.length - 1].label : '';
178
201
 
179
202
  return (
180
203
  <Fragment>
181
- <Breadcrumbs categoryId={categoryId} />
182
- <StoreTitle>{categoryName}</StoreTitle>
204
+ <Breadcrumbs categoryId={categoryId} currentFilter={activeFilters} />
205
+ <StoreTitle>{currentFilter ? currentFilter : categoryName}</StoreTitle>
183
206
  <article className={classes.root} data-cy="CategoryContent-root">
184
207
  <div className={classes.categoryHeader}>
185
208
  <h1 aria-live="polite" className={classes.title}>
@@ -187,16 +210,17 @@ const CategoryContent = props => {
187
210
  className={classes.categoryTitle}
188
211
  data-cy="CategoryContent-categoryTitle"
189
212
  >
190
- {categoryTitle}
213
+ {currentFilter ? currentFilter : categoryTitle}
191
214
  </span>
192
215
  </h1>
193
216
  {categoryDescriptionElement}
194
217
  </div>
195
- {activeFilters.size <= 0 && category && category.custom_landing_page ? (
218
+ {/* {activeFilters.size <= 0 && category && category.custom_landing_page ? ( */}
196
219
  <>
197
- <SubCategory customLandingPage={category.custom_landing_page} />
220
+ <SubCategory children={children} />
221
+ <CustomSubCategory customSubCategory={category ? category.custom_subcategory : null} />
198
222
  </>
199
- ) : (
223
+ {/* ) : ( */}
200
224
  <>
201
225
  {/* <SubCategory filters={filters} children={children} /> */}
202
226
  <FilterTop filters={filters} category={category} />
@@ -232,7 +256,7 @@ const CategoryContent = props => {
232
256
  </div>
233
257
  </div>
234
258
  </>
235
- )}
259
+ {/* )} */}
236
260
  </article>
237
261
  </Fragment>
238
262
  );
@@ -10,7 +10,8 @@ import Shimmer from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/comp
10
10
  import defaultClasses from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.module.css';
11
11
  import { ArrowRight2 } from 'iconsax-react';
12
12
  import cn from 'classnames';
13
-
13
+ import { getFiltersFromSearch } from '@magento/peregrine/lib/talons/FilterModal/helpers';
14
+ import { useLocation } from 'react-router-dom';
14
15
 
15
16
  /**
16
17
  * Breadcrumbs! Generates a sorted display of category links.
@@ -21,7 +22,7 @@ import cn from 'classnames';
21
22
  const Breadcrumbs = props => {
22
23
  const classes = useStyle(defaultClasses, props.classes);
23
24
 
24
- const { categoryId, currentProduct } = props;
25
+ const { categoryId, currentProduct, currentFilter } = props;
25
26
 
26
27
  const talonProps = useBreadcrumbs({ categoryId });
27
28
 
@@ -69,13 +70,36 @@ const Breadcrumbs = props => {
69
70
  );
70
71
  }
71
72
 
73
+ const filterBreadcrumbsElement = [];
74
+
75
+ currentFilter && currentFilter.length && currentFilter.map((filter, index) => {
76
+ currentProduct ? (
77
+ filterBreadcrumbsElement.push(
78
+ <Link
79
+ className={classes.link}
80
+ to={resourceUrl(currentCategoryPath)}
81
+ onClick={handleClick}
82
+ >
83
+ {filter.label}
84
+ </Link>
85
+ )
86
+ ) : (
87
+ filterBreadcrumbsElement.push(
88
+ <>
89
+ <span className={classes.divider}>{DELIMITER}</span>
90
+ <span className={cn(classes.currentCategory, 'text-blue-700 font-medium')}>{filter.label}</span>
91
+ </>
92
+ )
93
+ );
94
+ });
95
+
72
96
  // If we have a "currentProduct" it means we're on a PDP so we want the last
73
97
  // category text to be a link. If we don't have a "currentProduct" we're on
74
98
  // a category page so it should be regular text.
75
- const currentCategoryLink = currentProduct ? (
99
+ const currentCategoryLink = currentProduct || currentFilter && currentFilter.length ? (
76
100
  <Link
77
101
  className={classes.link}
78
- to={resourceUrl(currentCategoryPath)}
102
+ to={'/'+resourceUrl(currentCategoryPath)}
79
103
  onClick={handleClick}
80
104
  >
81
105
  {currentCategory}
@@ -90,7 +114,7 @@ const Breadcrumbs = props => {
90
114
  <span className={classes.text}>{currentProduct}</span>
91
115
  </Fragment>
92
116
  ) : null;
93
-
117
+
94
118
  return (
95
119
  <div className={classes.root} aria-live="polite" aria-busy="false">
96
120
  <Link className={classes.link} to="/">
@@ -99,6 +123,7 @@ const Breadcrumbs = props => {
99
123
  {links}
100
124
  <span className={classes.divider}>{DELIMITER}</span>
101
125
  {currentCategoryLink}
126
+ {filterBreadcrumbsElement}
102
127
  {currentProductNode}
103
128
  </div>
104
129
  );
@@ -20,16 +20,18 @@ const CurrentFilters = props => {
20
20
  const { title, value } = item || {};
21
21
  const key = `${group}::${title}_${value}`;
22
22
 
23
- elements.push(
24
- <li key={key} className={classes.item}>
25
- <CurrentFilter
26
- group={group}
27
- item={item}
28
- removeItem={removeItem}
29
- onRemove={onRemove}
30
- />
31
- </li>
32
- );
23
+ if (group != "card_set") {
24
+ elements.push(
25
+ <li key={key} className={classes.item}>
26
+ <CurrentFilter
27
+ group={group}
28
+ item={item}
29
+ removeItem={removeItem}
30
+ onRemove={onRemove}
31
+ />
32
+ </li>
33
+ );
34
+ }
33
35
  }
34
36
  }
35
37
 
@@ -245,7 +245,7 @@ const FilterList = props => {
245
245
 
246
246
  FilterList.defaultProps = {
247
247
  onApply: null,
248
- itemCountToShow: 2
248
+ itemCountToShow: 5
249
249
  };
250
250
 
251
251
  FilterList.propTypes = {
@@ -19,7 +19,7 @@ const SCROLL_OFFSET = 150;
19
19
  * @param {Object} props.filters - filters to display
20
20
  */
21
21
  const FilterSidebar = props => {
22
- const { filters, filterCountToOpen } = props;
22
+ const { filters, filterCountToOpen, allowedFilters } = props;
23
23
  const talonProps = useFilterSidebar({ filters });
24
24
  const {
25
25
  filterApi,
@@ -52,25 +52,33 @@ const FilterSidebar = props => {
52
52
  [handleApply, filterRef]
53
53
  );
54
54
 
55
+ const allowedFiltersArr = [];
56
+
57
+ allowedFilters.length && allowedFilters.map((val, index) => {
58
+ allowedFiltersArr.push(val.code);
59
+ });
60
+
55
61
  const filtersList = useMemo(
56
62
  () =>
57
63
  Array.from(filterItems, ([group, items], iteration) => {
58
64
  const blockState = filterState.get(group);
59
65
  const groupName = filterNames.get(group);
60
66
  const frontendInput = filterFrontendInput.get(group);
61
- return (
62
- <FilterBlock
63
- key={group}
64
- filterApi={filterApi}
65
- filterState={blockState}
66
- filterFrontendInput={frontendInput}
67
- group={group}
68
- items={items}
69
- name={groupName}
70
- onApply={handleApplyFilter}
71
- initialOpen={iteration < filterCountToOpen}
72
- />
73
- );
67
+ if (!allowedFiltersArr.length && group != "category_uid" || allowedFiltersArr.length && allowedFiltersArr.includes(group)) {
68
+ return (
69
+ <FilterBlock
70
+ key={group}
71
+ filterApi={filterApi}
72
+ filterState={blockState}
73
+ filterFrontendInput={frontendInput}
74
+ group={group}
75
+ items={items}
76
+ name={groupName}
77
+ onApply={handleApplyFilter}
78
+ initialOpen={iteration < filterCountToOpen}
79
+ />
80
+ );
81
+ }
74
82
  }),
75
83
  [
76
84
  filterApi,
@@ -20,6 +20,7 @@ export const GET_COLLECTIBLE_GAME_QUERY = gql`
20
20
  option_id
21
21
  set_abbreviation
22
22
  release_date
23
+ release_year
23
24
  release_number
24
25
  }
25
26
  }
@@ -8,8 +8,6 @@ import DEFAULT_OPERATIONS from './collectibleGameSets.gql';
8
8
 
9
9
  export const useCollectibleGameSets = props => {
10
10
 
11
- const { product_type } = props
12
-
13
11
  const operations = mergeOperations(DEFAULT_OPERATIONS, null);
14
12
  const { getStoreConfigData, getCollectibleGameQuery } = operations;
15
13
  const { pathname } = useLocation();
@@ -27,8 +25,8 @@ export const useCollectibleGameSets = props => {
27
25
 
28
26
  const pathnameArr = pathname.split('/');
29
27
 
30
- const categoryUrlKey = pathnameArr[pathnameArr.length - 1].replace('.html','');
31
- const productType = product_type ? product_type : 'singles';
28
+ const categoryUrlKey = pathnameArr[pathnameArr.length - 2].replace('.html','');
29
+ const productType = pathnameArr[pathnameArr.length - 1].replace('.html','');
32
30
 
33
31
  const categoryUrlSuffix = storeConfigData?.storeConfig?.category_url_suffix;
34
32
 
@@ -50,7 +48,6 @@ export const useCollectibleGameSets = props => {
50
48
  }
51
49
 
52
50
  const collectibleGameSets = data.collectibleGameSets;
53
-
54
51
  if (!collectibleGameSets) {
55
52
  return null;
56
53
  }
@@ -58,7 +55,6 @@ export const useCollectibleGameSets = props => {
58
55
  return collectibleGameSets;
59
56
  }, [data, categoryUrlKey]);
60
57
 
61
- // Update the page indicator if the GraphQL query is in flight.
62
58
  useEffect(() => {
63
59
  setPageLoading(isBackgroundLoading);
64
60
  }, [isBackgroundLoading, setPageLoading]);
@@ -0,0 +1,49 @@
1
+ import { useMemo } from 'react';
2
+ import { useQuery } from '@apollo/client';
3
+
4
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
5
+
6
+ import DEFAULT_OPERATIONS from './subCategory.gql';
7
+
8
+ const getPath = (path, suffix) => {
9
+ if (path) {
10
+ return `/${path}${suffix || ''}`;
11
+ }
12
+
13
+ // If there is no path this is just a dead link.
14
+ return '#';
15
+ };
16
+
17
+ export const useCustomSubCategory = props => {
18
+ const { customSubCategory } = props;
19
+
20
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
21
+ const { getStoreConfigQuery } = operations;
22
+
23
+ const { data: storeConfigData } = useQuery(getStoreConfigQuery, {
24
+ fetchPolicy: 'cache-and-network'
25
+ });
26
+
27
+ const categoryUrlSuffix = useMemo(() => {
28
+ if (storeConfigData) {
29
+ return storeConfigData.storeConfig.category_url_suffix;
30
+ }
31
+ }, [storeConfigData]);
32
+
33
+ const normalizedData = useMemo(() => {
34
+ if (customSubCategory) {
35
+ return (
36
+ customSubCategory.length &&
37
+ customSubCategory
38
+ .map(category => ({
39
+ text: category.name,
40
+ path: `/${category.path}`
41
+ }))
42
+ );
43
+ }
44
+ }, [categoryUrlSuffix, customSubCategory]);
45
+
46
+ return {
47
+ normalizedData: normalizedData || []
48
+ };
49
+ };