@nyris/nyris-webapp 0.3.57 → 0.3.59

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 (94) hide show
  1. package/build/asset-manifest.json +15 -16
  2. package/build/index.html +1 -1
  3. package/build/js/settings.example.js +1 -0
  4. package/build/static/css/main.cb6e2cfd.css +4 -0
  5. package/build/static/css/main.cb6e2cfd.css.map +1 -0
  6. package/build/static/js/main.a1e24447.js +3 -0
  7. package/build/static/js/{main.5143aa56.js.LICENSE.txt → main.a1e24447.js.LICENSE.txt} +55 -0
  8. package/build/static/js/main.a1e24447.js.map +1 -0
  9. package/build/static/media/camera_simple.bff4194954bbb5f4bc33bd99014a93e8.svg +3 -0
  10. package/build/static/media/collpase.50dae91fff891c46b10dfc281344d0ef.svg +6 -0
  11. package/build/static/media/crop.0676ebbbdc1375ed67e32bba890ce941.svg +3 -0
  12. package/build/static/media/{download.8007f7c72e2080a9ffa96fa63d480dcf.svg → download.a8452bc23334e9f8e53fe1225742d216.svg} +1 -1
  13. package/build/static/media/gallery.15d1f3308921480a8c9d96d9a77c9966.svg +3 -0
  14. package/build/static/media/{logout.b544fcd2969edf431a1e998333119834.svg → logout.bab56bd407f25eb34d6eff401a436ce1.svg} +1 -1
  15. package/build/static/media/next-arrow.b13263d05d107ceb5e99bc4fabb41279.svg +3 -0
  16. package/build/static/media/plus.329672cb2feb55345490589e91481b88.svg +3 -0
  17. package/package.json +8 -5
  18. package/public/index.html +0 -3
  19. package/public/js/settings.example.js +1 -0
  20. package/src/Router.tsx +2 -2
  21. package/src/Store/Store.ts +2 -0
  22. package/src/Store/requestStore.ts +70 -0
  23. package/src/Store/resultStore.ts +25 -0
  24. package/src/Store/search/Search.ts +2 -33
  25. package/src/Store/search/search.initialState.ts +1 -4
  26. package/src/Store/search/types.ts +0 -5
  27. package/src/common/assets/icons/arrow_enter.svg +3 -0
  28. package/src/common/assets/icons/camera_simple.svg +3 -0
  29. package/src/common/assets/icons/collpase.svg +6 -0
  30. package/src/common/assets/icons/crop.svg +3 -0
  31. package/src/common/assets/icons/download.svg +1 -1
  32. package/src/common/assets/icons/gallery.svg +3 -0
  33. package/src/common/assets/icons/logout.svg +1 -1
  34. package/src/common/assets/icons/next-arrow.svg +3 -0
  35. package/src/common/assets/icons/plus.svg +3 -0
  36. package/src/components/CadenasWebViewer.tsx +1 -1
  37. package/src/components/DragDropFile.tsx +17 -77
  38. package/src/components/Experience-visual-search/ExperienceVisualSearch.tsx +47 -71
  39. package/src/components/Feedback.tsx +23 -9
  40. package/src/components/GoBackButton.tsx +15 -18
  41. package/src/components/HeaderMobile.tsx +342 -246
  42. package/src/components/ImageCaptureHelpModal.tsx +63 -65
  43. package/src/components/ImagePreview.tsx +564 -0
  44. package/src/components/Inquiry/InquiryBanner.tsx +1 -1
  45. package/src/components/Inquiry/InquiryModal.tsx +4 -7
  46. package/src/components/Layout.tsx +9 -18
  47. package/src/components/MobileLayout.tsx +51 -0
  48. package/src/components/MobilePostFilter.tsx +9 -3
  49. package/src/components/PanelResult/PostFilterAlgolia.tsx +4 -32
  50. package/src/components/PanelResult/expandable-panel.tsx +3 -16
  51. package/src/components/PanelResult/virtual-state-results.ts +17 -22
  52. package/src/components/ProductDetailView.tsx +1 -1
  53. package/src/components/SidePanel.tsx +7 -97
  54. package/src/components/UploadDisclaimer.tsx +85 -0
  55. package/src/components/appMobile.scss +2 -2
  56. package/src/components/common.scss +57 -27
  57. package/src/components/drawer/cameraCustom.tsx +389 -231
  58. package/src/components/icon-label/icon-label.tsx +1 -1
  59. package/src/components/input/inputSearch.tsx +197 -338
  60. package/src/components/pre-filter/index.tsx +70 -72
  61. package/src/components/results/ItemResult.tsx +34 -17
  62. package/src/components/rfq/RfqBanner.tsx +1 -4
  63. package/src/components/rfq/RfqModal.tsx +10 -10
  64. package/src/hooks/useFilteredRegions.ts +1 -1
  65. package/src/hooks/useImageSearch.ts +189 -0
  66. package/src/hooks/useSearchOrRedirect.ts +84 -0
  67. package/src/index.css +4 -0
  68. package/src/page/landingPage/Home.tsx +49 -0
  69. package/src/page/landingPage/{AppMD.tsx → HomeDesktop.tsx} +7 -34
  70. package/src/page/landingPage/{AppMobile.tsx → HomeMobile.tsx} +8 -37
  71. package/src/page/landingPage/common.scss +9 -1
  72. package/src/page/result/index.tsx +118 -232
  73. package/src/services/Feedback.ts +4 -5
  74. package/src/services/image.ts +19 -0
  75. package/src/types.ts +9 -7
  76. package/src/utils.ts +44 -0
  77. package/tailwind.config.js +54 -0
  78. package/build/static/css/main.67965609.css +0 -2
  79. package/build/static/css/main.67965609.css.map +0 -1
  80. package/build/static/js/main.5143aa56.js +0 -3
  81. package/build/static/js/main.5143aa56.js.map +0 -1
  82. package/build/static/media/arrow_down.f417689ce292978a8292a7f00407fdd5.svg +0 -3
  83. package/build/static/media/arrow_left.73d03a534eaf9b99ab196e0fb67da602.svg +0 -3
  84. package/build/static/media/arrow_right.59a4594a3a1657037537dbae1eee0251.svg +0 -3
  85. package/build/static/media/arrow_up.85dbe70bc51ec32c8894a06499330f14.svg +0 -3
  86. package/build/static/media/home.9ffb65a9c0be8fc5a502ba05cf5f719c.svg +0 -3
  87. package/build/static/media/icon_camera_mobile.6772053c4dfef487255649d2a05cc9d4.svg +0 -3
  88. package/build/static/media/reverse_camera.cee0200b151941cc83c182167a85d667.svg +0 -5
  89. package/src/App.tsx +0 -18
  90. package/src/components/AppMobile.tsx +0 -117
  91. package/src/components/FooterMobile.tsx +0 -230
  92. package/src/components/ImagePreviewMobile.tsx +0 -237
  93. /package/build/static/media/{add.2b72cedb98c4c89c954266d2356c166c.svg → add-rounded.2b72cedb98c4c89c954266d2356c166c.svg} +0 -0
  94. /package/src/common/assets/icons/{add.svg → add-rounded.svg} +0 -0
