@riosst100/pwa-marketplace 1.6.1 → 1.6.3

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 (82) hide show
  1. package/package.json +3 -2
  2. package/src/components/AlphaFilter/alphaFilter.js +36 -8
  3. package/src/components/AlphaFilter/alphaFilter.shimmer.js +50 -0
  4. package/src/components/ArraySearchInput/arraySearchInput.js +100 -0
  5. package/src/components/ArraySearchInput/arraySearchInput.module.css +48 -0
  6. package/src/components/ArraySearchInput/index.js +1 -0
  7. package/src/components/CollectibleGameSets/collectibleGameSets.js +53 -25
  8. package/src/components/CollectibleGameSets/collectibleGameSets.module.css +8 -0
  9. package/src/components/CrossSeller/crossSellerBuy.js +19 -0
  10. package/src/components/CrossSeller/index.js +15 -0
  11. package/src/components/CrossSeller/item.js +79 -0
  12. package/src/components/CrossSeller/logo_seller.png +0 -0
  13. package/src/components/CrossSeller/starIcon.js +14 -0
  14. package/src/components/CrossSeller/verifyIcon.js +12 -0
  15. package/src/components/CustomSortBy/customSortBy.js +9 -11
  16. package/src/components/CustomSortBy/productSort.module.css +65 -0
  17. package/src/components/CustomSortBy/productSort.shimmer.js +28 -0
  18. package/src/components/CustomSortBy/productSort.shimmer.module.css +10 -0
  19. package/src/components/CustomSubCategory/subCategory.js +49 -0
  20. package/src/components/CustomSubCategory/subCategory.module.css +22 -0
  21. package/src/components/FilterTop/CurrentTopFilters/currentTopFilter.js +47 -0
  22. package/src/components/FilterTop/CurrentTopFilters/currentTopFilter.module.css +22 -0
  23. package/src/components/FilterTop/CurrentTopFilters/currentTopFilters.js +50 -0
  24. package/src/components/FilterTop/CurrentTopFilters/currentTopFilters.module.css +21 -0
  25. package/src/components/FilterTop/CurrentTopFilters/index.js +1 -0
  26. package/src/components/FilterTop/CustomFilters/customFilters.js +68 -30
  27. package/src/components/FilterTop/FilterBlockList/filterBlockList.js +50 -0
  28. package/src/components/FilterTop/FilterBlockList/filterBlockList.module.css +8 -0
  29. package/src/components/FilterTop/FilterBlockList/filterTopItem.js +40 -0
  30. package/src/components/FilterTop/FilterBlockList/filterTopItem.module.css +14 -0
  31. package/src/components/FilterTop/FilterBlockList/filterTopItemGroup.js +91 -0
  32. package/src/components/FilterTop/FilterBlockList/filterTopItemGroup.module.css +23 -0
  33. package/src/components/FilterTop/FilterBlockList/index.js +1 -0
  34. package/src/components/FilterTop/filterTop.js +131 -13
  35. package/src/components/FilterTop/filterTop.module.css +23 -58
  36. package/src/components/FilterTop/filterTop.shimmer.js +24 -24
  37. package/src/components/FilterTop/filterTopBlock.js +54 -0
  38. package/src/components/FilterTop/index.js +2 -2
  39. package/src/components/FilterTopBackup/CustomFilters/customFilter.js +83 -0
  40. package/src/components/FilterTopBackup/CustomFilters/customFilter.module.css +22 -0
  41. package/src/components/FilterTopBackup/CustomFilters/customFilters.js +132 -0
  42. package/src/components/FilterTopBackup/CustomFilters/customFilters.module.css +23 -0
  43. package/src/components/FilterTopBackup/CustomFilters/index.js +1 -0
  44. package/src/components/FilterTopBackup/filterTop.js +14 -0
  45. package/src/components/FilterTopBackup/filterTop.module.css +23 -0
  46. package/src/components/FilterTopBackup/filterTop.shimmer.js +24 -0
  47. package/src/components/FilterTopBackup/index.js +2 -0
  48. package/src/components/RelatedProduct/index.js +16 -0
  49. package/src/components/RelatedProduct/relatedProduct.js +112 -0
  50. package/src/components/RelatedProduct/sample.json +154 -0
  51. package/src/components/ShopBy/index.js +2 -0
  52. package/src/components/ShopBy/shopBy.js +237 -0
  53. package/src/components/ShopBy/shopBy.module.css +46 -0
  54. package/src/components/ShopBy/shopBy.shimmer.js +50 -0
  55. package/src/components/ShopByPage/index.js +1 -0
  56. package/src/components/ShopByPage/shopByPage.js +12 -0
  57. package/src/hooks/useCustomSort.js +21 -0
  58. package/src/intercept.js +1 -8
  59. package/src/overwrites/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js +2 -2
  60. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js +1 -0
  61. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryFragments.gql.js +31 -0
  62. package/src/overwrites/venia-ui/lib/RootComponents/Category/NoProductsFound/noProductsFound.js +0 -13
  63. package/src/overwrites/venia-ui/lib/RootComponents/Category/category.js +13 -2
  64. package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +31 -6
  65. package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.js +3 -2
  66. package/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js +5 -5
  67. package/src/overwrites/venia-ui/lib/components/Gallery/gallery.js +3 -3
  68. package/src/overwrites/venia-ui/lib/components/Gallery/item.js +3 -1
  69. package/src/overwrites/venia-ui/lib/components/Newsletter/newsletter.js +2 -2
  70. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/CustomAttributes/customAttributes.js +1 -1
  71. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +13 -1
  72. package/src/overwrites/venia-ui/lib/components/QuantityStepper/quantityStepper.js +2 -2
  73. package/src/overwrites/venia-ui/lib/components/SearchBar/searchBar.js +1 -1
  74. package/src/talons/ArraySearchInput/useArraySearchInput.js +58 -0
  75. package/src/talons/CollectibleGameSets/useCollectibleGameSets.js +75 -2
  76. package/src/talons/CustomFilters/useCustomFilters.js +163 -5
  77. package/src/talons/FilterTop/customFilters.gql.js +45 -0
  78. package/src/talons/FilterTop/filterTop.gql.js +45 -0
  79. package/src/talons/FilterTop/index.js +1 -0
  80. package/src/talons/FilterTop/useFilterTop.js +330 -0
  81. package/src/talons/ShopBy/shopBy.gql.js +47 -0
  82. package/src/talons/ShopBy/useShopBy.js +171 -0
