@nyris/nyris-webapp 0.3.42 → 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 (64) hide show
  1. package/build/asset-manifest.json +16 -12
  2. package/build/index.html +1 -1
  3. package/build/{precache-manifest.009e864ff0764cf3cf8a9b290c334099.js → precache-manifest.fdacc61375beca4170228eb2951d87b0.js} +26 -10
  4. package/build/service-worker.js +1 -1
  5. package/build/static/css/main.1b40c5ff.chunk.css +2 -0
  6. package/build/static/css/main.1b40c5ff.chunk.css.map +1 -0
  7. package/build/static/js/2.82ef1cd4.chunk.js +3 -0
  8. package/build/static/js/2.82ef1cd4.chunk.js.map +1 -0
  9. package/build/static/js/main.7cdac2fb.chunk.js +3 -0
  10. package/build/static/js/main.7cdac2fb.chunk.js.map +1 -0
  11. package/build/static/media/avatar.4c5346ed.svg +3 -0
  12. package/build/static/media/logout.07b9ef7f.svg +3 -0
  13. package/build/static/media/powered_by_nyris.e6766baf.svg +3 -0
  14. package/build/static/media/powered_by_nyris_colored.08d00bae.svg +9 -0
  15. package/package.json +3 -3
  16. package/src/Router.tsx +1 -0
  17. package/src/Store/Store.ts +2 -4
  18. package/src/Store/constants.ts +6 -0
  19. package/src/Store/search/Search.ts +10 -3
  20. package/src/Store/search/types.ts +1 -0
  21. package/src/common/assets/icons/avatar.svg +3 -0
  22. package/src/common/assets/icons/logout.svg +3 -0
  23. package/src/common/assets/images/powered_by_nyris.svg +3 -0
  24. package/src/common/assets/images/powered_by_nyris_colored.svg +9 -0
  25. package/src/components/AppMobile.tsx +1 -0
  26. package/src/components/AuthenticatedRoute.tsx +4 -1
  27. package/src/components/DragDropFile.tsx +3 -4
  28. package/src/components/FooterMobile.tsx +137 -24
  29. package/src/components/Header.tsx +120 -1
  30. package/src/components/HeaderMobile.tsx +230 -163
  31. package/src/components/ImagePreviewMobile.tsx +57 -8
  32. package/src/components/Layout.tsx +10 -53
  33. package/src/components/NoAccess.tsx +66 -0
  34. package/src/components/PoweredByNyris.tsx +49 -0
  35. package/src/components/ProductDetailView.tsx +16 -10
  36. package/src/components/ProductList/index.tsx +16 -93
  37. package/src/components/ProductList/useProductList.ts +114 -0
  38. package/src/components/Provider/InstantSearchProvider.tsx +66 -0
  39. package/src/components/appMobile.scss +2 -2
  40. package/src/components/common.scss +8 -1
  41. package/src/components/drawer/cameraCustom.tsx +3 -3
  42. package/src/components/input/inputSearch.tsx +86 -17
  43. package/src/components/pre-filter/index.tsx +58 -7
  44. package/src/components/results/ItemResult.tsx +6 -1
  45. package/src/index.tsx +1 -1
  46. package/src/page/landingPage/common.scss +4 -1
  47. package/src/page/result/index.tsx +154 -131
  48. package/src/services/Feedback.ts +1 -1
  49. package/src/services/image.ts +8 -5
  50. package/src/types.ts +13 -12
  51. package/build/static/css/main.86d40309.chunk.css +0 -2
  52. package/build/static/css/main.86d40309.chunk.css.map +0 -1
  53. package/build/static/js/2.1757789c.chunk.js +0 -3
  54. package/build/static/js/2.1757789c.chunk.js.map +0 -1
  55. package/build/static/js/main.1d184393.chunk.js +0 -3
  56. package/build/static/js/main.1d184393.chunk.js.map +0 -1
  57. package/src/Store/auth/Auth.ts +0 -33
  58. package/src/Store/auth/types.ts +0 -11
  59. package/src/Store/nyris/Nyris.ts +0 -67
  60. package/src/Store/nyris/types.ts +0 -11
  61. package/src/components/Feedback.tsx +0 -91
  62. /package/build/static/js/{2.1757789c.chunk.js.LICENSE.txt → 2.82ef1cd4.chunk.js.LICENSE.txt} +0 -0
  63. /package/build/static/js/{main.1d184393.chunk.js.LICENSE.txt → main.7cdac2fb.chunk.js.LICENSE.txt} +0 -0
  64. /package/src/components/{AuthProvider.tsx → Provider/AuthProvider.tsx} +0 -0