@@ -16,7 +16,7 @@ import { truncateString } from 'helpers/truncateString';
16
16
  import { find } from 'services/image';
17
17
  import { useQuery } from 'hooks/useQuery';
18
18
  import { useTranslation } from 'react-i18next';
19
- import ClearOutlinedIcon from "@material-ui/icons/ClearOutlined";
19
+ import ClearOutlinedIcon from '@material-ui/icons/ClearOutlined';
20
20
 
21
21
  interface Props {
22
22
  handleClose?: any;
@@ -32,21 +32,16 @@ function PreFilterComponent(props: Props) {
32
32
  const query = useQuery();
33
33
  const searchQuery = query.get('query') || '';
34
34
  const { search } = stateGlobal;
35
- const {
36
- preFilter: keyFilterState,
37
-
38
- requestImage,
39
- selectedRegion,
40
- } = search;
35
+ const { preFilter: keyFilterState, requestImage, selectedRegion } = search;
41
36
 
42
37
  const [keyFilter, setKeyFilter] = useState<Record<string, boolean>>(
43
38
  keyFilterState || {},
44
39
  );
40
+ const [searchKey, setSearchKey] = useState<string>('');
45
41
 
46
42
  const [isLoading, setLoading] = useState<boolean>(false);
47
43
  const [columns, setColumns] = useState<number>(0);
48
44
  const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
49
- const [searchKey, setSearchKey] = useState<string>('');
50
45
 
51
46
  const selectedFilter = useMemo(
52
47
  () =>
@@ -64,10 +59,10 @@ function PreFilterComponent(props: Props) {
64
59
  // eslint-disable-next-line react-hooks/exhaustive-deps
65
60
  }, []);
66
61
 
67
- useEffect(() => {
68
- filterSearchHandler(searchKey);
69
- // eslint-disable-next-line react-hooks/exhaustive-deps
70
- }, [searchKey]);
62
+ // useEffect(() => {
63
+ // filterSearchHandler(searchKey);
64
+ // // eslint-disable-next-line react-hooks/exhaustive-deps
65
+ // }, [searchKey]);
71
66
 
72
67
  const getDataFilterDesktop = async () => {
73
68
  setLoading(true);
@@ -239,6 +234,7 @@ function PreFilterComponent(props: Props) {
239
234
  className="input-search-filter"
240
235
  placeholder={t('Search')}
241
236
  onChange={(e: any) => {
237
+ filterSearchHandler(e.target.value);
242
238
  setSearchKey(e.target.value);
243
239
  }}
244
240
  value={searchKey}
@@ -247,6 +243,7 @@ function PreFilterComponent(props: Props) {
247
243
  className="btn-clear-text"
248
244
  onClick={() => {
249
245
  setSearchKey('');
246
+ filterSearchHandler('');
250
247
  }}
251
248
  >
252
249
  <ClearOutlinedIcon style={{ fontSize: 16, color: '#2B2C46' }} />
@@ -331,76 +328,77 @@ function PreFilterComponent(props: Props) {
331
328
  columnCount: 4,
332
329
  padding: '24px 24px 40px 24px',
333
330
  height: '100%',
334
-
335
331
  backgroundColor: '#FAFAFA',
336
332
  }
337
333
  }
338
334
  >
339
- {Object.entries(resultFilter).map(([key, value]: any, i: any) => {
340
- return (
341
- <div className="box-group-items" key={key}>
342
- <div
343
- style={{
344
- display: 'flex',
345
- flexDirection: 'column',
346
- rowGap: '12px',
347
- width: '100%',
348
- }}
349
- >
350
- <Typography
335
+ {Object.entries(resultFilter)
336
+ .sort()
337
+ .map(([key, value]: any, i: any) => {
338
+ return (
339
+ <div className="box-group-items" key={key}>
340
+ <div
351
341
  style={{
352
- fontWeight: 'bold',
353
- color: '#000',
354
- fontSize: '12px',
342
+ display: 'flex',
343
+ flexDirection: 'column',
344
+ rowGap: '12px',
345
+ width: '100%',
355
346
  }}
356
347
  >
357
- {key}
358
- </Typography>
348
+ <Typography
349
+ style={{
350
+ fontWeight: 'bold',
351
+ color: '#000',
352
+ fontSize: '12px',
353
+ }}
354
+ >
355
+ {key}
356
+ </Typography>
359
357
 
360
- {value.map((item: any, index: any) => {
361
- return (
362
- <Tooltip
363
- key={item}
364
- title={item}
365
- placement="top"
366
- arrow={true}
367
- disableHoverListener={item.length < 35}
368
- >
369
- <div
370
- aria-label={item}
371
- style={{
372
- cursor: 'pointer',
373
- fontSize: '12px',
374
- minHeight: '20px',
375
- color: '#2B2C46',
376
- width: '100%',
377
- maxWidth: 'fit-content',
378
- overflow: 'hidden',
379
- textOverflow: 'ellipsis',
380
- whiteSpace: 'nowrap',
381
- backgroundColor: keyFilter[item] ? '#E9E9EC' : '',
382
- borderRadius: 8,
383
- paddingLeft: '8px',
384
- paddingRight: '8px',
385
- }}
386
- onClick={() => {
387
- if (selectedFilter < maxFilter) {
388
- setKeyFilter({
389
- ...keyFilter,
390
- [item]: !keyFilter[item],
391
- });
392
- }
393
- }}
358
+ {value.map((item: any, index: any) => {
359
+ return (
360
+ <Tooltip
361
+ key={item}
362
+ title={item}
363
+ placement="top"
364
+ arrow={true}
365
+ disableHoverListener={item.length < 35}
394
366
  >
395
- {truncateString(item, !isMobile ? 35 : 35)}
396
- </div>
397
- </Tooltip>
398
- );
399
- })}
367
+ <div
368
+ aria-label={item}
369
+ style={{
370
+ cursor: 'pointer',
371
+ fontSize: '12px',
372
+ minHeight: '20px',
373
+ color: '#2B2C46',
374
+ width: '100%',
375
+ maxWidth: 'fit-content',
376
+ overflow: 'hidden',
377
+ textOverflow: 'ellipsis',
378
+ whiteSpace: 'nowrap',
379
+ backgroundColor: keyFilter[item] ? '#E9E9EC' : '',
380
+ borderRadius: 8,
381
+ paddingLeft: '8px',
382
+ paddingRight: '8px',
383
+ }}
384
+ onClick={() => {
385
+ if (selectedFilter < maxFilter) {
386
+ setKeyFilter({
387
+ ...keyFilter,
388
+ [item]: !keyFilter[item],
389
+ });
390
+ }
391
+ }}
392
+ >
393
+ {truncateString(item, !isMobile ? 35 : 35)}
394
+ </div>
395
+ </Tooltip>
396
+ );
397
+ })}
398
+ </div>
400
399
  </div>
401
- </div>
402
- );
403
- })}
400
+ );
401
+ })}
404
402
  {isLoading && (
405
403
  <div style={{ columnCount: isMobile ? 1 : 4 }}>
406
404
  {Array(12)
@@ -166,7 +166,7 @@ function ItemResult(props: Props) {
166
166
  </Button>
167
167
  </div>
168
168
  )}
169
- {!isHover && main_image_link && (
169
+ {!isHover && main_image_link && !settings.noSimilarSearch && (
170
170
  <div
171
171
  className="box-icon-modal"
172
172
  onClick={() => {
@@ -227,12 +227,24 @@ function ItemResult(props: Props) {
227
227
  <div className="info-sku">{dataItem.sku}</div>
228
228
  <span className="info-marking">{dataItem.Bezeichnung}</span>
229
229
  <Tooltip
230
- title={settings.language === 'en' ? dataItem.VK_Text_Englisch: dataItem.VK_Text_Deutsch}
230
+ title={
231
+ settings.language === 'en'
232
+ ? dataItem.VK_Text_Englisch
233
+ : dataItem.VK_Text_Deutsch
234
+ }
231
235
  placement="top"
232
236
  arrow={true}
233
- disableHoverListener={settings.language === 'en' ? dataItem.VK_Text_Englisch?.length < 76 : dataItem.VK_Text_Deutsch?.length < 76}
237
+ disableHoverListener={
238
+ settings.language === 'en'
239
+ ? dataItem.VK_Text_Englisch?.length < 76
240
+ : dataItem.VK_Text_Deutsch?.length < 76
241
+ }
234
242
  >
235
- <div className="info-description">{settings.language === 'en' ? dataItem.VK_Text_Englisch : dataItem.VK_Text_Deutsch}</div>
243
+ <div className="info-description">
244
+ {settings.language === 'en'
245
+ ? dataItem.VK_Text_Englisch
246
+ : dataItem.VK_Text_Deutsch}
247
+ </div>
236
248
  </Tooltip>
237
249
  </div>
238
250
  ) : (
@@ -242,7 +254,7 @@ function ItemResult(props: Props) {
242
254
  flexDirection: 'column',
243
255
  backgroundColor: '#F3F3F5',
244
256
  flexGrow: 1,
245
- zIndex: 100,
257
+ zIndex: 10,
246
258
  display: 'flex',
247
259
  }}
248
260
  >
@@ -306,16 +318,19 @@ function ItemResult(props: Props) {
306
318
  color: settings.theme?.mainTextColor || '#2B2C46',
307
319
  }}
308
320
  >
309
- <span
310
- style={{
311
- color: get(dataItem, settings.field.warehouseStockValue)
312
- ? '#00C070'
313
- : '#c54545',
314
- fontWeight: 600,
315
- }}
316
- >
317
- {get(dataItem, settings.field.warehouseStockValue) || 0}
318
- </span>
321
+ <span
322
+ style={{
323
+ color: get(
324
+ dataItem,
325
+ settings.field.warehouseStockValue,
326
+ )
327
+ ? '#00C070'
328
+ : '#c54545',
329
+ fontWeight: 600,
330
+ }}
331
+ >
332
+ {get(dataItem, settings.field.warehouseStockValue) || 0}
333
+ </span>
319
334
  </Typography>
320
335
  )}
321
336
  </div>
@@ -397,7 +412,8 @@ function ItemResult(props: Props) {
397
412
  boxShadow: '-2px 2px 4px rgba(170, 171, 181, 0.5)',
398
413
  // marginBottom: 22,
399
414
  height: 40,
400
- background: settings.theme.secondaryCTAButtonColor || '#2B2C46',
415
+ background:
416
+ settings.theme.secondaryCTAButtonColor || '#2B2C46',
401
417
  borderRadius: 4,
402
418
  padding: '0px 8px',
403
419
  marginTop: '8px',
@@ -431,7 +447,8 @@ function ItemResult(props: Props) {
431
447
  fontSize: '12px',
432
448
  letterSpacing: '0.27px',
433
449
  wordBreak: 'break-all',
434
- maxWidth: !isMobile && secondaryCTALink ? '136px' : '164x',
450
+ maxWidth:
451
+ !isMobile && secondaryCTALink ? '136px' : '164x',
435
452
  paddingRight: '8px',
436
453
  }}
437
454
  align="left"
@@ -42,10 +42,7 @@ function RfqBanner({
42
42
  >
43
43
  <div>
44
44
  <img
45
- src={getCroppedCanvas(
46
- requestImage?.canvas,
47
- selectedRegion,
48
- )?.toDataURL()}
45
+ src={getCroppedCanvas(requestImage, selectedRegion)?.toDataURL()}
49
46
  alt="request_image"
50
47
  style={{
51
48
  mixBlendMode: rfqStatus !== 'inactive' ? 'overlay' : 'unset',
@@ -10,9 +10,8 @@ import toast from 'react-hot-toast';
10
10
  import { ReactComponent as ErrorIcon } from 'common/assets/icons/error.svg';
11
11
  import { useMediaQuery } from 'react-responsive';
12
12
  import { useAppSelector } from '../../Store/Store';
13
+ import useRequestStore from 'Store/requestStore';
13
14
  interface Props {
14
- requestImage: any;
15
- selectedRegion: any;
16
15
  setIsRfqModalOpen: any;
17
16
  isRfqModalOpen?: any;
18
17
  setRfqStatus: any;
@@ -35,8 +34,6 @@ const getErrorMessage = (error: any) => {
35
34
  };
36
35
 
37
36
  export default function RfqModal({
38
- requestImage,
39
- selectedRegion,
40
37
  setIsRfqModalOpen,
41
38
  isRfqModalOpen,
42
39
  setRfqStatus,
@@ -47,6 +44,12 @@ export default function RfqModal({
47
44
  const { settings } = useAppSelector(state => state);
48
45
 
49
46
  const [information, setInformation] = useState('');
47
+
48
+ const { requestImages, regions } = useRequestStore(state => ({
49
+ requestImages: state.requestImages,
50
+ regions: state.regions,
51
+ }));
52
+
50
53
  const setFormattedContent = React.useCallback(
51
54
  (text: string) => {
52
55
  setInformation(text.slice(0, 150));
@@ -61,8 +64,8 @@ export default function RfqModal({
61
64
 
62
65
  const handleRfq = async (e: { preventDefault: () => void }) => {
63
66
  e.preventDefault();
64
- const { canvas }: any = requestImage;
65
- const croppedImage = getCroppedCanvas(canvas, selectedRegion);
67
+ const canvas: any = requestImages[0];
68
+ const croppedImage = getCroppedCanvas(canvas, regions[0]);
66
69
  const serviceId = 'service_zfsxshi';
67
70
  setIsRfqModalOpen(false);
68
71
  const templateId = settings.rfq?.emailTemplateId;
@@ -179,10 +182,7 @@ export default function RfqModal({
179
182
  }}
180
183
  >
181
184
  <img
182
- src={getCroppedCanvas(
183
- requestImage?.canvas,
184
- selectedRegion,
185
- )?.toDataURL()}
185
+ src={getCroppedCanvas(requestImages[0], regions[0])?.toDataURL()}
186
186
  alt="request_image"
187
187
  style={{ maxHeight: '200px' }}
188
188
  />
@@ -3,7 +3,7 @@ import { useMemo } from 'react';
3
3
  const useFilteredRegions = (regions: any, imageSelection: any) => {
4
4
  const filteredRegions = useMemo(
5
5
  () =>
6
- regions.map(
6
+ regions?.map(
7
7
  (region: {
8
8
  normalizedRect: { x1: any; x2: any; y1: any; y2: any };
9
9
  }) => {
@@ -0,0 +1,189 @@
1
+ import { RectCoords } from '@nyris/nyris-api';
2
+ import { isEmpty } from 'lodash';
3
+ import { useCallback } from 'react';
4
+ import { createImage, find, findMulti, findRegions } from 'services/image';
5
+ import useRequestStore from 'Store/requestStore';
6
+ import useResultStore from 'Store/resultStore';
7
+ import {
8
+ setFirstSearchImage,
9
+ setFirstSearchPrefilters,
10
+ setFirstSearchResults,
11
+ setRegions,
12
+ setRequestImage,
13
+ setSearchResults,
14
+ setSelectedRegion,
15
+ setShowFeedback,
16
+ updateStatusLoading,
17
+ } from 'Store/search/Search';
18
+ import { useAppDispatch, useAppSelector } from 'Store/Store';
19
+ import { AppSettings } from 'types';
20
+ import { compressImage } from 'utils';
21
+
22
+ export const useImageSearch = () => {
23
+ const dispatch = useAppDispatch();
24
+
25
+ const preFilter = useAppSelector(state => state.search.preFilter);
26
+ const firstSearchResults = useAppSelector(
27
+ state => state.search.firstSearchResults,
28
+ );
29
+ const regions = useAppSelector(state => state.settings.regions);
30
+
31
+ const { setRequestImages, setImageRegions } = useRequestStore(state => ({
32
+ setRequestImages: state.setRequestImages,
33
+ setImageRegions: state.setRegions,
34
+ requestImages: state.requestImages,
35
+ regions: state.regions,
36
+ }));
37
+
38
+ const { setDetectedObject } = useResultStore(state => ({
39
+ setDetectedObject: state.setDetectedObject,
40
+ }));
41
+
42
+ const singleImageSearch = useCallback(
43
+ async ({
44
+ image,
45
+ settings,
46
+ showFeedback = true,
47
+ imageRegion,
48
+ newSearch,
49
+ }: {
50
+ image: any;
51
+ settings: AppSettings;
52
+ showFeedback?: boolean;
53
+ imageRegion?: RectCoords;
54
+ newSearch?: boolean;
55
+ }) => {
56
+ let region: RectCoords | undefined = imageRegion;
57
+ let res: any;
58
+ let compressedBase64;
59
+
60
+ try {
61
+ compressedBase64 = await compressImage(image);
62
+ } catch (error) {}
63
+
64
+ let canvasImage = await createImage(compressedBase64 || image);
65
+
66
+ if (!imageRegion) {
67
+ dispatch(setRequestImage(canvasImage));
68
+ setRequestImages([canvasImage]);
69
+ }
70
+
71
+ if (regions && !imageRegion) {
72
+ try {
73
+ let res = await findRegions(canvasImage, settings);
74
+ setDetectedObject(res.regions, 0);
75
+ dispatch(setRegions(res.regions));
76
+ region = res.selectedRegion;
77
+ dispatch(setSelectedRegion(region));
78
+ setImageRegions([region]);
79
+ } catch (error) {}
80
+ }
81
+
82
+ const preFilterValues = [
83
+ {
84
+ key: settings.visualSearchFilterKey,
85
+ values: Object.keys(preFilter),
86
+ },
87
+ ];
88
+ let filters: any[] = [];
89
+
90
+ try {
91
+ res = await find({
92
+ image: canvasImage,
93
+ settings,
94
+ filters: !isEmpty(preFilter) ? preFilterValues : undefined,
95
+ region,
96
+ });
97
+
98
+ res?.results.forEach((item: any) => {
99
+ filters.push({
100
+ sku: item.sku,
101
+ score: item.score,
102
+ });
103
+ });
104
+ const payload = {
105
+ ...res,
106
+ filters,
107
+ };
108
+ dispatch(setSearchResults(payload));
109
+
110
+ if (showFeedback) {
111
+ dispatch(setShowFeedback(true));
112
+ }
113
+ // go back
114
+ if (!firstSearchResults || newSearch) {
115
+ dispatch(setFirstSearchResults(payload));
116
+ dispatch(setFirstSearchImage(canvasImage));
117
+ dispatch(setFirstSearchPrefilters(preFilter));
118
+ }
119
+ } catch (error) {
120
+ dispatch(updateStatusLoading(false));
121
+ }
122
+
123
+ return res;
124
+ },
125
+ [
126
+ dispatch,
127
+ firstSearchResults,
128
+ preFilter,
129
+ regions,
130
+ setDetectedObject,
131
+ setImageRegions,
132
+ setRequestImages,
133
+ ],
134
+ );
135
+
136
+ const multiImageSearch = useCallback(
137
+ async ({
138
+ images,
139
+ settings,
140
+ regions,
141
+ showFeedback = true,
142
+ }: {
143
+ images: HTMLCanvasElement[];
144
+ regions: RectCoords[];
145
+ settings: AppSettings;
146
+ showFeedback?: boolean;
147
+ }) => {
148
+ const preFilterValues = [
149
+ {
150
+ key: settings.visualSearchFilterKey,
151
+ values: Object.keys(preFilter),
152
+ },
153
+ ];
154
+ let filters: any[] = [];
155
+ let res: any;
156
+ try {
157
+ const res = await findMulti({
158
+ images,
159
+ settings,
160
+ regions,
161
+ filters: !isEmpty(preFilter) ? preFilterValues : undefined,
162
+ });
163
+
164
+ res?.results.forEach((item: any) => {
165
+ filters.push({
166
+ sku: item.sku,
167
+ score: item.score,
168
+ });
169
+ });
170
+ const payload = {
171
+ ...res,
172
+ filters,
173
+ };
174
+ dispatch(setSearchResults(payload));
175
+
176
+ if (showFeedback) {
177
+ dispatch(setShowFeedback(true));
178
+ }
179
+ } catch (error) {
180
+ dispatch(updateStatusLoading(false));
181
+ }
182
+
183
+ return res;
184
+ },
185
+ [dispatch, preFilter],
186
+ );
187
+
188
+ return { singleImageSearch, multiImageSearch };
189
+ };
@@ -0,0 +1,84 @@
1
+ import preFilter from 'components/pre-filter';
2
+ import { debounce, find, isEmpty } from 'lodash';
3
+ import { useCallback } from 'react';
4
+ import { useHistory } from 'react-router-dom';
5
+ import useRequestStore from 'Store/requestStore';
6
+ import {
7
+ updateQueryText,
8
+ updateStatusLoading,
9
+ setSearchResults,
10
+ setShowFeedback,
11
+ } from 'Store/search/Search';
12
+ import { useAppDispatch, useAppSelector } from 'Store/Store';
13
+
14
+ export const useSearchOrRedirect = () => {
15
+ const dispatch = useAppDispatch();
16
+ const history = useHistory();
17
+
18
+ const { requestImages, regions } = useRequestStore(state => ({
19
+ requestImages: state.requestImages,
20
+ regions: state.regions,
21
+ }));
22
+
23
+ const settings = useAppSelector(state => state.settings);
24
+ const isAlgoliaEnabled = settings.algolia?.enabled;
25
+
26
+ // eslint-disable-next-line react-hooks/exhaustive-deps
27
+ const searchOrRedirect = useCallback(
28
+ debounce((value: any) => {
29
+ if (!isAlgoliaEnabled) {
30
+ dispatch(updateQueryText(value));
31
+ let payload: any;
32
+ let filters: any[] = [];
33
+ const preFilterValues = [
34
+ {
35
+ key: settings.visualSearchFilterKey,
36
+ values: Object.keys(preFilter),
37
+ },
38
+ ];
39
+ if (value || requestImages.length > 0) {
40
+ dispatch(updateStatusLoading(true));
41
+ find({
42
+ image: requestImages[0],
43
+ settings,
44
+ filters: !isEmpty(preFilter) ? preFilterValues : undefined,
45
+ region: regions[0],
46
+ text: value,
47
+ })
48
+ .then((res: any) => {
49
+ res?.results.forEach((item: any) => {
50
+ filters.push({
51
+ sku: item.sku,
52
+ score: item.score,
53
+ });
54
+ });
55
+ payload = {
56
+ ...res,
57
+ filters,
58
+ };
59
+ dispatch(setSearchResults(payload));
60
+ dispatch(updateStatusLoading(false));
61
+ dispatch(setShowFeedback(true));
62
+ })
63
+ .catch((e: any) => {
64
+ dispatch(updateStatusLoading(false));
65
+ });
66
+ } else {
67
+ dispatch(setSearchResults([]));
68
+ }
69
+ }
70
+
71
+ if (value) {
72
+ history.push({
73
+ pathname: '/result',
74
+ search: `?query=${value}`,
75
+ });
76
+ } else {
77
+ history.push('/result');
78
+ }
79
+ }, 500),
80
+ [requestImages, preFilter, regions, isAlgoliaEnabled],
81
+ );
82
+
83
+ return searchOrRedirect;
84
+ };
package/src/index.css CHANGED
@@ -1,3 +1,7 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
1
5
  body {
2
6
  margin: 0;
3
7
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",