@nyris/nyris-webapp 0.3.65 → 0.3.67

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 (32) hide show
  1. package/build/asset-manifest.json +6 -6
  2. package/build/index.html +1 -1
  3. package/build/static/css/main.05cedda2.css +4 -0
  4. package/build/static/css/main.05cedda2.css.map +1 -0
  5. package/build/static/js/main.6bc37b78.js +3 -0
  6. package/build/static/js/main.6bc37b78.js.map +1 -0
  7. package/package.json +3 -3
  8. package/src/Store/Store.ts +2 -1
  9. package/src/Store/constants.ts +1 -0
  10. package/src/components/DragDropFile.tsx +22 -5
  11. package/src/components/Experience-visual-search/ExperienceVisualSearch.tsx +43 -29
  12. package/src/components/ImagePreview.tsx +6 -2
  13. package/src/components/ProductList/index.tsx +13 -2
  14. package/src/components/UploadDisclaimer.tsx +4 -1
  15. package/src/components/drawer/cameraCustom.tsx +33 -17
  16. package/src/components/input/inputSearch.tsx +33 -14
  17. package/src/components/pre-filter/index.tsx +40 -1
  18. package/src/hooks/useCadSearch.ts +179 -0
  19. package/src/hooks/useImageSearch.ts +23 -9
  20. package/src/page/landingPage/Home.tsx +47 -4
  21. package/src/page/landingPage/HomeDesktop.tsx +12 -2
  22. package/src/page/landingPage/HomeMobile.tsx +12 -2
  23. package/src/page/result/index.tsx +7 -31
  24. package/src/services/image.ts +28 -0
  25. package/src/types.ts +25 -24
  26. package/src/utils.ts +23 -11
  27. package/.prettierrc +0 -7
  28. package/build/static/css/main.3a42f868.css +0 -4
  29. package/build/static/css/main.3a42f868.css.map +0 -1
  30. package/build/static/js/main.1bd28035.js +0 -3
  31. package/build/static/js/main.1bd28035.js.map +0 -1
  32. /package/build/static/js/{main.1bd28035.js.LICENSE.txt → main.6bc37b78.js.LICENSE.txt} +0 -0
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@nyris/nyris-webapp",
3
- "version": "0.3.65",
3
+ "version": "0.3.67",
4
4
  "dependencies": {
5
5
  "@auth0/auth0-react": "^2.2.4",
6
6
  "@emailjs/browser": "^4.3.3",
7
7
  "@material-ui/core": "^4.12.4",
8
8
  "@material-ui/icons": "^4.11.3",
9
9
  "@material-ui/lab": "^4.0.0-alpha.61",
10
- "@nyris/nyris-api": "^0.3.65",
11
- "@nyris/nyris-react-components": "^0.3.65",
10
+ "@nyris/nyris-api": "^0.3.67",
11
+ "@nyris/nyris-react-components": "^0.3.67",
12
12
  "@reduxjs/toolkit": "^2.2.1",
13
13
  "@splidejs/react-splide": "^0.7.12",
14
14
  "@testing-library/jest-dom": "^5.17.0",
@@ -9,10 +9,11 @@ import { defaultSettings } from './constants';
9
9
  declare var settings: AppSettings;
10
10
 
11
11
  settings.algolia.enabled = true;
12
- settings.preview = true;
13
12
  settings.showFeedbackAndShare = false;
14
13
  settings.multiImageSearch = false;
14
+ settings.preview = true;
15
15
  settings.clarityId = '';
16
+ settings.cadSearch = false;
16
17
 
17
18
  if (settings.rfq?.enabled && settings.support?.enabled) {
18
19
  settings.support.enabled = false;
@@ -27,4 +27,5 @@ export const defaultSettings: AppSettings = {
27
27
  secondaryTitle: '',
28
28
  theme: {},
29
29
  productDetails: '',
30
+ support: {},
30
31
  };
@@ -8,6 +8,8 @@ import { useTranslation } from 'react-i18next';
8
8
  import Loading from './Loading';
9
9
  import { useImageSearch } from 'hooks/useImageSearch';
10
10
  import { Icon } from '@nyris/nyris-react-components';
11
+ import { isCadFile } from '@nyris/nyris-api';
12
+ import { useCadSearch } from 'hooks/useCadSearch';
11
13
 
12
14
  interface Props {
13
15
  acceptTypes: any;
@@ -21,8 +23,10 @@ function DragDropFile(props: Props) {
21
23
  const { isLoading } = props;
22
24
  const settings = useAppSelector(state => state.settings);
23
25
  const { t } = useTranslation();
26
+ const { cadSearch } = useCadSearch();
24
27
 
25
28
  const { singleImageSearch } = useImageSearch();
29
+ const isCadSearch = window.settings.cadSearch;
26
30
 
27
31
  const { getRootProps, getInputProps, isDragActive } = useDropzone({
28
32
  onDrop: async (fs: File[], _, e) => {
@@ -31,12 +35,22 @@ function DragDropFile(props: Props) {
31
35
 
32
36
  dispatch(updateStatusLoading(true));
33
37
  dispatch(loadingActionResults());
34
-
35
- singleImageSearch({ image: fs[0], settings, showFeedback: true }).then(
36
- () => {
38
+ if (isCadFile(fs[0]) && isCadSearch) {
39
+ dispatch(updateStatusLoading(true));
40
+ dispatch(loadingActionResults());
41
+ if (history.location.pathname !== '/result') {
42
+ history.push('/result');
43
+ }
44
+ cadSearch({ file: fs[0], settings, newSearch: true }).then(res => {
37
45
  dispatch(updateStatusLoading(false));
38
- },
39
- );
46
+ });
47
+ } else {
48
+ singleImageSearch({ image: fs[0], settings, showFeedback: true }).then(
49
+ () => {
50
+ dispatch(updateStatusLoading(false));
51
+ },
52
+ );
53
+ }
40
54
  },
41
55
  });
42
56
 
@@ -81,6 +95,9 @@ function DragDropFile(props: Props) {
81
95
  className="inputFile"
82
96
  placeholder="Choose photo"
83
97
  style={{ display: 'block', cursor: 'pointer' }}
98
+ accept={`${
99
+ isCadSearch ? '.stp,.step,.stl,.obj,.glb,.gltf,' : ''
100
+ }image/*`}
84
101
  />
85
102
  </div>
86
103
  </div>
@@ -12,11 +12,14 @@ import { useImageSearch } from 'hooks/useImageSearch';
12
12
  import { useHistory } from 'react-router-dom';
13
13
  import { Icon } from '@nyris/nyris-react-components';
14
14
 
15
- function ExperienceVisualSearch() {
15
+ function ExperienceVisualSearch({
16
+ experienceVisualSearchBlobs,
17
+ }: {
18
+ experienceVisualSearchBlobs: Blob[];
19
+ }) {
16
20
  const dispatch = useAppDispatch();
17
21
  const { settings } = useAppSelector(state => state);
18
22
  const [showModal, setShowModal] = useState(false);
19
- const [images, setImages] = useState<string[]>([]);
20
23
  const button = useRef(null);
21
24
  let interval = useRef<NodeJS.Timeout | null>(null);
22
25
  const history = useHistory();
@@ -51,22 +54,17 @@ function ExperienceVisualSearch() {
51
54
  const modalToggle = (isOpen: boolean) => {
52
55
  setShowModal(isOpen);
53
56
  if (isOpen) {
54
- const randomImages = settings?.experienceVisualSearchImages?.slice(
55
- 0,
56
- Math.min(settings?.experienceVisualSearchImages?.length, 4),
57
- );
58
- setImages(randomImages || []);
59
57
  document.body.classList.add('overflow-hidden');
60
58
  } else {
61
59
  document.body.classList.remove('overflow-hidden');
62
60
  }
63
61
  };
64
62
 
65
- const getUrlToCanvasFile = async (url: string) => {
63
+ const getUrlToCanvasFile = async (blob: string) => {
66
64
  dispatch(updateStatusLoading(true));
67
65
  dispatch(loadingActionResults());
68
66
 
69
- singleImageSearch({ image: url, settings }).then(() => {
67
+ singleImageSearch({ image: blob, settings }).then(() => {
70
68
  dispatch(updateStatusLoading(false));
71
69
  });
72
70
  history.push('/result');
@@ -112,30 +110,46 @@ function ExperienceVisualSearch() {
112
110
  Start your visual search by selecting an image below.
113
111
  </div>
114
112
  <div className="custom-modal-body-content experience-visual-search-images">
115
- {images.map(itemImage => (
116
- <div
117
- className="experience-visual-search-image-container"
118
- onClick={() => {
119
- modalToggle(false);
120
- getUrlToCanvasFile(itemImage);
121
- }}
122
- >
113
+ {new Array(4).fill(1).map((val, index) => {
114
+ let itemImage: any;
115
+
116
+ if (index <= experienceVisualSearchBlobs.length - 1) {
117
+ itemImage = URL.createObjectURL(
118
+ experienceVisualSearchBlobs[index],
119
+ );
120
+ }
121
+ return (
123
122
  <div
124
- className="experience-visual-search-image"
125
- style={{
126
- backgroundImage: `url(${itemImage}?width=192&height=192)`,
123
+ key={index}
124
+ className="experience-visual-search-image-container"
125
+ onClick={() => {
126
+ if (itemImage) {
127
+ modalToggle(false);
128
+ getUrlToCanvasFile(itemImage);
129
+ }
127
130
  }}
128
- />
129
- <div className="box-icon-modal">
130
- <Icon
131
- name="search_image"
132
- width={16}
133
- height={16}
134
- color={'#AAABB5'}
131
+ >
132
+ <div
133
+ className={`experience-visual-search-image ${
134
+ !itemImage ? 'animate-pulse bg-gray-200' : ''
135
+ }`}
136
+ style={{
137
+ backgroundImage: `url(${itemImage})`,
138
+ }}
135
139
  />
140
+ {itemImage && (
141
+ <div className="box-icon-modal">
142
+ <Icon
143
+ name="search_image"
144
+ width={16}
145
+ height={16}
146
+ color={'#AAABB5'}
147
+ />
148
+ </div>
149
+ )}
136
150
  </div>
137
- </div>
138
- ))}
151
+ );
152
+ })}
139
153
  </div>
140
154
  </div>
141
155
  </div>,
@@ -33,7 +33,6 @@ function ImagePreviewComponent({
33
33
  isExpanded,
34
34
  isCameraUploadEnabled = true,
35
35
  }: {
36
- requestImage?: any;
37
36
  imageSelection?: any;
38
37
  filteredRegions?: any;
39
38
  showAdjustInfo?: any;
@@ -180,6 +179,7 @@ function ImagePreviewComponent({
180
179
  settings,
181
180
  imageRegion: r,
182
181
  showFeedback: true,
182
+ compress: false,
183
183
  }).then((res: any) => {
184
184
  dispatch(updateStatusLoading(false));
185
185
 
@@ -467,7 +467,11 @@ function ImagePreviewComponent({
467
467
  'object-cover',
468
468
  'shadow-inner',
469
469
  ])}
470
- src={image.toDataURL()}
470
+ src={(() => {
471
+ if (image?.toDataURL) {
472
+ return image.toDataURL();
473
+ }
474
+ })()}
471
475
  alt=""
472
476
  onClick={() => {
473
477
  setCurrentIndex(index);
@@ -51,14 +51,25 @@ function ProductListComponent({
51
51
  !isSearchStalled
52
52
  ) {
53
53
  return (
54
- <div style={{ marginTop: '50px', width: '100%', textAlign: 'center', marginBottom: '12px' }}>
54
+ <div
55
+ style={{
56
+ marginTop: '50px',
57
+ width: '100%',
58
+ textAlign: 'center',
59
+ marginBottom: '12px',
60
+ }}
61
+ >
55
62
  {t('No products were found matching your search criteria.')}
56
63
  </div>
57
64
  );
58
65
  }
59
66
  return productList.map((hit: any, i: number) => {
60
67
  return (
61
- <div key={i} style={{ height: 'fit-content' }} className={settings.simpleCardView ? 'border' : ''}>
68
+ <div
69
+ key={i}
70
+ style={{ height: 'fit-content' }}
71
+ className={settings.simpleCardView ? 'border' : ''}
72
+ >
62
73
  <ItemResult
63
74
  dataItem={hit}
64
75
  indexItem={i}
@@ -13,6 +13,7 @@ function UploadDisclaimer({
13
13
  isMobile: boolean;
14
14
  }) {
15
15
  const [dontShowAgain, setDontShowAgain] = useState(false);
16
+ const isCadSearch = window.settings.cadSearch;
16
17
 
17
18
  return (
18
19
  <>
@@ -67,7 +68,9 @@ function UploadDisclaimer({
67
68
  type="file"
68
69
  name="take-picture"
69
70
  id="nyris__upload-photo"
70
- accept="image/jpeg,image/png,image/webp"
71
+ accept={`${
72
+ isCadSearch ? '.stp,.step,.stl,.obj,.glb,.gltf,' : ''
73
+ }image/jpeg,image/png,image/webp`}
71
74
  onChange={makeFileHandler(file =>
72
75
  onContinue({ file, dontShowAgain }),
73
76
  )}
@@ -1,5 +1,4 @@
1
1
  // @ts-nocheck
2
-
3
2
  import cx from 'classnames';
4
3
 
5
4
  import { Drawer } from '@material-ui/core';
@@ -20,6 +19,8 @@ import ImageCaptureHelpModal from 'components/ImageCaptureHelpModal';
20
19
  import { createPortal } from 'react-dom';
21
20
  import { compressImage } from 'utils';
22
21
  import { Icon } from '@nyris/nyris-react-components';
22
+ import { isCadFile } from '@nyris/nyris-api';
23
+ import { useCadSearch } from 'hooks/useCadSearch';
23
24
 
24
25
  interface Props {
25
26
  show: boolean;
@@ -35,7 +36,7 @@ function CameraCustom(props: Props) {
35
36
  const settings = useAppSelector(state => state.settings);
36
37
  const history = useHistory();
37
38
  const dispatch = useAppDispatch();
38
-
39
+ const isCadSearch = window.settings.cadSearch;
39
40
  const { singleImageSearch, multiImageSearch } = useImageSearch();
40
41
 
41
42
  const { requestImages, setRequestImages, regions } = useRequestStore(
@@ -45,6 +46,7 @@ function CameraCustom(props: Props) {
45
46
  regions: state.regions,
46
47
  }),
47
48
  );
49
+ const { cadSearch } = useCadSearch();
48
50
 
49
51
  const [capturedImages, setCapturedImages] = useState<HTMLCanvasElement[]>([]);
50
52
  const [currentIndex, setCurrentIndex] = useState(0);
@@ -81,21 +83,31 @@ function CameraCustom(props: Props) {
81
83
  };
82
84
 
83
85
  const handlerFindImage = async (image: any) => {
84
- dispatch(updateStatusLoading(true));
85
- dispatch(loadingActionResults());
86
- if (history.location.pathname !== '/result') {
87
- history.push('/result');
88
- }
89
-
90
- singleImageSearch({
91
- image: image,
92
- settings,
93
- newSearch,
94
- showFeedback: true,
95
- }).then(() => {
96
- dispatch(updateStatusLoading(false));
97
- });
86
+ if (isCadFile(image) && isCadSearch) {
87
+ dispatch(updateStatusLoading(true));
88
+ dispatch(loadingActionResults());
89
+ if (history.location.pathname !== '/result') {
90
+ history.push('/result');
91
+ }
92
+ cadSearch({ file: image, settings, newSearch: true }).then((res: any) => {
93
+ dispatch(updateStatusLoading(false));
94
+ });
95
+ } else {
96
+ dispatch(updateStatusLoading(true));
97
+ dispatch(loadingActionResults());
98
+ if (history.location.pathname !== '/result') {
99
+ history.push('/result');
100
+ }
98
101
 
102
+ singleImageSearch({
103
+ image: image,
104
+ settings,
105
+ newSearch,
106
+ showFeedback: true,
107
+ }).then(() => {
108
+ dispatch(updateStatusLoading(false));
109
+ });
110
+ }
99
111
  dispatch(onToggleModalItemDetail(false));
100
112
  handleClose();
101
113
  };
@@ -245,7 +257,11 @@ function CameraCustom(props: Props) {
245
257
  handlerFindImage(file);
246
258
  }
247
259
  }}
248
- accept="image/jpeg,image/png,image/webp"
260
+ accept={`${
261
+ isCadSearch
262
+ ? '.stp,.step,.stl,.obj,.glb,.gltf,'
263
+ : ''
264
+ }image/jpeg,image/png,image/webp`}
249
265
  onClick={event => {
250
266
  // @ts-ignore
251
267
  event.target.value = '';
@@ -23,6 +23,8 @@ import UploadDisclaimer from 'components/UploadDisclaimer';
23
23
  import useRequestStore from 'Store/requestStore';
24
24
  import { useSearchOrRedirect } from 'hooks/useSearchOrRedirect';
25
25
  import { Icon } from '@nyris/nyris-react-components';
26
+ import { useCadSearch } from 'hooks/useCadSearch';
27
+ import { isCadFile } from '@nyris/nyris-api';
26
28
 
27
29
  const SearchBox = (props: any) => {
28
30
  const { refine }: any = props;
@@ -41,6 +43,7 @@ const SearchBox = (props: any) => {
41
43
  const isAlgoliaEnabled = settings.algolia?.enabled;
42
44
  const searchbar = useRef<HTMLDivElement | null>(null);
43
45
  const { singleImageSearch } = useImageSearch();
46
+ const { cadSearch } = useCadSearch();
44
47
  const { user } = useAuth0();
45
48
  const [showDisclaimer, setShowDisclaimer] = useState(false);
46
49
 
@@ -48,6 +51,8 @@ const SearchBox = (props: any) => {
48
51
  requestImages: state.requestImages,
49
52
  }));
50
53
 
54
+ const isCadSearch = window.settings.cadSearch;
55
+
51
56
  const visualSearch = useMemo(() => requestImages.length > 0, [requestImages]);
52
57
 
53
58
  useEffect(() => {
@@ -112,20 +117,31 @@ const SearchBox = (props: any) => {
112
117
  const searchOrRedirect = useSearchOrRedirect();
113
118
 
114
119
  const onImageUpload = async (fs: any) => {
115
- dispatch(updateStatusLoading(true));
116
- dispatch(loadingActionResults());
117
- if (history.location.pathname !== '/result') {
118
- history.push('/result');
119
- }
120
+ if (isCadFile(fs) && isCadSearch) {
121
+ dispatch(updateStatusLoading(true));
122
+ dispatch(loadingActionResults());
123
+ if (history.location.pathname !== '/result') {
124
+ history.push('/result');
125
+ }
126
+ cadSearch({ file: fs, settings, newSearch: true }).then(res => {
127
+ dispatch(updateStatusLoading(false));
128
+ });
129
+ } else {
130
+ dispatch(updateStatusLoading(true));
131
+ dispatch(loadingActionResults());
132
+ if (history.location.pathname !== '/result') {
133
+ history.push('/result');
134
+ }
120
135
 
121
- singleImageSearch({
122
- image: fs,
123
- settings,
124
- showFeedback: true,
125
- newSearch: true,
126
- }).then(() => {
127
- dispatch(updateStatusLoading(false));
128
- });
136
+ singleImageSearch({
137
+ image: fs,
138
+ settings,
139
+ showFeedback: true,
140
+ newSearch: true,
141
+ }).then(() => {
142
+ dispatch(updateStatusLoading(false));
143
+ });
144
+ }
129
145
  };
130
146
 
131
147
  const onChangeText = (event: any) => {
@@ -305,7 +321,9 @@ const SearchBox = (props: any) => {
305
321
  )}
306
322
  <div className="wrap-box-input-mobile d-flex">
307
323
  <input
308
- accept="image/*"
324
+ accept={`${
325
+ isCadSearch ? '.stp,.step,.stl,.obj,.glb,.gltf,' : ''
326
+ }image/*`}
309
327
  id="icon-button-file"
310
328
  type="file"
311
329
  style={{ display: 'none' }}
@@ -315,6 +333,7 @@ const SearchBox = (props: any) => {
315
333
  onChange={e => {
316
334
  if (e?.target?.files) {
317
335
  const file = e?.target?.files[0];
336
+
318
337
  onImageUpload(file);
319
338
  }
320
339
  }}
@@ -4,6 +4,7 @@ import CloseIcon from '@material-ui/icons/Close';
4
4
  import { getFilters, searchFilters } from 'services/filter';
5
5
  import { useAppDispatch, useAppSelector } from 'Store/Store';
6
6
  import {
7
+ loadingActionResults,
7
8
  setPreFilter,
8
9
  setSearchResults,
9
10
  updateStatusLoading,
@@ -17,6 +18,8 @@ import { useQuery } from 'hooks/useQuery';
17
18
  import { useTranslation } from 'react-i18next';
18
19
  import ClearOutlinedIcon from '@material-ui/icons/ClearOutlined';
19
20
  import { Icon } from '@nyris/nyris-react-components';
21
+ import { useImageSearch } from 'hooks/useImageSearch';
22
+ import useRequestStore from 'Store/requestStore';
20
23
 
21
24
  interface Props {
22
25
  handleClose?: any;
@@ -43,6 +46,15 @@ function PreFilterComponent(props: Props) {
43
46
  const [columns, setColumns] = useState<number>(0);
44
47
  const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
45
48
 
49
+ const { singleImageSearch, multiImageSearch } = useImageSearch();
50
+
51
+ const { imageRegions, requestImages } = useRequestStore(state => ({
52
+ requestImages: state.requestImages,
53
+ updateRegion: state.updateRegion,
54
+ resetRegions: state.resetRegions,
55
+ imageRegions: state.regions,
56
+ }));
57
+
46
58
  const selectedFilter = useMemo(
47
59
  () =>
48
60
  Object.keys(keyFilter).reduce((count, key) => {
@@ -123,6 +135,7 @@ function PreFilterComponent(props: Props) {
123
135
  const onHandlerSubmitData = () => {
124
136
  const preFilter = pickBy(keyFilter, value => !!value);
125
137
  dispatch(setPreFilter(preFilter));
138
+ handleClose();
126
139
 
127
140
  if (!settings.algolia?.enabled && (searchQuery || requestImage)) {
128
141
  let payload: any;
@@ -158,8 +171,34 @@ function PreFilterComponent(props: Props) {
158
171
  .catch((e: any) => {
159
172
  dispatch(updateStatusLoading(false));
160
173
  });
174
+ } else {
175
+ if (requestImages.length === 0) {
176
+ return;
177
+ }
178
+ dispatch(updateStatusLoading(true));
179
+ dispatch(loadingActionResults());
180
+
181
+ if (requestImages.length === 1) {
182
+ singleImageSearch({
183
+ image: requestImages[0],
184
+ settings,
185
+ imageRegion: imageRegions[0],
186
+ preFilterParams: preFilter,
187
+ compress: false,
188
+ }).then(res => {
189
+ dispatch(updateStatusLoading(false));
190
+ });
191
+ } else {
192
+ multiImageSearch({
193
+ images: requestImages,
194
+ settings,
195
+ regions: imageRegions,
196
+ preFilterParams: preFilter,
197
+ }).then(res => {
198
+ dispatch(updateStatusLoading(false));
199
+ });
200
+ }
161
201
  }
162
- handleClose();
163
202
  };
164
203
  const { t } = useTranslation();
165
204