@nyris/nyris-webapp 0.3.47 → 0.3.48

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 (60) hide show
  1. package/build/asset-manifest.json +16 -12
  2. package/build/index.html +1 -1
  3. package/build/js/settings.example.js +84 -13
  4. package/build/{precache-manifest.694373c4d80fe3bb40d0d6526b473852.js → precache-manifest.87ecf17e376539dad2c663829130bfdc.js} +26 -10
  5. package/build/service-worker.js +1 -1
  6. package/build/static/css/main.24b5a712.chunk.css +2 -0
  7. package/build/static/css/main.24b5a712.chunk.css.map +1 -0
  8. package/build/static/js/2.f9395632.chunk.js +3 -0
  9. package/build/static/js/2.f9395632.chunk.js.map +1 -0
  10. package/build/static/js/main.e2a2eb38.chunk.js +3 -0
  11. package/build/static/js/main.e2a2eb38.chunk.js.map +1 -0
  12. package/build/static/media/add.ba46a4bf.svg +4 -0
  13. package/build/static/media/arrow_left.fd9d4390.svg +3 -0
  14. package/build/static/media/arrow_right.c6fdab0b.svg +3 -0
  15. package/build/static/media/minus.3fce6c0a.svg +3 -0
  16. package/package.json +3 -3
  17. package/public/js/settings.example.js +84 -13
  18. package/src/Store/Store.ts +1 -0
  19. package/src/Store/search/Search.ts +36 -0
  20. package/src/Store/search/search.initialState.ts +1 -0
  21. package/src/Store/search/types.ts +1 -0
  22. package/src/common/assets/icons/add.svg +4 -0
  23. package/src/common/assets/icons/minus.svg +3 -0
  24. package/src/components/HeaderMobile.tsx +33 -12
  25. package/src/components/ImagePreviewMobile.tsx +1 -0
  26. package/src/components/Inquiry/InquiryBanner.tsx +1 -1
  27. package/src/components/Layout.tsx +19 -1
  28. package/src/components/MobilePostFilter.tsx +14 -5
  29. package/src/components/PanelResult/PostFilter.tsx +314 -0
  30. package/src/components/PanelResult/{index.tsx → PostFilterAlgolia.tsx} +44 -15
  31. package/src/components/PanelResult/expandable-panel.tsx +20 -14
  32. package/src/components/ProductAttribute.tsx +38 -34
  33. package/src/components/ProductDetailView.tsx +37 -22
  34. package/src/components/ProductList/index.tsx +0 -3
  35. package/src/components/ProductList/useProductList.ts +6 -3
  36. package/src/components/SelectedPostFilter.tsx +103 -0
  37. package/src/components/SidePanel.tsx +18 -7
  38. package/src/components/common.scss +4 -0
  39. package/src/components/current-refinements/getCurrentRefinement.ts +10 -18
  40. package/src/components/icon-label/icon-label.tsx +23 -18
  41. package/src/components/input/inputSearch.tsx +2 -2
  42. package/src/components/pre-filter/index.tsx +16 -10
  43. package/src/components/results/ItemResult.tsx +33 -22
  44. package/src/hooks/useFilter.ts +92 -0
  45. package/src/hooks/useFilteredResult.ts +29 -0
  46. package/src/index.css +2 -1
  47. package/src/page/landingPage/AppMD.tsx +1 -5
  48. package/src/page/landingPage/common.scss +10 -3
  49. package/src/page/result/index.tsx +37 -29
  50. package/src/services/image.ts +0 -5
  51. package/src/translations.ts +9 -0
  52. package/src/types.ts +1 -5
  53. package/build/static/css/main.21021ebe.chunk.css +0 -2
  54. package/build/static/css/main.21021ebe.chunk.css.map +0 -1
  55. package/build/static/js/2.3e652625.chunk.js +0 -3
  56. package/build/static/js/2.3e652625.chunk.js.map +0 -1
  57. package/build/static/js/main.37e28702.chunk.js +0 -3
  58. package/build/static/js/main.37e28702.chunk.js.map +0 -1
  59. /package/build/static/js/{2.3e652625.chunk.js.LICENSE.txt → 2.f9395632.chunk.js.LICENSE.txt} +0 -0
  60. /package/build/static/js/{main.37e28702.chunk.js.LICENSE.txt → main.e2a2eb38.chunk.js.LICENSE.txt} +0 -0
