@nyris/nyris-webapp 0.3.41 → 0.3.43

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 (72) hide show
  1. package/build/_redirects +1 -0
  2. package/build/asset-manifest.json +16 -12
  3. package/build/index.html +1 -1
  4. package/build/{precache-manifest.d9bae9c81b3a390a89a437be45e3a94b.js → precache-manifest.fdacc61375beca4170228eb2951d87b0.js} +27 -11
  5. package/build/service-worker.js +1 -1
  6. package/build/static/css/main.1b40c5ff.chunk.css +2 -0
  7. package/build/static/css/main.1b40c5ff.chunk.css.map +1 -0
  8. package/build/static/js/2.82ef1cd4.chunk.js +3 -0
  9. package/build/static/js/{2.1e5f374f.chunk.js.LICENSE.txt → 2.82ef1cd4.chunk.js.LICENSE.txt} +2 -0
  10. package/build/static/js/2.82ef1cd4.chunk.js.map +1 -0
  11. package/build/static/js/main.7cdac2fb.chunk.js +3 -0
  12. package/build/static/js/main.7cdac2fb.chunk.js.map +1 -0
  13. package/build/static/media/avatar.4c5346ed.svg +3 -0
  14. package/build/static/media/logout.07b9ef7f.svg +3 -0
  15. package/build/static/media/powered_by_nyris.e6766baf.svg +3 -0
  16. package/build/static/media/powered_by_nyris_colored.08d00bae.svg +9 -0
  17. package/package.json +4 -3
  18. package/public/_redirects +1 -0
  19. package/src/App.tsx +0 -17
  20. package/src/Router.tsx +13 -36
  21. package/src/Store/Store.ts +2 -4
  22. package/src/Store/constants.ts +7 -0
  23. package/src/Store/search/Search.ts +10 -3
  24. package/src/Store/search/types.ts +1 -0
  25. package/src/common/assets/icons/avatar.svg +3 -0
  26. package/src/common/assets/icons/logout.svg +3 -0
  27. package/src/common/assets/images/powered_by_nyris.svg +3 -0
  28. package/src/common/assets/images/powered_by_nyris_colored.svg +9 -0
  29. package/src/components/AppMobile.tsx +1 -0
  30. package/src/components/AuthenticatedRoute.tsx +38 -0
  31. package/src/components/CadenasWebViewer.tsx +5 -2
  32. package/src/components/DragDropFile.tsx +3 -4
  33. package/src/components/FooterMobile.tsx +137 -24
  34. package/src/components/Header.tsx +120 -1
  35. package/src/components/HeaderMobile.tsx +230 -163
  36. package/src/components/ImagePreviewMobile.tsx +57 -8
  37. package/src/components/Layout.tsx +16 -56
  38. package/src/components/NoAccess.tsx +66 -0
  39. package/src/components/PoweredByNyris.tsx +49 -0
  40. package/src/components/ProductDetailView.tsx +17 -7
  41. package/src/components/ProductList/index.tsx +16 -93
  42. package/src/components/ProductList/useProductList.ts +114 -0
  43. package/src/components/Provider/AuthProvider.tsx +25 -0
  44. package/src/components/Provider/InstantSearchProvider.tsx +66 -0
  45. package/src/components/appMobile.scss +2 -2
  46. package/src/components/carousel/ImagePreviewCarousel.tsx +6 -2
  47. package/src/components/common.scss +8 -1
  48. package/src/components/drawer/cameraCustom.tsx +3 -3
  49. package/src/components/input/inputSearch.tsx +86 -17
  50. package/src/components/pre-filter/index.tsx +58 -7
  51. package/src/components/results/ItemResult.tsx +6 -1
  52. package/src/components/rfq/RfqBanner.tsx +0 -1
  53. package/src/index.tsx +9 -6
  54. package/src/page/Login.tsx +21 -0
  55. package/src/page/Logout.tsx +23 -0
  56. package/src/page/landingPage/common.scss +4 -1
  57. package/src/page/result/index.tsx +154 -131
  58. package/src/services/Feedback.ts +1 -1
  59. package/src/services/image.ts +8 -5
  60. package/src/types.ts +35 -25
  61. package/build/static/css/main.86d40309.chunk.css +0 -2
  62. package/build/static/css/main.86d40309.chunk.css.map +0 -1
  63. package/build/static/js/2.1e5f374f.chunk.js +0 -3
  64. package/build/static/js/2.1e5f374f.chunk.js.map +0 -1
  65. package/build/static/js/main.909efae2.chunk.js +0 -3
  66. package/build/static/js/main.909efae2.chunk.js.map +0 -1
  67. package/src/Store/auth/Auth.ts +0 -33
  68. package/src/Store/auth/types.ts +0 -11
  69. package/src/Store/nyris/Nyris.ts +0 -67
  70. package/src/Store/nyris/types.ts +0 -11
  71. package/src/components/Feedback.tsx +0 -91
  72. /package/build/static/js/{main.909efae2.chunk.js.LICENSE.txt → main.7cdac2fb.chunk.js.LICENSE.txt} +0 -0