@@ -0,0 +1,330 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useHistory, useLocation } from 'react-router-dom';
3
+
4
+ import { useAppContext } from '@magento/peregrine/lib/context/app';
5
+
6
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
7
+ import { useFilterState } from '@magento/peregrine/lib/talons/FilterModal';
8
+ import { useLazyQuery, useQuery } from '@apollo/client';
9
+
10
+ import {
11
+ getFiltersFromSearch,
12
+ getFilterInput,
13
+ getSearchFromState,
14
+ getStateFromSearch,
15
+ sortFiltersArray,
16
+ stripHtml
17
+ } from '@magento/peregrine/lib/talons/FilterModal/helpers';
18
+
19
+ import DEFAULT_OPERATIONS from './filterTop.gql';
20
+
21
+ const DRAWER_NAME = 'filter';
22
+
23
+ export const useFilterTop = props => {
24
+ const { filters } = props;
25
+
26
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
27
+ const { getFilterInputsQuery, getCustomFilters } = operations;
28
+
29
+ const [runQuery, queryResponse] = useLazyQuery(getCustomFilters, {
30
+ fetchPolicy: 'cache-and-network',
31
+ nextFetchPolicy: 'cache-first'
32
+ });
33
+
34
+ const [isApplying, setIsApplying] = useState(false);
35
+ const [{ drawer }, { toggleDrawer, closeDrawer }] = useAppContext();
36
+ const [filterState, filterApi] = useFilterState();
37
+ const prevDrawer = useRef(null);
38
+ const isOpen = drawer === DRAWER_NAME;
39
+
40
+ const history = useHistory();
41
+ const { pathname, search } = useLocation();
42
+
43
+ const { data: introspectionData } = useQuery(getFilterInputsQuery);
44
+
45
+ const filterTypeMap = useMemo(() => {
46
+ const typeMap = new Map();
47
+ if (introspectionData) {
48
+ introspectionData.__type.inputFields.forEach(({ name, type }) => {
49
+ typeMap.set(name, type.name);
50
+ });
51
+ }
52
+ return typeMap;
53
+ }, [introspectionData]);
54
+
55
+ useEffect(() => {
56
+
57
+ if (!filterTypeMap.size) {
58
+ return;
59
+ }
60
+
61
+ const filters = getFiltersFromSearch(search);
62
+
63
+ // Construct the filter arg object.
64
+ const newFilters = {};
65
+ filters.forEach((values, key) => {
66
+ newFilters[key] = getFilterInput(values, filterTypeMap.get(key));
67
+ });
68
+
69
+ // Use the category uid for the current category page regardless of the
70
+ // applied filters. Follow-up in PWA-404.
71
+ // newFilters['category_uid'] = { eq: id };
72
+
73
+ runQuery({
74
+ variables: {
75
+ filters: newFilters
76
+ }
77
+ });
78
+ }, [
79
+ runQuery,
80
+ filterTypeMap,
81
+ search
82
+ ]);
83
+
84
+ const attributeCodes = useMemo(
85
+ () => filters.map(({ attribute_code }) => attribute_code),
86
+ [filters]
87
+ );
88
+
89
+ // Create a set of disabled filters.
90
+ const DISABLED_FILTERS = useMemo(() => {
91
+ const disabled = new Set();
92
+ // Disable category filtering when not on a search page.
93
+ if (pathname !== '/search.html') {
94
+ // disabled.add('category_id');
95
+ // disabled.add('category_uid');
96
+ }
97
+
98
+ return disabled;
99
+ }, [pathname]);
100
+
101
+ // console.log(introspectionData)
102
+
103
+ // Get "allowed" filters by intersection of filter attribute codes and
104
+ // schema input field types. This restricts the displayed filters to those
105
+ // that the api will understand.
106
+ const possibleFilters = useMemo(() => {
107
+ const nextFilters = new Set();
108
+ const inputFields = introspectionData
109
+ ? introspectionData.__type.inputFields
110
+ : [];
111
+
112
+ // perform mapping and filtering in the same cycle
113
+ for (const { name } of inputFields) {
114
+ const isValid = attributeCodes.includes(name);
115
+ const isEnabled = !DISABLED_FILTERS.has(name);
116
+
117
+ if (isValid && isEnabled) {
118
+ nextFilters.add(name);
119
+ }
120
+ }
121
+
122
+ nextFilters.add('card_print_version');
123
+
124
+ return nextFilters;
125
+ }, [DISABLED_FILTERS, attributeCodes, introspectionData]);
126
+
127
+ const isBooleanFilter = options => {
128
+ const optionsString = JSON.stringify(options);
129
+ return (
130
+ options.length <= 2 &&
131
+ (optionsString.includes(
132
+ JSON.stringify({
133
+ __typename: 'AggregationOption',
134
+ label: '0',
135
+ value: '0'
136
+ })
137
+ ) ||
138
+ optionsString.includes(
139
+ JSON.stringify({
140
+ __typename: 'AggregationOption',
141
+ label: '1',
142
+ value: '1'
143
+ })
144
+ ))
145
+ );
146
+ };
147
+
148
+ const {
149
+ called: called,
150
+ loading: customFiltersLoading,
151
+ error,
152
+ data
153
+ } = queryResponse;
154
+
155
+ const customFiltersData = customFiltersLoading && !data ? null : (data ? data.customSubFilters : null);
156
+
157
+ // useEffect(() => {
158
+ // if (data) {
159
+ // filters.concat(customFiltersData);
160
+ // }
161
+ // }, [customFiltersData])
162
+
163
+ // const customFilters = [];
164
+
165
+ // iterate over filters once to set up all the collections we need
166
+ const [
167
+ filterNames,
168
+ filterKeys,
169
+ filterItems,
170
+ filterFrontendInput,
171
+ customFilters
172
+ ] = useMemo(() => {
173
+ const names = new Map();
174
+ const keys = new Set();
175
+ const frontendInput = new Map();
176
+ const itemsByGroup = new Map();
177
+ const customFiltersMap = [];
178
+
179
+ const sortedFilters = sortFiltersArray([...filters]);
180
+
181
+ for (const filter of sortedFilters) {
182
+ const { options, label: name, attribute_code: group } = filter;
183
+
184
+ // If this aggregation is not a possible filter, just back out.
185
+ if (possibleFilters.has(group)) {
186
+ const items = [];
187
+
188
+ // add filter name
189
+ names.set(group, name);
190
+
191
+ // add filter key permutations
192
+ keys.add(`${group}[filter]`);
193
+
194
+ // Add frontend input type
195
+ frontendInput.set(group, null);
196
+ // add items
197
+ for (const { label, value, path } of options) {
198
+ items.push({ title: stripHtml(label), value, path });
199
+ }
200
+
201
+ itemsByGroup.set(group, items);
202
+ }
203
+ }
204
+
205
+ if (customFiltersData) {
206
+ customFiltersData.map(({ attribute_code, options }, index) => {
207
+
208
+ customFiltersMap.push(attribute_code)
209
+
210
+
211
+ const items = [];
212
+
213
+ names.set(attribute_code, '');
214
+ frontendInput.set(attribute_code, null);
215
+
216
+ // add filter key permutations
217
+ keys.add(`${attribute_code}[filter]`);
218
+
219
+ // add items
220
+ for (const { label, value, path } of options) {
221
+ items.push({ title: stripHtml(label), value, path });
222
+ }
223
+
224
+ itemsByGroup.set(attribute_code, items);
225
+ })
226
+ }
227
+
228
+ return [names, keys, itemsByGroup, frontendInput, customFiltersMap];
229
+ }, [filters, customFiltersData, possibleFilters]);
230
+
231
+ // on apply, write filter state to location
232
+ useEffect(() => {
233
+ if (isApplying) {
234
+ const nextSearch = getSearchFromState(
235
+ search,
236
+ filterKeys,
237
+ filterState
238
+ );
239
+
240
+ // write filter state to history
241
+ history.push({ pathname, search: nextSearch });
242
+
243
+ // mark the operation as complete
244
+ setIsApplying(false);
245
+ }
246
+ }, [filterKeys, filterState, history, isApplying, pathname, search]);
247
+
248
+ const handleOpen = useCallback(() => {
249
+ toggleDrawer(DRAWER_NAME);
250
+ }, [toggleDrawer]);
251
+
252
+ const handleClose = useCallback(() => {
253
+ closeDrawer();
254
+ }, [closeDrawer]);
255
+
256
+ const handleApply = useCallback(() => {
257
+ setIsApplying(true);
258
+ handleClose();
259
+ }, [handleClose]);
260
+
261
+ const handleReset = useCallback(() => {
262
+ filterApi.clear();
263
+ setIsApplying(true);
264
+ }, [filterApi, setIsApplying]);
265
+
266
+ const handleKeyDownActions = useCallback(
267
+ event => {
268
+ // do not handle keyboard actions when the modal is closed
269
+ if (!isOpen) {
270
+ return;
271
+ }
272
+
273
+ switch (event.keyCode) {
274
+ // when "Esc" key fired -> close the modal
275
+ case 27:
276
+ handleClose();
277
+ break;
278
+ }
279
+ },
280
+ [isOpen, handleClose]
281
+ );
282
+
283
+ useEffect(() => {
284
+ const justOpened =
285
+ prevDrawer.current === null && drawer === DRAWER_NAME;
286
+ const justClosed =
287
+ prevDrawer.current === DRAWER_NAME && drawer === null;
288
+
289
+ // on drawer toggle, read filter state from location
290
+ if (justOpened || justClosed) {
291
+ const nextState = getStateFromSearch(
292
+ search,
293
+ filterKeys,
294
+ filterItems
295
+ );
296
+
297
+ filterApi.setItems(nextState);
298
+ }
299
+
300
+ // on drawer close, update the modal visibility state
301
+ if (justClosed) {
302
+ handleClose();
303
+ }
304
+
305
+ prevDrawer.current = drawer;
306
+ }, [drawer, filterApi, filterItems, filterKeys, search, handleClose]);
307
+
308
+ useEffect(() => {
309
+ const nextState = getStateFromSearch(search, filterKeys, filterItems);
310
+
311
+ filterApi.setItems(nextState);
312
+ }, [filterApi, filterItems, filterKeys, search]);
313
+
314
+ return {
315
+ filterApi,
316
+ filterItems,
317
+ filterKeys,
318
+ filterNames,
319
+ filterFrontendInput,
320
+ filterState,
321
+ handleApply,
322
+ handleClose,
323
+ handleKeyDownActions,
324
+ handleOpen,
325
+ handleReset,
326
+ isApplying,
327
+ isOpen,
328
+ customFilters
329
+ };
330
+ };
@@ -0,0 +1,47 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ export const GET_STORE_CONFIG_DATA = gql`
4
+ query getStoreConfigData {
5
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
6
+ storeConfig {
7
+ store_code
8
+ product_url_suffix
9
+ category_url_suffix
10
+ }
11
+ }
12
+ `;
13
+
14
+ export const GET_SHOP_BY_DATA_QUERY = gql`
15
+ query getShopByData($categoryUrlKey: String!, $attributeCode: String) {
16
+ shopByData(categoryUrlKey: $categoryUrlKey, attributeCode: $attributeCode) {
17
+ label
18
+ count
19
+ attribute_code
20
+ options {
21
+ label
22
+ value
23
+ }
24
+ position
25
+ }
26
+ }
27
+ `;
28
+
29
+ export const GET_CATEGORY_CONTENT = gql`
30
+ query getCategoryData($id: String!) {
31
+ categories(filters: { category_uid: { in: [$id] } }) {
32
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
33
+ items {
34
+ uid
35
+ name
36
+ url_key
37
+ url_path
38
+ }
39
+ }
40
+ }
41
+ `;
42
+
43
+ export default {
44
+ getStoreConfigData: GET_STORE_CONFIG_DATA,
45
+ getShopByDataQuery: GET_SHOP_BY_DATA_QUERY,
46
+ getCategoryContentQuery: GET_CATEGORY_CONTENT
47
+ };
@@ -0,0 +1,171 @@
1
+ import { useQuery } from '@apollo/client';
2
+ import { useEffect, useMemo } from 'react';
3
+ import { useLocation } from 'react-router-dom';
4
+ import { useAppContext } from '@magento/peregrine/lib/context/app';
5
+
6
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
7
+ import DEFAULT_OPERATIONS from './shopBy.gql';
8
+
9
+ export const useShopBy = props => {
10
+
11
+ const { searchQuery, setActive, currentSort, categoryId, shopby } = props
12
+
13
+ const { value: sortby } = currentSort
14
+
15
+ const operations = mergeOperations(DEFAULT_OPERATIONS, null);
16
+ const { getStoreConfigData, getShopByDataQuery, getCategoryContentQuery } = operations;
17
+ const { pathname } = useLocation();
18
+ // const [
19
+ // ,
20
+ // {
21
+ // actions: { setPageLoading }
22
+ // }
23
+ // ] = useAppContext();
24
+
25
+ const { data: storeConfigData } = useQuery(getStoreConfigData, {
26
+ fetchPolicy: 'cache-and-network',
27
+ nextFetchPolicy: 'cache-first'
28
+ });
29
+
30
+
31
+
32
+ // // console.log(sortby)
33
+
34
+ const pathnameArr = pathname.split('/');
35
+
36
+ const categoryUrlKey = pathnameArr[pathnameArr.length - 1].replace('.html','');
37
+
38
+ const categoryUrlSuffix = storeConfigData?.storeConfig?.category_url_suffix;
39
+
40
+ const { error, loading, data } = useQuery(getShopByDataQuery, {
41
+ fetchPolicy: 'cache-and-network',
42
+ nextFetchPolicy: 'cache-first',
43
+ skip: !storeConfigData,
44
+ variables: {
45
+ categoryUrlKey: categoryUrlKey,
46
+ attributeCode: shopby
47
+ }
48
+ });
49
+
50
+ 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'];
51
+
52
+ // const isBackgroundLoading = !!data && loading;
53
+
54
+ const originalDataResult = useMemo(() => {
55
+ if (!data) {
56
+ return null;
57
+ }
58
+
59
+ // console.log(data)
60
+
61
+ const rawData = data.shopByData;
62
+ if (!rawData) {
63
+ return null;
64
+ }
65
+
66
+ // console.log(rawData)
67
+
68
+ let options = rawData[0].options;
69
+
70
+ const mappingData = options.reduce((acc, item) => {
71
+ // console.log(item)
72
+ let firstLetter = item.label.charAt(0).toUpperCase();
73
+ if (!alpha.includes(firstLetter)) {
74
+ firstLetter = '#';
75
+ }
76
+ acc[firstLetter] = acc[firstLetter] || [];
77
+ acc[firstLetter].push(item);
78
+ return acc;
79
+ }, {});
80
+
81
+
82
+
83
+ // console.log(mappingData)
84
+
85
+ const sortbyData = [];
86
+
87
+ return sortby != "all" ? sortbyData && sortbyData.length ? sortbyData.sort((a, b) => b.release_type.toLowerCase().localeCompare(a.release_type.toLowerCase())) : sortbyData : mappingData;
88
+ }, [data, sortby, searchQuery]);
89
+
90
+ const dataResult = useMemo(() => {
91
+ if (!data) {
92
+ return null;
93
+ }
94
+
95
+ // console.log(data)
96
+
97
+ const rawData = data.shopByData;
98
+ if (!rawData) {
99
+ return null;
100
+ }
101
+
102
+ // console.log(rawData)
103
+
104
+ let options = rawData[0].options;
105
+
106
+ if (searchQuery) {
107
+ setActive('all')
108
+ options = options.filter(function(option) {
109
+ return option.label.search(new RegExp(searchQuery, "i")) != -1;
110
+ });
111
+ }
112
+
113
+ const mappingData = options.reduce((acc, item) => {
114
+ // console.log(item)
115
+ let firstLetter = item.label.charAt(0).toUpperCase();
116
+ if (!alpha.includes(firstLetter)) {
117
+ firstLetter = '#';
118
+ }
119
+ acc[firstLetter] = acc[firstLetter] || [];
120
+ acc[firstLetter].push(item);
121
+ return acc;
122
+ }, {});
123
+
124
+
125
+
126
+ // console.log(mappingData)
127
+
128
+ const sortbyData = [];
129
+
130
+ return sortby != "all" ? sortbyData && sortbyData.length ? sortbyData.sort((a, b) => b.release_type.toLowerCase().localeCompare(a.release_type.toLowerCase())) : sortbyData : mappingData;
131
+ }, [data, sortby, searchQuery]);
132
+
133
+ const availableGroups = originalDataResult ? Object.keys(originalDataResult).sort() : [];
134
+
135
+ const filteredAvailableGroups = dataResult ? Object.keys(dataResult).sort() : [];
136
+
137
+
138
+ const attributeData = data ? data.shopByData[0] : null
139
+
140
+ const { data: categoryData, loading: categoryLoading } = useQuery(
141
+ getCategoryContentQuery,
142
+ {
143
+ fetchPolicy: 'cache-and-network',
144
+ nextFetchPolicy: 'cache-first',
145
+ skip: !categoryId,
146
+ variables: {
147
+ id: categoryId
148
+ }
149
+ }
150
+ );
151
+
152
+ const category =
153
+ categoryData && categoryData.categories.items.length
154
+ ? categoryData.categories.items[0]
155
+ : null;
156
+
157
+ return {
158
+ error,
159
+ loading,
160
+ dataResult,
161
+ // collectibleGameSets,
162
+ filteredAvailableGroups,
163
+ categoryUrlSuffix,
164
+ categoryUrlKey,
165
+ availableGroups,
166
+ attributeData,
167
+ alpha,
168
+ category
169
+ // productType
170
+ };
171
+ };