@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.
- package/build/asset-manifest.json +16 -12
- package/build/index.html +1 -1
- package/build/js/settings.example.js +84 -13
- package/build/{precache-manifest.694373c4d80fe3bb40d0d6526b473852.js → precache-manifest.87ecf17e376539dad2c663829130bfdc.js} +26 -10
- package/build/service-worker.js +1 -1
- package/build/static/css/main.24b5a712.chunk.css +2 -0
- package/build/static/css/main.24b5a712.chunk.css.map +1 -0
- package/build/static/js/2.f9395632.chunk.js +3 -0
- package/build/static/js/2.f9395632.chunk.js.map +1 -0
- package/build/static/js/main.e2a2eb38.chunk.js +3 -0
- package/build/static/js/main.e2a2eb38.chunk.js.map +1 -0
- package/build/static/media/add.ba46a4bf.svg +4 -0
- package/build/static/media/arrow_left.fd9d4390.svg +3 -0
- package/build/static/media/arrow_right.c6fdab0b.svg +3 -0
- package/build/static/media/minus.3fce6c0a.svg +3 -0
- package/package.json +3 -3
- package/public/js/settings.example.js +84 -13
- package/src/Store/Store.ts +1 -0
- package/src/Store/search/Search.ts +36 -0
- package/src/Store/search/search.initialState.ts +1 -0
- package/src/Store/search/types.ts +1 -0
- package/src/common/assets/icons/add.svg +4 -0
- package/src/common/assets/icons/minus.svg +3 -0
- package/src/components/HeaderMobile.tsx +33 -12
- package/src/components/ImagePreviewMobile.tsx +1 -0
- package/src/components/Inquiry/InquiryBanner.tsx +1 -1
- package/src/components/Layout.tsx +19 -1
- package/src/components/MobilePostFilter.tsx +14 -5
- package/src/components/PanelResult/PostFilter.tsx +314 -0
- package/src/components/PanelResult/{index.tsx → PostFilterAlgolia.tsx} +44 -15
- package/src/components/PanelResult/expandable-panel.tsx +20 -14
- package/src/components/ProductAttribute.tsx +38 -34
- package/src/components/ProductDetailView.tsx +37 -22
- package/src/components/ProductList/index.tsx +0 -3
- package/src/components/ProductList/useProductList.ts +6 -3
- package/src/components/SelectedPostFilter.tsx +103 -0
- package/src/components/SidePanel.tsx +18 -7
- package/src/components/common.scss +4 -0
- package/src/components/current-refinements/getCurrentRefinement.ts +10 -18
- package/src/components/icon-label/icon-label.tsx +23 -18
- package/src/components/input/inputSearch.tsx +2 -2
- package/src/components/pre-filter/index.tsx +16 -10
- package/src/components/results/ItemResult.tsx +33 -22
- package/src/hooks/useFilter.ts +92 -0
- package/src/hooks/useFilteredResult.ts +29 -0
- package/src/index.css +2 -1
- package/src/page/landingPage/AppMD.tsx +1 -5
- package/src/page/landingPage/common.scss +10 -3
- package/src/page/result/index.tsx +37 -29
- package/src/services/image.ts +0 -5
- package/src/translations.ts +9 -0
- package/src/types.ts +1 -5
- package/build/static/css/main.21021ebe.chunk.css +0 -2
- package/build/static/css/main.21021ebe.chunk.css.map +0 -1
- package/build/static/js/2.3e652625.chunk.js +0 -3
- package/build/static/js/2.3e652625.chunk.js.map +0 -1
- package/build/static/js/main.37e28702.chunk.js +0 -3
- package/build/static/js/main.37e28702.chunk.js.map +0 -1
- /package/build/static/js/{2.3e652625.chunk.js.LICENSE.txt → 2.f9395632.chunk.js.LICENSE.txt} +0 -0
- /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
|
|
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
|
-
}, [
|
|
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
|
|
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 '
|
|
9
|
-
import KeyboardArrowLeftOutlinedIcon from '
|
|
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(
|
|
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:
|
|
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
|
|
146
|
-
|
|
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>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CurrentRefinement } from
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
2
|
-
import RemoveIcon from
|
|
3
|
-
import AddIcon from
|
|
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 =
|
|
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 =
|
|
20
|
-
className =
|
|
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
|
|
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
|
-
<
|
|
54
|
+
<p
|
|
49
55
|
style={{
|
|
50
|
-
textTransform: "none",
|
|
51
56
|
fontSize: 12,
|
|
52
|
-
color:
|
|
53
|
-
fontWeight:
|
|
57
|
+
color: '#2B2C46',
|
|
58
|
+
fontWeight: 500,
|
|
54
59
|
}}
|
|
55
60
|
>
|
|
56
61
|
{label}
|
|
57
|
-
</
|
|
62
|
+
</p>
|
|
58
63
|
</div>
|
|
59
64
|
)}
|
|
60
|
-
{tagIcon ===
|
|
61
|
-
<RemoveIcon
|
|
65
|
+
{tagIcon === 'remove' ? (
|
|
66
|
+
<RemoveIcon width={16} height={16} />
|
|
62
67
|
) : (
|
|
63
|
-
<AddIcon
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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=
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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:
|
|
287
|
-
? '#00C070'
|
|
288
|
-
: '#c54545',
|
|
289
|
-
fontWeight: 600,
|
|
283
|
+
color: '#2B2C46',
|
|
290
284
|
}}
|
|
291
285
|
>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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={
|
|
334
|
-
|
|
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={
|
|
349
|
+
title={
|
|
350
|
+
get(dataItem, settings.field.warehouseShelfNumber) ||
|
|
351
|
+
settings.field.warehouseShelfNumber
|
|
352
|
+
}
|
|
343
353
|
value={
|
|
344
|
-
dataItem
|
|
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
|
+
};
|