@@ -1,18 +1,11 @@
1
1
  import { Box } from '@material-ui/core';
2
- import { MultipleQueriesQuery } from '@algolia/client-search';
3
- import algoliasearch from 'algoliasearch/lite';
4
2
  import { ReactNode } from 'components/common';
5
- import React, { memo, useEffect, useMemo, useState } from 'react';
6
- import { InstantSearch } from 'react-instantsearch-dom';
3
+ import React, { memo, useEffect, useState } from 'react';
7
4
  import { useMediaQuery } from 'react-responsive';
8
5
  import { useHistory } from 'react-router-dom';
9
- import {
10
- changeValueTextSearch,
11
- onResetRequestImage,
12
- setUpdateSession,
13
- } from 'Store/search/Search';
6
+ import { onResetRequestImage, setUpdateSession } from 'Store/search/Search';
14
7
  import { useAppDispatch, useAppSelector } from 'Store/Store';
15
- import { AlgoliaSettings, AppState } from '../types';
8
+ import { AppState } from '../types';
16
9
  import './appMobile.scss';
17
10
  import './common.scss';
18
11
  import FooterMobile from './FooterMobile';
@@ -26,11 +19,13 @@ import Loading from './Loading';
26
19
  import i18n from 'i18next';
27
20
  import { initReactI18next } from 'react-i18next';
28
21
  import { translations } from 'translations';
22
+ import { useAuth0 } from '@auth0/auth0-react';
23
+ import InstantSearchProvider from './Provider/InstantSearchProvider';
24
+ import PoweredByNyris from './PoweredByNyris';
29
25
 
30
26
  declare var psol: any;
31
27
 