@@ -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%;
@@ -54,7 +54,6 @@
54
54
  border-bottom: 1px solid #E0E0E0;
55
55
 
56
56
  .box-content {
57
- max-width: 1280px;
58
57
  margin: auto;
59
58
  height: 100%;
60
59
  justify-content: space-between;
@@ -129,3 +128,11 @@
129
128
  background-color: hsla(0, 0%, 0%, 0.3);
130
129
  z-index: 1;
131
130
  }
131
+
132
+ .powered-by-nyris{
133
+ background-color: #fff;
134
+ &:hover {
135
+ background-color:#F0EFFF,
136
+
137
+ }
138
+ }
@@ -9,7 +9,7 @@ import React, { useCallback, useRef, useState } from 'react';
9
9
  import { useDropzone } from 'react-dropzone';
10
10
  import { useHistory } from 'react-router-dom';
11
11
  import Webcam from 'react-webcam';
12
- import { createImage, findByImage, findRegions } from 'services/image';
12
+ import { createImage, find, findRegions } from 'services/image';
13
13
  import {
14
14
  onToggleModalItemDetail,
15
15
  setImageSearchInput,
@@ -82,7 +82,7 @@ function CameraCustom(props: Props) {
82
82
  ];
83
83
  let filters: any[] = [];
84
84
 
85
- findByImage({
85
+ find({
86
86
  image: imageConvert,
87
87
  settings,
88
88
  filters: !isEmpty(preFilter) ? preFilterValues : undefined,
@@ -136,7 +136,7 @@ function CameraCustom(props: Props) {
136
136
  values: Object.keys(preFilter) as string[],
137
137
  },
138
138
  ];
139
- return findByImage({
139
+ return find({
140
140
  image,
141
141
  settings,
142
142
  filters: !isEmpty(preFilter) ? preFilterValues : undefined,
@@ -10,7 +10,7 @@ import { useDropzone } from 'react-dropzone';
10
10
  import { connectSearchBox } from 'react-instantsearch-dom';
11
11
  import { useMediaQuery } from 'react-responsive';
12
12
  import { useHistory } from 'react-router-dom';
13
- import { createImage, findByImage, findRegions } from 'services/image';
13
+ import { createImage, find, findRegions } from 'services/image';
14
14
  import { ReactComponent as IconFilter } from 'common/assets/icons/filter_settings.svg';
15
15
  import { ReactComponent as IconSearch } from 'common/assets/icons/icon_search.svg';
16
16
 
@@ -23,6 +23,7 @@ import {
23
23
  loadingActionResults,
24
24
  setRegions,
25
25
  setSelectedRegion,
26
+ updateQueryText,
26
27
  } from 'Store/search/Search';
27
28
  import { useAppDispatch, useAppSelector } from 'Store/Store';
28
29
  import DefaultModal from 'components/modal/DefaultModal';
@@ -35,7 +36,8 @@ const SearchBox = (props: any) => {
35
36
  // const containerRefInputMobile = useRef<HTMLDivElement>(null);
36
37
  const stateGlobal = useAppSelector(state => state);
37
38
  const { search, settings } = stateGlobal;
38
- const { imageThumbSearchInput, preFilter } = search;
39
+ const { imageThumbSearchInput, preFilter, requestImage, selectedRegion } =
40
+ search;
39
41
  const focusInp: any = useRef<HTMLDivElement | null>(null);
40
42
  const history = useHistory();
41
43
  const [valueInput, setValueInput] = useState<string>('');
@@ -45,6 +47,8 @@ const SearchBox = (props: any) => {
45
47
  const [isOpenModalFilterDesktop, setToggleModalFilterDesktop] =
46
48
  useState<boolean>(false);
47
49
  const { t } = useTranslation();
50
+ const isAlgoliaEnabled = settings.algolia?.enabled;
51
+
48
52
  useEffect(() => {
49
53
  if (focusInp?.current) {
50
54
  focusInp?.current.focus();
@@ -55,25 +59,76 @@ const SearchBox = (props: any) => {
55
59
  const searchQuery = query.get('query') || '';
56
60
  if (!isEmpty(searchQuery)) {
57
61
  setValueInput(searchQuery);
58
- refine(searchQuery);
59
- // not an ideal solution: fixes text search not working from landing page
60
- setTimeout(() => {
62
+ dispatch(updateQueryText(searchQuery));
63
+
64
+ if (isAlgoliaEnabled) {
61
65
  refine(searchQuery);
62
- }, 100);
66
+ // not an ideal solution: fixes text search not working from landing page
67
+ setTimeout(() => {
68
+ refine(searchQuery);
69
+ }, 100);
70
+ }
63
71
  }
64
- }, [query, refine]);
72
+ }, [query, refine, dispatch, isAlgoliaEnabled]);
65
73
 
66
74
  useEffect(() => {
67
75
  if (imageThumbSearchInput) {
68
76
  setValueInput('');
69
- refine('');
77
+ if (isAlgoliaEnabled) {
78
+ refine('');
79
+ }
70
80
  history.push('/result');
71
81
  }
72
82
  // eslint-disable-next-line react-hooks/exhaustive-deps
73
- }, [imageThumbSearchInput]);
83
+ }, [imageThumbSearchInput, isAlgoliaEnabled]);
74
84
 
75
85
  const searchOrRedirect = useCallback(
76
- debounce((value: any) => {
86
+ debounce((value: any, withImage = true) => {
87
+ if (!isAlgoliaEnabled) {
88
+ dispatch(updateQueryText(value));
89
+ let payload: any;
90
+ let filters: any[] = [];
91
+ const preFilterValues = [
92
+ {
93
+ key: settings.visualSearchFilterKey,
94
+ values: Object.keys(preFilter) as string[],
95
+ },
96
+ ];
97
+ if (value || requestImage) {
98
+ dispatch(updateStatusLoading(true));
99
+ find({
100
+ image: withImage
101
+ ? (requestImage?.canvas as HTMLCanvasElement)
102
+ : undefined,
103
+ settings,
104
+ filters: !isEmpty(preFilter) ? preFilterValues : undefined,
105
+ region: withImage ? selectedRegion : undefined,
106
+ text: value,
107
+ })
108
+ .then((res: any) => {
109
+ res?.results.map((item: any) => {
110
+ filters.push({
111
+ sku: item.sku,
112
+ score: item.score,
113
+ });
114
+ });
115
+ payload = {
116
+ ...res,
117
+ filters,
118
+ };
119
+
120
+ dispatch(setSearchResults(payload));
121
+ dispatch(updateStatusLoading(false));
122
+ })
123
+ .catch((e: any) => {
124
+ console.log('error input search', e);
125
+ dispatch(updateStatusLoading(false));
126
+ });
127
+ } else {
128
+ dispatch(setSearchResults([]));
129
+ }
130
+ }
131
+
77
132
  if (value) {
78
133
  history.push({
79
134
  pathname: '/result',
@@ -83,11 +138,12 @@ const SearchBox = (props: any) => {
83
138
  history.push('/result');
84
139
  }
85
140
  }, 500),
86
- [],
141
+ [requestImage, preFilter, selectedRegion, isAlgoliaEnabled],
87
142
  );
88
143
 
89
144
  const { getInputProps } = useDropzone({
90
145
  onDrop: async (fs: File[]) => {
146
+ if (!fs[0]) return;
91
147
  dispatch(updateStatusLoading(true));
92
148
  dispatch(loadingActionResults());
93
149
  if (history.location.pathname !== '/result') {
@@ -114,7 +170,7 @@ const SearchBox = (props: any) => {
114
170
  dispatch(setSelectedRegion(region));
115
171
  }
116
172
 
117
- return findByImage({
173
+ return find({
118
174
  image,
119
175
  settings,
120
176
  filters: !isEmpty(preFilter) ? preFilterValues : undefined,
@@ -143,11 +199,13 @@ const SearchBox = (props: any) => {
143
199
 
144
200
  const onChangeText = (event: any) => {
145
201
  setValueInput(event.currentTarget.value);
146
- // debounceSearch(event.currentTarget.value);
202
+
147
203
  searchOrRedirect(event.currentTarget.value);
148
204
  if (event.currentTarget.value === '') {
149
205
  setValueInput('');
150
- refine('');
206
+ if (isAlgoliaEnabled) {
207
+ refine('');
208
+ }
151
209
  }
152
210
  };
153
211
 
@@ -258,7 +316,11 @@ const SearchBox = (props: any) => {
258
316
  history.push('/');
259
317
  }
260
318
  dispatch(reset(''));
261
- refine(valueInput);
319
+ if (isAlgoliaEnabled) {
320
+ refine(valueInput);
321
+ } else {
322
+ searchOrRedirect(valueInput, false);
323
+ }
262
324
  }}
263
325
  >
264
326
  <CloseIcon
@@ -293,12 +355,19 @@ const SearchBox = (props: any) => {
293
355
  className="btn-clear-text"
294
356
  onClick={() => {
295
357
  if (imageThumbSearchInput) {
358
+ if (!isAlgoliaEnabled) {
359
+ searchOrRedirect('');
360
+ }
296
361
  setValueInput('');
297
- refine('');
362
+ if (isAlgoliaEnabled) {
363
+ refine('');
364
+ }
298
365
  return;
299
366
  }
300
367
  setValueInput('');
301
- refine('');
368
+ if (isAlgoliaEnabled) {
369
+ refine('');
370
+ }
302
371
  dispatch(reset(''));
303
372
  history.push('/');
304
373
  }}
@@ -4,11 +4,17 @@ import CloseIcon from '@material-ui/icons/Close';
4
4
  import IconSearch from 'common/assets/icons/icon_search.svg';
5
5
  import { getFilters, searchFilters } from 'services/filter';
6
6
  import { useAppDispatch, useAppSelector } from 'Store/Store';
7
- import { setPreFilter } from 'Store/search/Search';
7
+ import {
8
+ setPreFilter,
9
+ setSearchResults,
10
+ updateStatusLoading,
11
+ } from 'Store/search/Search';
8
12
  import { useMediaQuery } from 'react-responsive';
9
13
  import { isEmpty, pickBy } from 'lodash';
10
14
  import { Skeleton } from '@material-ui/lab';
11
15
  import { truncateString } from 'helpers/truncateString';
16
+ import { find } from 'services/image';
17
+ import { useQuery } from 'hooks/useQuery';
12
18
 
13
19
  interface Props {
14
20
  handleClose?: any;
@@ -19,11 +25,18 @@ function PreFilterComponent(props: Props) {
19
25
  const { handleClose } = props;
20
26
  const dispatch = useAppDispatch();
21
27
  const stateGlobal = useAppSelector(state => state);
22
- const {
23
- settings,
24
- search: { preFilter: keyFilterState },
25
- } = stateGlobal;
28
+ const { settings } = stateGlobal;
26
29
  const [resultFilter, setResultFilter] = useState<any>([]);
30
+ const query = useQuery();
31
+ const searchQuery = query.get('query') || '';
32
+ const { search } = stateGlobal;
33
+ const {
34
+ preFilter: keyFilterState,
35
+
36
+ requestImage,
37
+ selectedRegion,
38
+ } = search;
39
+
27
40
  const [keyFilter, setKeyFilter] = useState<Record<string, boolean>>(
28
41
  keyFilterState || {},
29
42
  );
@@ -105,7 +118,45 @@ function PreFilterComponent(props: Props) {
105
118
  };
106
119
 
107
120
  const onHandlerSubmitData = () => {
108
- dispatch(setPreFilter(pickBy(keyFilter, value => !!value)));
121
+ const preFilter = pickBy(keyFilter, value => !!value);
122
+ dispatch(setPreFilter(preFilter));
123
+
124
+ if (!settings.algolia?.enabled && (searchQuery || requestImage)) {
125
+ let payload: any;
126
+ let filters: any[] = [];
127
+ const preFilterValues = [
128
+ {
129
+ key: settings.visualSearchFilterKey,
130
+ values: Object.keys(preFilter) as string[],
131
+ },
132
+ ];
133
+ dispatch(updateStatusLoading(true));
134
+
135
+ find({
136
+ image: requestImage?.canvas as HTMLCanvasElement,
137
+ settings,
138
+ filters: !isEmpty(preFilter) ? preFilterValues : undefined,
139
+ region: selectedRegion,
140
+ text: searchQuery,
141
+ })
142
+ .then((res: any) => {
143
+ res?.results.map((item: any) => {
144
+ filters.push({
145
+ sku: item.sku,
146
+ score: item.score,
147
+ });
148
+ });
149
+ payload = {
150
+ ...res,
151
+ filters,
152
+ };
153
+ dispatch(setSearchResults(payload));
154
+ dispatch(updateStatusLoading(false));
155
+ })
156
+ .catch((e: any) => {
157
+ dispatch(updateStatusLoading(false));
158
+ });
159
+ }
109
160
  handleClose();
110
161
  };
111
162
 
@@ -338,7 +389,7 @@ function PreFilterComponent(props: Props) {
338
389
  {Array(6)
339
390
  .fill('')
340
391
  .map((_, index) => (
341
- <Skeleton animation={'pulse'} height={30} />
392
+ <Skeleton key={index} animation={'pulse'} height={30} />
342
393
  ))}
343
394
  </Box>
344
395
  );
@@ -22,6 +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
26
 
26
27
  interface Props {
27
28
  dataItem: any;
@@ -111,7 +112,11 @@ function ItemResult(props: Props) {
111
112
  dispatch(updateStatusLoading(false));
112
113
  }, 400);
113
114
  };
114
- const ctaLink = dataItem[settings.field?.ctaLinkField];
115
+ const ctaLink = get(
116
+ dataItem,
117
+ settings.field?.ctaLinkField ? settings.field?.ctaLinkField : 'links.main',
118
+ );
119
+
115
120
  return (
116
121
  <Box className="wrap-main-item-result">
117
122
  <DefaultModal
package/src/index.tsx CHANGED
@@ -13,7 +13,7 @@ import { store } from 'Store/Store';
13
13
  import 'react-responsive-carousel/lib/styles/carousel.min.css';
14
14
  import { createTheme } from '@material-ui/core/styles';
15
15
  import { Toaster } from 'components/Toaster';
16
- import AuthProvider from 'components/AuthProvider';
16
+ import AuthProvider from 'components/Provider/AuthProvider';
17
17
 
18
18
  document.title = window.location.host;
19
19
 
@@ -483,6 +483,10 @@ button {
483
483
  }
484
484
  }
485
485
  }
486
+ .contact-support:hover {
487
+ cursor: pointer;
488
+ color: #fff !important;
489
+ }
486
490
 
487
491
  .wrap-main-item-result {
488
492
  // filter: drop-shadow(0px 0px 6px #aaabb5b3);
@@ -916,7 +920,6 @@ button {
916
920
  }
917
921
  }
918
922
  .box-result {
919
- height: calc(100vh - 148px);
920
923
  @media screen and (max-width: 776px) {
921
924
  height: 100%
922
925
  }