@@ -13,7 +13,6 @@ interface Props {
13
13
  getUrlToCanvasFile: any;
14
14
  setLoading?: any;
15
15
  sendFeedBackAction: any;
16
- moreInfoText: any;
17
16
  requestImage?: any;
18
17
  searchQuery?: string;
19
18
  }
@@ -22,7 +21,6 @@ function ProductListComponent({
22
21
  allSearchResults,
23
22
  getUrlToCanvasFile,
24
23
  sendFeedBackAction,
25
- moreInfoText,
26
24
  searchQuery,
27
25
  requestImage,
28
26
  isSearchStalled,
@@ -79,7 +77,6 @@ function ProductListComponent({
79
77
  handlerCloseGroup(hitItem, index)
80
78
  }
81
79
  isGroupItem={settings.showGroup ? hit?.isGroup : false}
82
- moreInfoText={moreInfoText}
83
80
  main_image_link={
84
81
  hit['image(main_similarity)'] || hit['main_image_link']
85
82
  }
@@ -1,4 +1,5 @@
1
1
  import { useAppSelector } from 'Store/Store';
2
+ import { useFilteredResult } from 'hooks/useFilteredResult';
2
3
  import { groupBy, uniqueId } from 'lodash';
3
4
  import { useEffect, useMemo, useState } from 'react';
4
5
 
@@ -91,14 +92,16 @@ export const useProductList = ({ allSearchResults, isSearchStalled }: any) => {
91
92
  }
92
93
  }, [isSearchStalled]);
93
94
 
95
+ const filteredResult = useFilteredResult(results);
96
+
94
97
  const productList = useMemo(() => {
95
- return results?.map((item: any) => {
98
+ return filteredResult?.map((item: any) => {
96
99
  return {
97
100
  ...item,
98
- main_image_link: item.image || item.images ? item.images[0] : '',
101
+ main_image_link: item.image || (item.images ? item.images[0] : ''),
99
102
  };
100
103
  });
101
- }, [results]);
104
+ }, [filteredResult]);
102
105
 
103
106
  return {
104
107
  productList: algolia?.enabled ? itemShowDefault : productList || [],
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import classNames from 'classnames';
3
+ import { ReactComponent as IconClose } from 'common/assets/icons/close.svg';
4
+ import { atom } from 'jotai';
5
+ import { useMemo } from 'react';
6
+ import { useAppDispatch, useAppSelector } from 'Store/Store';
7
+ import { useFilter } from 'hooks/useFilter';
8
+ import { get } from 'lodash';
9
+ import { clearPostFilter, setPostFilter } from 'Store/search/Search';
10
+ import { Box } from '@material-ui/core';
11
+
12
+ export type CurrentRefinementsProps = {
13
+ className?: string;
14
+ };
15
+
16
+ export type CurrentRefinement = {
17
+ category?: string;
18
+ label: string;
19
+ };
20
+
21
+ export const refinementCountAtom = atom(0);
22
+
23
+ export function SelectedPostFilter({ className }: CurrentRefinementsProps) {
24
+ const stateGlobal = useAppSelector(state => state);
25
+ const dispatch = useAppDispatch();
26
+ const {
27
+ search: { postFilter, results },
28
+ } = stateGlobal;
29
+ const filter = useFilter(results);
30
+
31
+ const selectedFilters = useMemo(() => {
32
+ const selectedFilters: any[] = [];
33
+ Object.keys(filter).forEach(key => {
34
+ const values = filter[key];
35
+ values.forEach((data: { value: string }) => {
36
+ if (get(postFilter, `${key}.${data.value}`)) {
37
+ selectedFilters.push({ key, ...data });
38
+ }
39
+ });
40
+ });
41
+ return selectedFilters;
42
+ }, [filter, postFilter]);
43
+
44
+ if (!selectedFilters.length) {
45
+ return null;
46
+ }
47
+
48
+ return (
49
+ <Box className="wrap-box-refinements">
50
+ <div style={{ display: 'flex', flexFlow: 'wrap', columnGap: '8px' }}>
51
+ {selectedFilters.map(filter => {
52
+ return (
53
+ <div
54
+ key={filter.value}
55
+ style={{
56
+ display: 'flex',
57
+ alignItems: 'center',
58
+ columnGap: '12px',
59
+ fontSize: '12px',
60
+ padding: '4px 8px 4px 8px',
61
+ backgroundColor: '#E9E9EC',
62
+ borderRadius: '18px',
63
+ width: 'fit-content',
64
+ }}
65
+ >
66
+ <p>
67
+ {filter.value} ({filter.count})
68
+ </p>
69
+ <div
70
+ style={{
71
+ padding: '2px',
72
+ display: 'flex',
73
+ alignItems: 'center',
74
+ justifyContent: 'center',
75
+ cursor: 'pointer',
76
+ }}
77
+ onClick={() => {
78
+ dispatch(
79
+ setPostFilter({
80
+ [filter.key]: filter.value,
81
+ }),
82
+ );
83
+ }}
84
+ >
85
+ <IconClose width={12} height={12} />
86
+ </div>
87
+ </div>
88
+ );
89
+ })}
90
+ <div
91
+ key="clear"
92
+ className={classNames('flex items-center')}
93
+ style={{ padding: '4px', cursor: 'pointer' }}
94
+ onClick={() => dispatch(clearPostFilter())}
95
+ >
96
+ <div className="text-f12" style={{ color: '#E31B5D' }}>
97
+ Clear all
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </Box>
102
+ );
103
+ }
@@ -2,11 +2,14 @@ import { Box, Button, Typography } from '@material-ui/core';
2
2
  import { RectCoords } from '@nyris/nyris-api';
3
3
  import { Preview } from '@nyris/nyris-react-components';
4
4
  import React, { useState } from 'react';
5
- import ExpandablePanelComponent from './PanelResult';
5
+ import PostFilterPanel from './PanelResult/PostFilter';
6
+ import PostFilterPanelAlgolia from './PanelResult/PostFilterAlgolia';
7
+
6
8
  import { useTranslation } from 'react-i18next';
7
9
  import { useAppSelector } from 'Store/Store';
8
- import KeyboardArrowRightOutlinedIcon from '@material-ui/icons/KeyboardArrowRightOutlined';
9
- import KeyboardArrowLeftOutlinedIcon from '@material-ui/icons/KeyboardArrowLeftOutlined';
10
+ import { ReactComponent as KeyboardArrowRightOutlinedIcon } from 'common/assets/icons/arrow_right.svg';
11
+ import { ReactComponent as KeyboardArrowLeftOutlinedIcon } from 'common/assets/icons/arrow_left.svg';
12
+
10
13
  import { DEFAULT_REGION } from '../constants';
11
14
  import { ReactComponent as IconInfo } from 'common/assets/icons/info-tooltip.svg';
12
15
 
@@ -32,7 +35,7 @@ function SidePanel({
32
35
  }) {
33
36
  const { t } = useTranslation();
34
37
  const [toggleColLeft, setToggleColLeft] = useState<boolean>(false);
35
- const stateGlobal = useAppSelector((state: any) => state);
38
+ const stateGlobal = useAppSelector(state => state);
36
39
  const { search, settings } = stateGlobal;
37
40
 
38
41
  const { requestImage } = search;
@@ -49,7 +52,7 @@ function SidePanel({
49
52
  <Box
50
53
  className="box-toggle-coloumn"
51
54
  style={{
52
- right: requestImage || toggleColLeft ? '0px' : '16px',
55
+ right: '0px',
53
56
  }}
54
57
  >
55
58
  <Button
@@ -142,8 +145,16 @@ function SidePanel({
142
145
  )}
143
146
 
144
147
  {showPostFilter && (
145
- <Box className="col-left__bottom">
146
- <ExpandablePanelComponent disjunctiveFacets={disjunctiveFacets} />
148
+ <Box
149
+ className="col-left__bottom"
150
+ style={{
151
+ marginTop: requestImage ? '16px' : '48px',
152
+ }}
153
+ >
154
+ {settings.algolia.enabled && (
155
+ <PostFilterPanelAlgolia disjunctiveFacets={disjunctiveFacets} />
156
+ )}
157
+ {!settings.algolia.enabled && <PostFilterPanel />}
147
158
  </Box>
148
159
  )}
149
160
  </Box>
@@ -269,4 +269,8 @@
269
269
  }
270
270
 
271
271
 
272
+ }
273
+
274
+ .MuiButton-root:hover {
275
+ text-decoration: none ;
272
276
  }
@@ -1,4 +1,4 @@
1
- import type { CurrentRefinement } from "./current-refinements";
1
+ import type { CurrentRefinement } from './current-refinements';
2
2
 
3
3
  function getRefinementConfig(r: any, refinement: any) {
4
4
  const refinementOptions = r.attribute;
@@ -10,7 +10,7 @@ function getRefinementConfig(r: any, refinement: any) {
10
10
 
11
11
  export function getCurrentRefinement(
12
12
  refinement: any,
13
- config: any
13
+ config: any,
14
14
  ): CurrentRefinement[] {
15
15
  let refinementConfig: any;
16
16
  config.forEach((r: any) => {
@@ -18,20 +18,12 @@ export function getCurrentRefinement(
18
18
  refinementConfig = r;
19
19
  }
20
20
  });
21
-
22
- switch (refinementConfig?.type) {
23
- case "size":
24
- case "list": {
25
- return (
26
- refinement?.items?.map((item: any) => ({
27
- category: refinementConfig?.header,
28
- label: item.label,
29
- value: item.value,
30
- })) || []
31
- );
32
- }
33
- default: {
34
- return [];
35
- }
36
- }
21
+
22
+ return (
23
+ refinement?.items?.map((item: any) => ({
24
+ category: refinementConfig?.header,
25
+ label: item.label,
26
+ value: item.value,
27
+ })) || []
28
+ );
37
29
  }
@@ -1,9 +1,8 @@
1
- import React, { useEffect, useState } from "react";
2
- import RemoveIcon from "@material-ui/icons/Remove";
3
- import AddIcon from "@material-ui/icons/Add";
4
- import { Typography } from "@material-ui/core";
1
+ import React, { useEffect, useState } from 'react';
2
+ import { ReactComponent as RemoveIcon } from 'common/assets/icons/minus.svg';
3
+ import { ReactComponent as AddIcon } from 'common/assets/icons/add.svg';
5
4
 
6
- export type LabelPosition = "bottom" | "left" | "right" | "top";
5
+ export type LabelPosition = 'bottom' | 'left' | 'right' | 'top';
7
6
 
8
7
  export type IconLabelProps = {
9
8
  icon?: any;
@@ -16,11 +15,11 @@ export type IconLabelProps = {
16
15
  export default function IconLabel({
17
16
  icon,
18
17
  label,
19
- labelPosition = "bottom",
20
- className = "gap-1",
21
- classNameLabel = "",
18
+ labelPosition = 'bottom',
19
+ className = 'gap-1',
20
+ classNameLabel = '',
22
21
  }: IconLabelProps) {
23
- const [tagIcon, setTagIcon] = useState<string>("");
22
+ const [tagIcon, setTagIcon] = useState<string>('');
24
23
  // let classNamePosition: string;
25
24
  // switch (labelPosition) {
26
25
  // case "top":
@@ -42,25 +41,31 @@ export default function IconLabel({
42
41
  }, [icon]);
43
42
 
44
43
  return (
45
- <div style={{ display: "flex", alignItems: 'center' }}>
44
+ <div
45
+ style={{
46
+ display: 'flex',
47
+ alignItems: 'center',
48
+ gap: '8px',
49
+ paddingRight: '2px',
50
+ }}
51
+ >
46
52
  {label && (
47
53
  <div className={classNameLabel}>
48
- <Typography
54
+ <p
49
55
  style={{
50
- textTransform: "none",
51
56
  fontSize: 12,
52
- color: "#55566B",
53
- fontWeight: 600,
57
+ color: '#2B2C46',
58
+ fontWeight: 500,
54
59
  }}
55
60
  >
56
61
  {label}
57
- </Typography>
62
+ </p>
58
63
  </div>
59
64
  )}
60
- {tagIcon === "remove" ? (
61
- <RemoveIcon style={{ color: "#55566B" }} />
65
+ {tagIcon === 'remove' ? (
66
+ <RemoveIcon width={16} height={16} />
62
67
  ) : (
63
- <AddIcon style={{ color: "#55566B" }} />
68
+ <AddIcon width={16} height={16} />
64
69
  )}
65
70
  </div>
66
71
  );
@@ -212,7 +212,7 @@ const SearchBox = (props: any) => {
212
212
  return (
213
213
  <div className="wrap-input-search-field">
214
214
  <div className="box-input-search d-flex">
215
- <form noValidate action="" role="search">
215
+ <div className="input-wrapper">
216
216
  <Box className="box-inp">
217
217
  <Tooltip
218
218
  title={
@@ -426,7 +426,7 @@ const SearchBox = (props: any) => {
426
426
  </Button>
427
427
  </Box>
428
428
  )}
429
- </form>
429
+ </div>
430
430
  </div>
431
431
  {settings.preFilterOption && (
432
432
  <DefaultModal
@@ -15,6 +15,7 @@ import { Skeleton } from '@material-ui/lab';
15
15
  import { truncateString } from 'helpers/truncateString';
16
16
  import { find } from 'services/image';
17
17
  import { useQuery } from 'hooks/useQuery';
18
+ import { useTranslation } from 'react-i18next';
18
19
 
19
20
  interface Props {
20
21
  handleClose?: any;
@@ -159,6 +160,7 @@ function PreFilterComponent(props: Props) {
159
160
  }
160
161
  handleClose();
161
162
  };
163
+ const { t } = useTranslation();
162
164
 
163
165
  return (
164
166
  <Box
@@ -225,7 +227,7 @@ function PreFilterComponent(props: Props) {
225
227
 
226
228
  <input
227
229
  className="input-search-filter"
228
- placeholder="Search"
230
+ placeholder={t('Search')}
229
231
  onChange={(e: any) => {
230
232
  filterSearchHandler(e.target.value);
231
233
  }}
@@ -285,7 +287,7 @@ function PreFilterComponent(props: Props) {
285
287
  setKeyFilter({});
286
288
  }}
287
289
  >
288
- Clear all
290
+ {t('Clear all')}
289
291
  </Box>
290
292
  </Box>
291
293
  </Box>
@@ -410,17 +412,18 @@ function PreFilterComponent(props: Props) {
410
412
  className="button-left"
411
413
  style={{
412
414
  width: '50%',
413
- height: '64px',
414
- backgroundColor: '#2B2C46',
415
+ backgroundColor: settings.theme.secondaryColor,
415
416
  color: '#fff',
416
417
  borderRadius: 0,
417
418
  justifyContent: 'flex-start',
418
419
  textTransform: 'none',
419
420
  paddingLeft: '16px',
421
+ paddingTop: '16px',
422
+ paddingBottom: '32px',
420
423
  }}
421
424
  onClick={() => handleClose()}
422
425
  >
423
- Cancel
426
+ {t('Cancel')}
424
427
  </Button>
425
428
  <Button
426
429
  className="button-right"
@@ -432,10 +435,12 @@ function PreFilterComponent(props: Props) {
432
435
  justifyContent: 'flex-start',
433
436
  textTransform: 'none',
434
437
  paddingLeft: '16px',
438
+ paddingTop: '16px',
439
+ paddingBottom: '32px',
435
440
  }}
436
441
  onClick={() => onHandlerSubmitData()}
437
442
  >
438
- Apply
443
+ {t('Apply')}
439
444
  </Button>
440
445
  </Box>
441
446
  )}
@@ -456,14 +461,15 @@ function PreFilterComponent(props: Props) {
456
461
  className="button-left"
457
462
  style={{
458
463
  width: '50%',
459
- backgroundColor: '#000000',
464
+ backgroundColor: settings.theme.secondaryColor,
460
465
  color: '#fff',
461
466
  borderRadius: 0,
462
467
  justifyContent: 'flex-start',
468
+ textTransform: 'none',
463
469
  }}
464
470
  onClick={() => handleClose()}
465
471
  >
466
- Cancel
472
+ {t('Cancel')}
467
473
  </Button>
468
474
  <Button
469
475
  className="button-right"
@@ -473,15 +479,15 @@ function PreFilterComponent(props: Props) {
473
479
  color: '#fff',
474
480
  borderRadius: 0,
475
481
  justifyContent: 'flex-start',
482
+ textTransform: 'none',
476
483
  }}
477
484
  onClick={() => onHandlerSubmitData()}
478
485
  >
479
- Apply
486
+ {t('Apply')}
480
487
  </Button>
481
488
  </Box>
482
489
  )}
483
490
  </Box>
484
491
  );
485
492
  }
486
-
487
493
  export default PreFilterComponent;
@@ -22,7 +22,7 @@ import { useMediaQuery } from 'react-responsive';
22
22
  import { feedbackClickEpic, feedbackConversionEpic } from 'services/Feedback';
23
23
  import ProductDetailView from 'components/ProductDetailView';
24
24
  import ProductAttribute from '../ProductAttribute';
25
- import { get } from 'lodash';
25
+ import { get, isUndefined } from 'lodash';
26
26
 
27
27
  interface Props {
28
28
  dataItem: any;
@@ -34,7 +34,6 @@ interface Props {
34
34
  handlerFeedback?: any;
35
35
  handlerGroupItem?: any;
36
36
  isGroupItem?: boolean;
37
- moreInfoText?: string;
38
37
  handlerCloseGroup?: any;
39
38
  main_image_link?: any;
40
39
  }
@@ -274,25 +273,28 @@ function ItemResult(props: Props) {
274
273
  </Typography>
275
274
  </Tooltip>
276
275
 
277
- {settings.warehouseVariant && (
278
- <Typography
279
- className="text-f12 max-line-1 fw-400"
280
- style={{
281
- color: '#2B2C46',
282
- }}
283
- >
284
- <span
276
+ {settings.warehouseVariant &&
277
+ !isUndefined(
278
+ get(dataItem, settings.field.warehouseStockValue),
279
+ ) && (
280
+ <Typography
281
+ className="text-f12 max-line-1 fw-400"
285
282
  style={{
286
- color: dataItem[settings.field.warehouseStockValue]
287
- ? '#00C070'
288
- : '#c54545',
289
- fontWeight: 600,
283
+ color: '#2B2C46',
290
284
  }}
291
285
  >
292
- {dataItem[settings.field.warehouseStockValue] || 0}
293
- </span>
294
- </Typography>
295
- )}
286
+ <span
287
+ style={{
288
+ color: get(dataItem, settings.field.warehouseStockValue)
289
+ ? '#00C070'
290
+ : '#c54545',
291
+ fontWeight: 600,
292
+ }}
293
+ >
294
+ {get(dataItem, settings.field.warehouseStockValue) || 0}
295
+ </span>
296
+ </Typography>
297
+ )}
296
298
  </Box>
297
299
  <Box
298
300
  display="flex"
@@ -330,8 +332,13 @@ function ItemResult(props: Props) {
330
332
  >
331
333
  {settings.field.warehouseNumber && (
332
334
  <ProductAttribute
333
- title={dataItem[settings.field.warehouseNumber]}
334
- value={dataItem[settings.field.warehouseNumberValue] || 'N/A'}
335
+ title={
336
+ get(dataItem, settings.field.warehouseNumber) ||
337
+ settings.field.warehouseNumber
338
+ }
339
+ value={
340
+ get(dataItem, settings.field.warehouseNumberValue) || 'N/A'
341
+ }
335
342
  padding="4px 8px"
336
343
  width={{ xs: '49%' }}
337
344
  />
@@ -339,9 +346,13 @@ function ItemResult(props: Props) {
339
346
 
340
347
  {settings.field.warehouseShelfNumber && (
341
348
  <ProductAttribute
342
- title={dataItem[settings.field.warehouseShelfNumber]}
349
+ title={
350
+ get(dataItem, settings.field.warehouseShelfNumber) ||
351
+ settings.field.warehouseShelfNumber
352
+ }
343
353
  value={
344
- dataItem[settings.field.warehouseShelfNumberValue] || 'N/A'
354
+ get(dataItem, settings.field.warehouseShelfNumberValue) ||
355
+ 'N/A'
345
356
  }
346
357
  padding="4px 8px"
347
358
  width={{ xs: '49%' }}
@@ -0,0 +1,92 @@
1
+ import { useAppSelector } from 'Store/Store';
2
+ import { useMemo } from 'react';
3
+
4
+ // Function to count occurrences and create array of objects
5
+ function countOccurrences(inputArray: any[]) {
6
+ const countMap: any = {};
7
+ inputArray.forEach((value: string | number) => {
8
+ countMap[value] = (countMap[value] || 0) + 1;
9
+ });
10
+
11
+ return Object.entries(countMap).map(([value, count]) => ({
12
+ value,
13
+ count,
14
+ }));
15
+ }
16
+
17
+ const getNextFilters = (data: any[], postFilters: any): any => {
18
+ const nextFilters: any = {};
19
+
20
+ data?.forEach(element => {
21
+ const filters = element.filters || {};
22
+ const keys = Object.keys(filters);
23
+
24
+ for (let i = 0; i < keys.length; i++) {
25
+ const xKey = keys[i];
26
+
27
+ let isNextFilter = keys.every(yKey => {
28
+ if (xKey !== yKey) {
29
+ const postFilter = postFilters[yKey] || {};
30
+ const filter = filters[yKey] || {};
31
+
32
+ const postFilterValues = Object.keys(postFilter).filter(
33
+ key => postFilter[key],
34
+ );
35
+
36
+ if (postFilterValues?.length > 0) {
37
+ return postFilterValues?.some(element => {
38
+ return filter?.includes(element);
39
+ });
40
+ }
41
+ return true;
42
+ }
43
+ return true;
44
+ });
45
+
46
+ if (isNextFilter) {
47
+ if (nextFilters[xKey]) {
48
+ nextFilters[xKey] = [...nextFilters[xKey], ...filters[xKey]];
49
+ } else {
50
+ nextFilters[xKey] = filters[xKey];
51
+ }
52
+ }
53
+ }
54
+ });
55
+
56
+ return nextFilters;
57
+ };
58
+
59
+ export const useFilter = (data: any) => {
60
+ const { postFilter } = useAppSelector(state => state.search);
61
+
62
+ const filters = useMemo(() => {
63
+ return getNextFilters(data, postFilter);
64
+ }, [data, postFilter]);
65
+
66
+ const filterCount = useMemo(() => {
67
+ const resultObject: any = {};
68
+ for (const key in filters) {
69
+ if (filters.hasOwnProperty(key)) {
70
+ resultObject[key] = countOccurrences(filters[key]);
71
+ }
72
+ }
73
+
74
+ return resultObject;
75
+ }, [filters]);
76
+
77
+ // If there is no item for a selected filter add the filter with count 0
78
+ Object.keys(postFilter).forEach(key => {
79
+ const filter = postFilter[key];
80
+ Object.keys(filter).forEach(value => {
81
+ const isNextFilter = filterCount[key].some((data: { value: string }) => {
82
+ return data.value === value;
83
+ });
84
+
85
+ if (!isNextFilter) {
86
+ filterCount[key].push({ value, count: 0 });
87
+ }
88
+ });
89
+ });
90
+
91
+ return filterCount;
92
+ };
@@ -0,0 +1,29 @@
1
+ import { useAppSelector } from 'Store/Store';
2
+ import { useMemo } from 'react';
3
+
4
+ function filterResultsBasedOnPostFilter(results: any, postFilter: any): any[] {
5
+ return results?.filter((result: { filters: { [x: string]: any } }) => {
6
+ return Object.keys(postFilter).every(filterType => {
7
+ const filter = postFilter[filterType];
8
+
9
+ const filterValues = Object.keys(filter).filter(key => filter[key]);
10
+
11
+ if (filterValues.length > 0) {
12
+ const resultFilterValues = result.filters[filterType];
13
+ if (resultFilterValues) {
14
+ return filterValues?.some((value: any) =>
15
+ resultFilterValues.includes(value),
16
+ );
17
+ }
18
+ }
19
+ return true;
20
+ });
21
+ });
22
+ }
23
+
24
+ export const useFilteredResult = (data: any) => {
25
+ const { postFilter } = useAppSelector(state => state.search);
26
+ return useMemo(() => {
27
+ return filterResultsBasedOnPostFilter(data, postFilter || {});
28
+ }, [data, postFilter]);
29
+ };