32
28
  jQuery(document).ready(function () {
33
- psol.core.setApiKey('66c56a38010f4a81a82f6ed51c903399');
34
29
  psol.core.setUserInfo({
35
30
  server_type: 'oem_apps_cadenas_webcomponentsdemo',
36
31
  title: 'Herr',
@@ -60,8 +55,7 @@ i18n.use(initReactI18next).init({
60
55
  function Layout({ children }: ReactNode): JSX.Element {
61
56
  const dispatch = useAppDispatch();
62
57
  const { settings, search } = useAppSelector<AppState>((state: any) => state);
63
- const { valueTextSearch, loadingSearchAlgolia } = search;
64
- const { apiKey, appId, indexName } = settings.algolia as AlgoliaSettings;
58
+ const { loadingSearchAlgolia } = search;
65
59
  const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
66
60
  const [isOpenFilter, setOpenFilter] = useState<boolean>(false);
67
61
  const history = useHistory();
@@ -69,7 +63,9 @@ function Layout({ children }: ReactNode): JSX.Element {
69
63
  (isMobile && history.location?.pathname === '/result') ||
70
64
  history.location?.pathname === '/';
71
65
  const language = useAppSelector(state => state.settings.language);
72
-
66
+ const { isAuthenticated } = useAuth0();
67
+ const { auth0, showPoweredByNyris } = settings;
68
+ const showApp = !auth0.enabled || (auth0.enabled && isAuthenticated);
73
69
  i18n.changeLanguage(language);
74
70
 
75
71
  useEffect(() => {
@@ -101,35 +97,6 @@ function Layout({ children }: ReactNode): JSX.Element {
101
97
  HeaderApp = Header;
102
98
  }
103
99
 
104
- const conditionalQuery = useMemo(() => {
105
- const searchClient = algoliasearch(appId, apiKey);
106
- searchClient.initIndex(indexName);
107
- return {
108
- ...searchClient,
109
- search(requests: MultipleQueriesQuery[]) {
110
- if (
111
- requests.every(
112
- (request: MultipleQueriesQuery) =>
113
- !request.params?.query &&
114
- (!request.params?.filters ||
115
- request.params?.filters.endsWith('<score=1>')),
116
- )
117
- ) {
118
- // Here we have to do something else
119
- return Promise.resolve({
120
- results: requests.map(() => ({
121
- hits: [],
122
- nbHits: 0,
123
- nbPages: 0,
124
- processingTimeMS: 0,
125
- })),
126
- });
127
- }
128
- return searchClient.search(requests);
129
- },
130
- };
131
- }, [apiKey, appId, indexName]);
132
-
133
100
  // First we get the viewport height and we multiple it by 1% to get a value for a vh unit
134
101
  let vh = window.innerHeight * 0.01;
135
102
  // Then we set the value in the --vh custom property to the root of the document
@@ -155,18 +122,9 @@ function Layout({ children }: ReactNode): JSX.Element {
155
122
  <Loading />
156
123
  </Box>
157
124
  )}
158
- <InstantSearch
159
- indexName={indexName}
160
- searchClient={conditionalQuery}
161
- searchState={valueTextSearch}
162
- onSearchStateChange={state => {
163
- if (state.page && state.query !== undefined) {
164
- dispatch(changeValueTextSearch(state));
165
- }
166
- }}
167
- >
168
- {isMobile && <AppMobile>{children}</AppMobile>}
169
- {!isMobile && (
125
+ <InstantSearchProvider>
126
+ {isMobile && showApp && <AppMobile>{children}</AppMobile>}
127
+ {!isMobile && showApp && (
170
128
  <div className={`layout-main-${classNameBoxVersion}`}>
171
129
  <div
172
130
  className={
@@ -197,9 +155,11 @@ function Layout({ children }: ReactNode): JSX.Element {
197
155
  <FooterApp />
198
156
  </div>
199
157
  )}
158
+ {showPoweredByNyris && <PoweredByNyris />}
200
159
  </div>
201
160
  )}
202
- </InstantSearch>
161
+ {!showApp && <> {children}</>}
162
+ </InstantSearchProvider>
203
163
  </div>
204
164
  );
205
165
  }
@@ -0,0 +1,66 @@
1
+ import { useAppSelector } from 'Store/Store';
2
+ import React from 'react';
3
+
4
+ import { useMediaQuery } from 'react-responsive';
5
+
6
+ function NoAccess() {
7
+ const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
8
+ const { supportEmail } = useAppSelector(state => state.settings.auth0);
9
+
10
+ return (
11
+ <div
12
+ style={{
13
+ backgroundColor: 'white',
14
+ height: '100%',
15
+ paddingLeft: isMobile ? '32px' : '64px',
16
+ paddingTop: isMobile ? '24px' : '32px',
17
+ display: 'flex',
18
+ flexDirection: 'column',
19
+ gap: '16px',
20
+ }}
21
+ >
22
+ <div
23
+ style={{
24
+ fontSize: '20px',
25
+ fontWeight: 'bold',
26
+ color: '#2B2C46',
27
+ }}
28
+ >
29
+ Email verification is required
30
+ </div>
31
+
32
+ <div
33
+ style={{
34
+ width: '200px',
35
+ height: '4px',
36
+ background: '#3E36DC',
37
+ borderRadius: '4px',
38
+ }}
39
+ />
40
+
41
+ <p style={{ color: '#2B2C46', fontSize: '13px' }}>
42
+ Please verify your email for access to the Search Suite. If you haven't
43
+ received your verification email yet, contact support.
44
+ </p>
45
+ <a
46
+ className="contact-support"
47
+ style={{
48
+ backgroundColor: '#2B2C46',
49
+ padding: '8px 16px 8px 16px',
50
+ color: '#fff',
51
+ border: 'none',
52
+ cursor: 'pointer',
53
+ fontSize: '14px',
54
+ width: 'fit-content',
55
+ }}
56
+ href={`mailto:${
57
+ supportEmail || 'support@nyris.io'
58
+ }?subject=Resend Email Verification&body=`}
59
+ >
60
+ Contact Support
61
+ </a>
62
+ </div>
63
+ );
64
+ }
65
+
66
+ export default NoAccess;
@@ -0,0 +1,49 @@
1
+ import React, { useState } from 'react';
2
+ import { ReactComponent as PoweredByNyrisImage } from 'common/assets/images/powered_by_nyris.svg';
3
+ import { ReactComponent as PoweredByNyrisImageColored } from 'common/assets/images/powered_by_nyris_colored.svg';
4
+
5
+ function PoweredByNyris() {
6
+ const [isHovered, setHovered] = useState(false);
7
+
8
+ const handleMouseOver = () => {
9
+ setHovered(true);
10
+ };
11
+
12
+ const handleMouseOut = () => {
13
+ setHovered(false);
14
+ };
15
+
16
+ return (
17
+ <div
18
+ className="powered-by-nyris"
19
+ style={{
20
+ display: 'flex',
21
+ padding: '9px 0px',
22
+ justifyContent: 'center',
23
+ borderTop: '1px solid #E0E0E0',
24
+ }}
25
+ onMouseOver={handleMouseOver}
26
+ onMouseOut={handleMouseOut}
27
+ >
28
+ {isHovered && (
29
+ <PoweredByNyrisImageColored
30
+ style={{ cursor: 'pointer' }}
31
+ onClick={() => {
32
+ window.open('https://www.nyris.io', '_blank');
33
+ }}
34
+ />
35
+ )}
36
+ {!isHovered && (
37
+ <PoweredByNyrisImage
38
+ style={{ cursor: 'pointer' }}
39
+ onClick={() => {
40
+ window.open('https://www.nyris.io', '_blank');
41
+ }}
42
+ color="#2B2C46"
43
+ />
44
+ )}
45
+ </div>
46
+ );
47
+ }
48
+
49
+ export default PoweredByNyris;
@@ -20,6 +20,7 @@ import { useTranslation } from 'react-i18next';
20
20
  import ProductAttribute from './ProductAttribute';
21
21
  import CadenasWebViewer from './CadenasWebViewer';
22
22
  import { makeStyles } from '@material-ui/core/styles';
23
+ import { get } from 'lodash';
23
24
 
24
25
  const useStyles = makeStyles(theme => ({
25
26
  buttonStyle3D: {
@@ -58,7 +59,7 @@ function ProductDetailView(props: Props) {
58
59
  const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
59
60
  const { settings } = useAppSelector<AppState>((state: any) => state);
60
61
  const brand = dataItem[settings.field.productTag];
61
- const ctaLink = dataItem[settings.field?.ctaLinkField];
62
+
62
63
  const [collapDescription, setCollapDescription] = useState(false);
63
64
  const [feedback, setFeedback] = useState('none');
64
65
  const [is3dView, setIs3dView] = useState(show3dView);
@@ -70,6 +71,11 @@ function ProductDetailView(props: Props) {
70
71
  const { t } = useTranslation();
71
72
  const classes = useStyles(props?.show3dView);
72
73
 
74
+ const ctaLink = get(
75
+ dataItem,
76
+ settings.field?.ctaLinkField ? settings.field?.ctaLinkField : 'links.main',
77
+ );
78
+
73
79
  useEffect(() => {
74
80
  if (dataItem) {
75
81
  checkDataItemResult(dataItem);
@@ -155,11 +161,16 @@ function ProductDetailView(props: Props) {
155
161
  height: is3dView ? '0px' : !isMobile ? '60%' : '368px',
156
162
  opacity: is3dView ? 0 : 1,
157
163
  transition: !is3dView ? 'opacity 3s ease' : '',
158
- paddingTop: '16px',
164
+ paddingTop: !is3dView ? '16px' : '0px',
159
165
  }}
160
166
  >
161
167
  {dataImageCarousel.length > 0 && (
162
- <ImagePreviewCarousel imgItem={dataImageCarousel} />
168
+ <ImagePreviewCarousel
169
+ imgItem={dataImageCarousel}
170
+ setSelectedImage={url => {
171
+ setUrlImage(url ? url : urlImage);
172
+ }}
173
+ />
163
174
  )}
164
175
  {dataImageCarousel.length > 0 && (
165
176
  <Button
@@ -175,6 +186,7 @@ function ProductDetailView(props: Props) {
175
186
  justifyContent: 'center',
176
187
  alignItems: 'center',
177
188
  cursor: 'pointer',
189
+ bottom: isMobile ? '25px' : '4px',
178
190
  }}
179
191
  onClick={() => {
180
192
  if (urlImage.length > 1) {
@@ -212,6 +224,7 @@ function ProductDetailView(props: Props) {
212
224
  style={{
213
225
  position: 'absolute',
214
226
  left: '16px',
227
+ bottom: isMobile ? '25px' : '10px',
215
228
  }}
216
229
  >
217
230
  {!is3dView &&
@@ -423,10 +436,7 @@ function ProductDetailView(props: Props) {
423
436
  }}
424
437
  onClick={() => {
425
438
  if (ctaLink) {
426
- window.open(
427
- `${dataItem[settings.field.ctaLinkField]}`,
428
- '_blank',
429
- );
439
+ window.open(`${ctaLink}`, '_blank');
430
440
  }
431
441
  }}
432
442
  >
@@ -1,12 +1,12 @@
1
1
  import { Box } from '@material-ui/core';
2
2
  import ItemResult from 'components/results/ItemResult';
3
- import { groupBy, uniqueId } from 'lodash';
4
- import React, { memo, useEffect, useMemo, useState } from 'react';
3
+ import React, { memo, useMemo } from 'react';
5
4
  import { useTranslation } from 'react-i18next';
6
5
  import { connectStateResults } from 'react-instantsearch-dom';
7
6
  import { useMediaQuery } from 'react-responsive';
8
7
  import { useAppSelector } from 'Store/Store';
9
8
  import { AppState } from 'types';
9
+ import { useProductList } from './useProductList';
10
10
 
11
11
  interface Props {
12
12
  allSearchResults: any;
@@ -29,106 +29,29 @@ function ProductListComponent({
29
29
  }: any): JSX.Element {
30
30
  const { search, settings } = useAppSelector<AppState>((state: any) => state);
31
31
  const { loadingSearchAlgolia } = search;
32
- const [hitGroups, setHitGroups] = useState<any>({});
33
- const [itemShowDefault, setItemShowDefault] = useState<any[]>([]);
34
- const [algoliaRequest, setAlgoliaRequest] = useState(false);
35
32
  const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
36
33
  const { t } = useTranslation();
37
-
38
- useEffect(() => {
39
- if (isSearchStalled) {
40
- setAlgoliaRequest(true);
41
- }
42
- }, [isSearchStalled]);
43
-
44
- useEffect(() => {
45
- if (!allSearchResults?.hits?.length) {
46
- setItemShowDefault([]);
47
- return;
48
- }
49
- setAlgoliaRequest(false);
50
- const listHistDefaultGroups = settings.showGroup
51
- ? setListHitDefault(allSearchResults?.hits)
52
- : allSearchResults?.hits;
53
- setItemShowDefault(listHistDefaultGroups);
54
- // eslint-disable-next-line react-hooks/exhaustive-deps
55
- }, [allSearchResults?.hits, search?.valueTextSearch]);
56
-
57
- const setListHitDefault = (hits: any) => {
58
- let newArrayShowGroup: any = [];
59
- let newArrayShowItem: any = [];
60
-
61
- const groupHits = hits.map((hit: { group_id: string }) => {
62
- if (!hit.group_id) {
63
- return { ...hit, group_id: uniqueId('random-group-id') };
64
- }
65
- return hit;
34
+ const { productList, handlerCloseGroup, handlerGroupItem, algoliaRequest } =
35
+ useProductList({
36
+ allSearchResults,
66
37
  });
67
38
 
68
- const groups = groupBy(groupHits, 'group_id');
69
- setHitGroups(groups);
70
- newArrayShowGroup = Object.values(groups);
71
- if (newArrayShowGroup.length === 0) {
72
- return hits;
73
- }
74
- newArrayShowGroup.forEach((item: any) => {
75
- let payload: any;
76
- if (item.length >= 2) {
77
- payload = {
78
- ...item[0],
79
- isGroup: true,
80
- collap: true,
81
- };
82
- newArrayShowItem.push(payload);
83
- } else {
84
- payload = {
85
- ...item[0],
86
- isGroup: false,
87
- collap: null,
88
- };
89
- newArrayShowItem.push(payload);
90
- }
91
- });
92
-
93
- return newArrayShowItem;
94
- };
95
-
96
- const handlerGroupItem = (hit: any, index: number) => {
97
- const group_id = hit.group_id;
98
- let newItemList = [...itemShowDefault];
99
- const firstArr = newItemList.slice(0, index + 1);
100
- firstArr.filter(item => item.group_id === group_id)[0].collap = false;
101
- let secondArr = newItemList.slice(index + 1, newItemList.length);
102
- let otherItemsInGroup = [...hitGroups[group_id]];
103
- otherItemsInGroup.shift();
104
- secondArr = otherItemsInGroup.concat(secondArr);
105
- setItemShowDefault(firstArr.concat(secondArr));
106
- };
107
- const handlerCloseGroup = (hit: any, index: number) => {
108
- const group_id = hit.group_id;
109
- let newItemList = [...itemShowDefault];
110
- const firstArr = newItemList.slice(0, index + 1);
111
- firstArr.filter(item => item.group_id === group_id)[0].collap = true;
112
- let secondArr = newItemList.slice(index + 1, newItemList.length);
113
- secondArr = secondArr.filter(item => {
114
- return item.group_id !== group_id;
115
- });
116
- setItemShowDefault(firstArr.concat(secondArr));
117
- };
118
-
119
39
  const renderItem = useMemo(() => {
120
- if (!requestImage && !search.valueTextSearch.query && !isSearchStalled) {
40
+ if (
41
+ !requestImage &&
42
+ !search.valueTextSearch.query &&
43
+ !searchQuery &&
44
+ !isSearchStalled
45
+ ) {
121
46
  return (
122
47
  <Box style={{ marginTop: '50px', width: '100%', textAlign: 'center' }}>
123
48
  {t('Please upload an image or enter a keyword to search.')}
124
49
  </Box>
125
50
  );
126
- }
127
- if (
128
- itemShowDefault.length === 0 &&
51
+ } else if (
52
+ productList.length === 0 &&
129
53
  !loadingSearchAlgolia &&
130
- !isSearchStalled &&
131
- (algoliaRequest || requestImage)
54
+ !isSearchStalled
132
55
  ) {
133
56
  return (
134
57
  <Box style={{ marginTop: '50px', width: '100%', textAlign: 'center' }}>
@@ -136,7 +59,7 @@ function ProductListComponent({
136
59
  </Box>
137
60
  );
138
61
  }
139
- return itemShowDefault.map((hit: any, i: number) => {
62
+ return productList.map((hit: any, i: number) => {
140
63
  return (
141
64
  <Box key={i} style={{ height: 'fit-content' }}>
142
65
  <ItemResult
@@ -166,7 +89,7 @@ function ProductListComponent({
166
89
  });
167
90
  // eslint-disable-next-line react-hooks/exhaustive-deps
168
91
  }, [
169
- itemShowDefault,
92
+ productList,
170
93
  searchQuery,
171
94
  requestImage,
172
95
  search.valueTextSearch,
@@ -0,0 +1,114 @@
1
+ import { useAppSelector } from 'Store/Store';
2
+ import { groupBy, uniqueId } from 'lodash';
3
+ import { useEffect, useMemo, useState } from 'react';
4
+
5
+ export const useProductList = ({ allSearchResults, isSearchStalled }: any) => {
6
+ const { search, settings } = useAppSelector(state => state);
7
+ const { valueTextSearch, results } = search || {};
8
+ const { showGroup, algolia } = settings || {};
9
+ const [itemShowDefault, setItemShowDefault] = useState<any[]>([]);
10
+ const [algoliaRequest, setAlgoliaRequest] = useState(false);
11
+ const [hitGroups, setHitGroups] = useState<any>({});
12
+
13
+ const setListHitDefault = (hits: any) => {
14
+ let newArrayShowGroup: any = [];
15
+ let newArrayShowItem: any = [];
16
+
17
+ const groupHits = hits.map((hit: { group_id: string }) => {
18
+ if (!hit.group_id) {
19
+ return { ...hit, group_id: uniqueId('random-group-id') };
20
+ }
21
+ return hit;
22
+ });
23
+
24
+ const groups = groupBy(groupHits, 'group_id');
25
+ setHitGroups(groups);
26
+ newArrayShowGroup = Object.values(groups);
27
+ if (newArrayShowGroup?.length === 0) {
28
+ return hits;
29
+ }
30
+ newArrayShowGroup.forEach((item: any) => {
31
+ let payload: any;
32
+ if (item?.length >= 2) {
33
+ payload = {
34
+ ...item[0],
35
+ isGroup: true,
36
+ collap: true,
37
+ };
38
+ newArrayShowItem.push(payload);
39
+ } else {
40
+ payload = {
41
+ ...item[0],
42
+ isGroup: false,
43
+ collap: null,
44
+ };
45
+ newArrayShowItem.push(payload);
46
+ }
47
+ });
48
+
49
+ return newArrayShowItem;
50
+ };
51
+
52
+ const handlerGroupItem = (hit: any, index: number) => {
53
+ const group_id = hit.group_id;
54
+ let newItemList = [...itemShowDefault];
55
+ const firstArr = newItemList.slice(0, index + 1);
56
+ firstArr.filter(item => item.group_id === group_id)[0].collap = false;
57
+ let secondArr = newItemList.slice(index + 1, newItemList.length);
58
+ let otherItemsInGroup = [...hitGroups[group_id]];
59
+ otherItemsInGroup.shift();
60
+ secondArr = otherItemsInGroup.concat(secondArr);
61
+ setItemShowDefault(firstArr.concat(secondArr));
62
+ };
63
+ const handlerCloseGroup = (hit: any, index: number) => {
64
+ const group_id = hit.group_id;
65
+ let newItemList = [...itemShowDefault];
66
+ const firstArr = newItemList.slice(0, index + 1);
67
+ firstArr.filter(item => item.group_id === group_id)[0].collap = true;
68
+ let secondArr = newItemList.slice(index + 1, newItemList.length);
69
+ secondArr = secondArr.filter(item => {
70
+ return item.group_id !== group_id;
71
+ });
72
+ setItemShowDefault(firstArr.concat(secondArr));
73
+ };
74
+
75
+ useEffect(() => {
76
+ if (!allSearchResults?.hits?.length) {
77
+ setItemShowDefault([]);
78
+ return;
79
+ }
80
+ setAlgoliaRequest(false);
81
+ const listHistDefaultGroups = showGroup
82
+ ? setListHitDefault(allSearchResults?.hits)
83
+ : allSearchResults?.hits;
84
+ setItemShowDefault(listHistDefaultGroups);
85
+ // eslint-disable-next-line react-hooks/exhaustive-deps
86
+ }, [allSearchResults?.hits, valueTextSearch]);
87
+
88
+ useEffect(() => {
89
+ if (isSearchStalled) {
90
+ setAlgoliaRequest(true);
91
+ }
92
+ }, [isSearchStalled]);
93
+
94
+ const productList = useMemo(() => {
95
+ return results?.map((item: any) => {
96
+ return {
97
+ ...item,
98
+ main_image_link: item.image || item.images ? item.images[0] : '',
99
+ };
100
+ });
101
+ }, [results]);
102
+
103
+ return {
104
+ productList: algolia?.enabled ? itemShowDefault : productList || [],
105
+ handlerGroupItem,
106
+ handlerCloseGroup,
107
+ algoliaRequest,
108
+ isLoading: false,
109
+ hasError: false,
110
+ errorMessage: '',
111
+ loadProductList: () => {},
112
+ reset: () => {},
113
+ };
114
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { Auth0Provider } from '@auth0/auth0-react';
3
+ import { useAppSelector } from 'Store/Store';
4
+
5
+ const AuthProvider = ({ children }: any) => {
6
+ const settings = useAppSelector(state => state.settings);
7
+
8
+ if (!settings.auth0.enabled) {
9
+ return <>{children}</>;
10
+ }
11
+
12
+ return (
13
+ <Auth0Provider
14
+ domain={settings.auth0.domain || ''}
15
+ clientId={settings.auth0.clientId || ''}
16
+ authorizationParams={{
17
+ redirect_uri: window.location.origin,
18
+ }}
19
+ >
20
+ {children}
21
+ </Auth0Provider>
22
+ );
23
+ };
24
+
25
+ export default AuthProvider;
@@ -0,0 +1,66 @@
1
+ import React, { memo, useMemo } from 'react';
2
+ import { MultipleQueriesQuery } from '@algolia/client-search';
3
+ import algoliasearch from 'algoliasearch/lite';
4
+ import { ReactNode } from 'components/common';
5
+ import { InstantSearch } from 'react-instantsearch-dom';
6
+ import { changeValueTextSearch } from 'Store/search/Search';
7
+ import { useAppDispatch, useAppSelector } from 'Store/Store';
8
+ import { AlgoliaSettings } from 'types';
9
+
10
+ function InstantSearchProvider({ children }: ReactNode): JSX.Element {
11
+ const dispatch = useAppDispatch();
12
+ const { settings, search } = useAppSelector(state => state);
13
+ const { valueTextSearch } = search;
14
+ const { apiKey, appId, indexName } = settings.algolia as AlgoliaSettings;
15
+ const isAlgoliaEnabled = settings.algolia?.enabled;
16
+
17
+ const conditionalQuery = useMemo(() => {
18
+ const searchClient = algoliasearch(appId, apiKey);
19
+ searchClient.initIndex(indexName);
20
+ return {
21
+ ...searchClient,
22
+ search(requests: MultipleQueriesQuery[]) {
23
+ if (
24
+ requests.every(
25
+ (request: MultipleQueriesQuery) =>
26
+ !request.params?.query &&
27
+ (!request.params?.filters ||
28
+ request.params?.filters.endsWith('<score=1>')),
29
+ )
30
+ ) {
31
+ // Here we have to do something else
32
+ return Promise.resolve({
33
+ results: requests.map(() => ({
34
+ hits: [],
35
+ nbHits: 0,
36
+ nbPages: 0,
37
+ processingTimeMS: 0,
38
+ })),
39
+ });
40
+ }
41
+ if (isAlgoliaEnabled) {
42
+ return searchClient.search(requests);
43
+ }
44
+ },
45
+ };
46
+ }, [apiKey, appId, indexName, isAlgoliaEnabled]);
47
+
48
+ return (
49
+ <div style={{ position: 'relative' }}>
50
+ <InstantSearch
51
+ indexName={indexName}
52
+ searchClient={conditionalQuery}
53
+ searchState={isAlgoliaEnabled ? valueTextSearch : {}}
54
+ onSearchStateChange={state => {
55
+ if (state.page && state.query !== undefined && isAlgoliaEnabled) {
56
+ dispatch(changeValueTextSearch(state));
57
+ }
58
+ }}
59
+ >
60
+ {children}
61
+ </InstantSearch>
62
+ </div>
63
+ );
64
+ }
65
+
66
+ export default memo(InstantSearchProvider);
@@ -41,7 +41,7 @@
41
41
 
42
42
  background: #FFFFFF;
43
43
  border-bottom: 1px solid #E9E9EC;
44
- box-shadow: 0px 0px 16px rgba(170, 171, 181, 0.5);
44
+ box-shadow: 0px 0px 16px 0px rgba(170, 171, 181, 0.50);
45
45
  border-radius: 32px;
46
46
 
47
47
  width: 100%;
@@ -308,7 +308,7 @@
308
308
 
309
309
  background: #FFFFFF;
310
310
  border-bottom: 1px solid #E9E9EC;
311
- box-shadow: 0px 0px 16px rgba(170, 171, 181, 0.5);
311
+ box-shadow: 0px 0px 16px 0px rgba(170, 171, 181, 0.50);
312
312
  border-radius: 32px;
313
313
 
314
314
  width: 100%;