@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
@@ -0,0 +1,564 @@
1
+ import { useCallback, useRef, useState } from 'react';
2
+
3
+ import cx from 'classnames';
4
+
5
+ import { RectCoords } from '@nyris/nyris-api';
6
+ import { Preview } from '@nyris/nyris-react-components';
7
+ import { DEFAULT_REGION } from '../constants';
8
+ import { useTranslation } from 'react-i18next';
9
+ import { useAppDispatch, useAppSelector } from 'Store/Store';
10
+ import { ReactComponent as PlusIcon } from 'common/assets/icons/plus.svg';
11
+ import { ReactComponent as CropIcon } from 'common/assets/icons/crop.svg';
12
+ import { ReactComponent as CollapseIcon } from 'common/assets/icons/collpase.svg';
13
+ import { ReactComponent as TrashIcon } from 'common/assets/icons/trash.svg';
14
+
15
+ import { ReactComponent as DownloadIcon } from 'common/assets/icons/download.svg';
16
+ import { ReactComponent as IconInfo } from 'common/assets/icons/info-tooltip.svg';
17
+
18
+ import { useQuery } from 'hooks/useQuery';
19
+ import {
20
+ loadingActionResults,
21
+ reset,
22
+ setSearchResults,
23
+ updateResultChangePosition,
24
+ updateStatusLoading,
25
+ } from 'Store/search/Search';
26
+ import { useHistory } from 'react-router-dom';
27
+ import { createImage, find } from 'services/image';
28
+ import { debounce, isEmpty, isUndefined } from 'lodash';
29
+ import useRequestStore from 'Store/requestStore';
30
+ import CameraCustom from './drawer/cameraCustom';
31
+ import useFilteredRegions from 'hooks/useFilteredRegions';
32
+ import useResultStore from 'Store/resultStore';
33
+ import { useDropzone } from 'react-dropzone';
34
+ import { useImageSearch } from 'hooks/useImageSearch';
35
+ import { compressImage } from 'utils';
36
+ import React from 'react';
37
+
38
+ function ImagePreviewComponent({
39
+ showAdjustInfo = false,
40
+ isExpanded,
41
+ isCameraUploadEnabled = true,
42
+ }: {
43
+ requestImage?: any;
44
+ imageSelection?: any;
45
+ filteredRegions?: any;
46
+ showAdjustInfo?: any;
47
+ isCameraUploadEnabled?: boolean;
48
+ isExpanded?: boolean;
49
+ }) {
50
+ const [showAdjustInfoBasedOnConfidence, setShowAdjustInfoBasedOnConfidence] =
51
+ useState(false);
52
+
53
+ const { t } = useTranslation();
54
+ const settings = useAppSelector(state => state.settings);
55
+ const preFilter = useAppSelector(state => state.search.preFilter);
56
+ const isAlgoliaEnabled = settings.algolia?.enabled;
57
+ const isMultiImageSearchEnabled = settings.multiImageSearch;
58
+
59
+ const query = useQuery();
60
+ const dispatch = useAppDispatch();
61
+ const history = useHistory();
62
+
63
+ const {
64
+ setRequestImages,
65
+ resetRegions,
66
+ requestImages,
67
+ addRequestImage,
68
+ regions,
69
+ updateRegion,
70
+ imageRegions,
71
+ } = useRequestStore(state => ({
72
+ requestImages: state.requestImages,
73
+ regions: state.regions,
74
+ addRequestImage: state.addRequestImage,
75
+ updateRegion: state.updateRegion,
76
+ imageRegions: state.regions,
77
+ resetRegions: state.resetRegions,
78
+ setRequestImages: state.setRequestImages,
79
+ }));
80
+
81
+ const { detectedObject } = useResultStore(state => ({
82
+ detectedObject: state.detectedObject,
83
+ }));
84
+
85
+ const { multiImageSearch, singleImageSearch } = useImageSearch();
86
+
87
+ const [showCamera, setShowCamera] = useState(false);
88
+ const [currentIndex, setCurrentIndex] = useState(requestImages.length - 1);
89
+ const [isVisible, setIsVisible] = useState(true);
90
+ const [zIndex, setZIndex] = useState<number>(0);
91
+
92
+ const previewWrapperRef = useRef<any>(null);
93
+
94
+ const searchQuery = query.get('query') || '';
95
+
96
+ const onImageRemove = () => {
97
+ dispatch(reset(''));
98
+ resetRegions();
99
+ setRequestImages([]);
100
+ if (!searchQuery) {
101
+ history.push('/');
102
+ }
103
+
104
+ if (!isAlgoliaEnabled) {
105
+ let payload: any;
106
+ let filters: any[] = [];
107
+ const preFilterValues = [
108
+ {
109
+ key: settings.visualSearchFilterKey,
110
+ values: Object.keys(preFilter),
111
+ },
112
+ ];
113
+ if (searchQuery || requestImages.length > 0) {
114
+ dispatch(updateStatusLoading(true));
115
+ find({
116
+ settings,
117
+ filters: !isEmpty(preFilter) ? preFilterValues : undefined,
118
+ text: searchQuery,
119
+ })
120
+ .then((res: any) => {
121
+ res?.results.forEach((item: any) => {
122
+ filters.push({
123
+ sku: item.sku,
124
+ score: item.score,
125
+ });
126
+ });
127
+ payload = {
128
+ ...res,
129
+ filters,
130
+ };
131
+
132
+ dispatch(setSearchResults(payload));
133
+ dispatch(updateStatusLoading(false));
134
+ })
135
+ .catch((e: any) => {
136
+ dispatch(updateStatusLoading(false));
137
+ });
138
+ } else {
139
+ dispatch(setSearchResults([]));
140
+ }
141
+ }
142
+ };
143
+
144
+ const handleExpand = () => {
145
+ if (isVisible) {
146
+ setTimeout(() => {
147
+ setZIndex(-1);
148
+ }, 300);
149
+ } else {
150
+ setZIndex(0);
151
+ }
152
+ setIsVisible(s => !s);
153
+ };
154
+
155
+ const filteredRegions = useFilteredRegions(
156
+ detectedObject[currentIndex],
157
+ regions[currentIndex],
158
+ );
159
+
160
+ const { getInputProps } = useDropzone({
161
+ onDrop: async (fs: File[]) => {
162
+ if (!fs[0]) return;
163
+
164
+ dispatch(updateStatusLoading(true));
165
+ dispatch(loadingActionResults());
166
+
167
+ const compressedBase64 = await compressImage(fs[0]);
168
+ let image = await createImage(compressedBase64);
169
+
170
+ multiImageSearch({
171
+ images: [...requestImages, image],
172
+ regions: regions,
173
+ settings,
174
+ }).then(() => {
175
+ dispatch(updateStatusLoading(false));
176
+ });
177
+ addRequestImage(image);
178
+ },
179
+ });
180
+
181
+ // eslint-disable-next-line react-hooks/exhaustive-deps
182
+ const findItemsInSelection = useCallback(
183
+ debounce(async (r: RectCoords, image: HTMLCanvasElement) => {
184
+ dispatch(updateStatusLoading(true));
185
+ singleImageSearch({
186
+ image: image,
187
+ settings,
188
+ imageRegion: r,
189
+ showFeedback: true,
190
+ }).then((res: any) => {
191
+ dispatch(updateStatusLoading(false));
192
+
193
+ dispatch(updateResultChangePosition(res));
194
+ const highConfidence = res.results.find(
195
+ (data: { score: number }) => data.score >= 0.65,
196
+ );
197
+ if (!highConfidence) {
198
+ setShowAdjustInfoBasedOnConfidence(true);
199
+ }
200
+ setTimeout(() => {
201
+ setShowAdjustInfoBasedOnConfidence(false);
202
+ }, 2000);
203
+ });
204
+ return;
205
+ }, 250),
206
+ [dispatch, settings, singleImageSearch],
207
+ );
208
+
209
+ const multiImageSearchOnRegionChange = useCallback(
210
+ (r: RectCoords, index: number) => {
211
+ dispatch(updateStatusLoading(true));
212
+ dispatch(loadingActionResults());
213
+ let modifiedRegions = [...imageRegions];
214
+ modifiedRegions[index] = r;
215
+ multiImageSearch({
216
+ images: requestImages,
217
+ regions: modifiedRegions,
218
+ settings,
219
+ }).then(() => {
220
+ dispatch(updateStatusLoading(false));
221
+ });
222
+ },
223
+ [dispatch, imageRegions, multiImageSearch, requestImages, settings],
224
+ );
225
+
226
+ // eslint-disable-next-line react-hooks/exhaustive-deps
227
+ const debouncedOnImageSelectionChange = useCallback(
228
+ debounce((r: RectCoords, index?: number) => {
229
+ if (requestImages.length > 1 && !isUndefined(index)) {
230
+ updateRegion(r, index);
231
+ multiImageSearchOnRegionChange(r, index);
232
+ } else {
233
+ updateRegion(r, 0);
234
+ findItemsInSelection(r, requestImages[0]);
235
+ }
236
+ }, 50),
237
+ [
238
+ findItemsInSelection,
239
+ multiImageSearchOnRegionChange,
240
+ requestImages,
241
+ updateRegion,
242
+ ],
243
+ );
244
+
245
+ const [editActive, setEditActive] = useState(false);
246
+ return (
247
+ <>
248
+ {/* Image preview Desktop, To-do: Remove and use same code as Image preview for Mobile */}
249
+ <div
250
+ ref={previewWrapperRef}
251
+ className={cx([
252
+ 'bg-primary',
253
+ 'justify-center',
254
+ isVisible ? (!isMultiImageSearchEnabled ? 'py-5' : 'pt-6') : '',
255
+ 'px-7',
256
+ 'w-full',
257
+ 'desktop:px-5',
258
+ 'relative',
259
+ 'hidden',
260
+ 'desktop:flex',
261
+ ])}
262
+ // style={{
263
+ // height: '100%',
264
+ // maxHeight: isVisible ? '800px' : 0,
265
+ // overflow: 'hidden',
266
+ // zIndex: zIndex,
267
+ // }}
268
+ style={{
269
+ // transform: isVisible ? 'translateY(0)' : `translateY(-100%)`,
270
+ // opacity: isVisible ? 1 : 0,
271
+ marginTop: isVisible ? '0px' : 'calc(-100% + 30px)',
272
+ transition: isVisible ? 'margin-top 0.4s linear' : '',
273
+ zIndex: zIndex,
274
+ }}
275
+ >
276
+ <div className="w-full bg-[#55566b] aspect-square flex just items-center">
277
+ <Preview
278
+ onSelectionChange={(r: RectCoords) => {
279
+ debouncedOnImageSelectionChange(r, currentIndex);
280
+ }}
281
+ image={requestImages[currentIndex]}
282
+ selection={regions[currentIndex] || DEFAULT_REGION}
283
+ regions={filteredRegions || []}
284
+ dotColor={'#FBD914'}
285
+ minCropWidth={30}
286
+ minCropHeight={30}
287
+ rounded={true}
288
+ />
289
+ </div>
290
+ {(showAdjustInfoBasedOnConfidence || showAdjustInfo) && (
291
+ <div
292
+ style={{
293
+ backgroundColor: '#3E36DC',
294
+ display: 'flex',
295
+ columnGap: '6px',
296
+ padding: '5px',
297
+ width: 'fit-content',
298
+ marginTop: 'auto',
299
+ position: 'absolute',
300
+ bottom: -20,
301
+ borderRadius: '16px',
302
+ zIndex: 1000,
303
+ height: 'fit-content',
304
+ }}
305
+ >
306
+ <IconInfo color="white" />
307
+ <p
308
+ style={{
309
+ fontSize: 12,
310
+ color: '#fff',
311
+ paddingRight: '4px',
312
+ }}
313
+ >
314
+ {showAdjustInfo
315
+ ? t('Crop the image for better results')
316
+ : 'Crop the image for better results'}
317
+ </p>
318
+ </div>
319
+ )}
320
+ <div
321
+ onClick={() => onImageRemove()}
322
+ className={`absolute left-2 top-2 flex justify-center items-center cursor-pointer`}
323
+ >
324
+ <div className="rounded-full bg-white/50 hover:bg-white w-6 h-6 flex justify-center items-center">
325
+ <TrashIcon className="text-primary" />
326
+ </div>
327
+ </div>
328
+ </div>
329
+ {/* Image preview Mobile*/}
330
+ <div
331
+ className={cx([
332
+ 'bg-primary',
333
+ 'justify-center',
334
+ 'p-5',
335
+ !editActive ? 'py-2' : '',
336
+ 'w-full',
337
+ 'relative',
338
+ 'flex',
339
+ 'desktop:hidden',
340
+ ])}
341
+ >
342
+ <div
343
+ className={`w-full ${
344
+ editActive ? 'bg-[#55566b] ' : ''
345
+ } flex just items-center`}
346
+ >
347
+ <Preview
348
+ onSelectionChange={(r: RectCoords) => {
349
+ debouncedOnImageSelectionChange(r, currentIndex);
350
+ }}
351
+ image={requestImages[currentIndex]}
352
+ selection={regions[currentIndex] || DEFAULT_REGION}
353
+ regions={filteredRegions || []}
354
+ minWidth={Math.min(
355
+ 80 *
356
+ (requestImages[currentIndex]?.width /
357
+ requestImages[currentIndex]?.height),
358
+ 200,
359
+ )}
360
+ minHeight={80}
361
+ dotColor={editActive ? '#FBD914' : ''}
362
+ minCropWidth={editActive ? 30 : 5}
363
+ minCropHeight={editActive ? 30 : 5}
364
+ rounded={false}
365
+ expandAnimation={editActive}
366
+ shrinkAnimation={!editActive}
367
+ onExpand={() => {
368
+ setEditActive(true);
369
+ }}
370
+ showGrip={editActive}
371
+ draggable={editActive ? true : false}
372
+ />
373
+ </div>
374
+ {(showAdjustInfoBasedOnConfidence || showAdjustInfo) && (
375
+ <div
376
+ style={{
377
+ backgroundColor: '#3E36DC',
378
+ display: 'flex',
379
+ columnGap: '6px',
380
+ padding: '5px',
381
+ width: 'fit-content',
382
+ marginTop: 'auto',
383
+ position: 'absolute',
384
+ bottom: -16,
385
+ borderRadius: '16px',
386
+ zIndex: 1000,
387
+ height: 'fit-content',
388
+ }}
389
+ >
390
+ <IconInfo color="white" />
391
+ <p
392
+ style={{
393
+ fontSize: 12,
394
+ color: '#fff',
395
+ paddingRight: '4px',
396
+ }}
397
+ >
398
+ {showAdjustInfo
399
+ ? t('Crop the image for better results')
400
+ : 'Crop the image for better results'}
401
+ </p>
402
+ </div>
403
+ )}
404
+ <div
405
+ onClick={() => setEditActive(s => !s)}
406
+ className={`absolute right-1 ${
407
+ editActive ? 'bottom-1' : 'bottom-8'
408
+ } flex justify-center items-center desktop:hidden p-1`}
409
+ >
410
+ <div className="rounded-full bg-white w-6 h-6 flex justify-center items-center desktop:hidden">
411
+ {editActive && <CollapseIcon className="text-primary" />}
412
+ {!editActive && <CropIcon className="text-primary" />}
413
+ </div>
414
+ </div>
415
+
416
+ <div
417
+ onClick={() => onImageRemove()}
418
+ className={`absolute left-1 ${
419
+ editActive ? 'top-1' : 'top-8'
420
+ } flex justify-center items-center desktop:hidden p-1`}
421
+ >
422
+ <div className="rounded-full bg-white w-6 h-6 flex justify-center items-center desktop:hidden">
423
+ <TrashIcon className="text-primary" />
424
+ </div>
425
+ </div>
426
+ </div>
427
+
428
+ <div className={cx([!isMultiImageSearchEnabled && 'hidden'])}>
429
+ <div
430
+ className={cx([
431
+ 'flex',
432
+ 'items-center',
433
+ 'gap-x-2',
434
+ 'h-28',
435
+ 'w-full',
436
+ 'bg-primary',
437
+ 'justify-center',
438
+ 'relative',
439
+ 'py-4',
440
+ 'desktop:pt-7 desktop:pb-5',
441
+ ])}
442
+ >
443
+ {requestImages.map((image, index) => {
444
+ return (
445
+ <div
446
+ key={index}
447
+ className={cx([
448
+ 'rounded-md',
449
+ 'relative',
450
+ 'p-1',
451
+ currentIndex === index && isVisible
452
+ ? ' bg-white'
453
+ : 'bg-transparent',
454
+ ])}
455
+ >
456
+ <img
457
+ className={cx([
458
+ 'w-[70px]',
459
+ 'h-[70px]',
460
+ 'desktop:w-[52px]',
461
+ 'desktop:h-[52px]',
462
+ 'rounded-md',
463
+ 'object-cover',
464
+ 'shadow-inner',
465
+ ])}
466
+ src={image.toDataURL()}
467
+ alt=""
468
+ onClick={() => {
469
+ setCurrentIndex(index);
470
+ if (!isVisible) {
471
+ handleExpand();
472
+ }
473
+ }}
474
+ />
475
+ {currentIndex === index && (
476
+ <div
477
+ className={cx([
478
+ 'absolute',
479
+ 'w-[70px]',
480
+ 'h-[70px]',
481
+ 'desktop:w-[52px]',
482
+ 'desktop:h-[52px]',
483
+ 'rounded-md',
484
+ 'top-1',
485
+ 'bg-black/15',
486
+ ])}
487
+ onClick={() => {
488
+ setCurrentIndex(index);
489
+ if (!isVisible) {
490
+ handleExpand();
491
+ }
492
+ }}
493
+ />
494
+ )}
495
+ </div>
496
+ );
497
+ })}
498
+ {requestImages.length < 3 && isMultiImageSearchEnabled && (
499
+ <label
500
+ className={cx([
501
+ 'w-[70px]',
502
+ 'h-[70px]',
503
+ 'desktop:w-[52px]',
504
+ 'desktop:h-[52px]',
505
+ 'bg-[#55566B]/50',
506
+ 'flex',
507
+ 'justify-center',
508
+ 'items-center',
509
+ 'border',
510
+ 'border-dashed',
511
+ 'border-[#AAABB5]',
512
+ 'rounded-md',
513
+ 'cursor-pointer',
514
+ ])}
515
+ onClick={() => {
516
+ if (isCameraUploadEnabled) {
517
+ setShowCamera(true);
518
+ }
519
+ }}
520
+ htmlFor={isCameraUploadEnabled ? '' : 'icon-add-image'}
521
+ >
522
+ <input
523
+ accept="image/*"
524
+ id="icon-add-image"
525
+ type="file"
526
+ style={{ display: 'none' }}
527
+ {...getInputProps({
528
+ onClick: e => {
529
+ e.currentTarget.value = '';
530
+ e.stopPropagation();
531
+ },
532
+ })}
533
+ />
534
+ <PlusIcon className={cx(['text-[#AAABB5] desktop:hidden'])} />
535
+ <div className="hidden desktop:block">
536
+ <DownloadIcon className={cx(['text-[#AAABB5]'])} />
537
+ </div>
538
+ </label>
539
+ )}
540
+ <div
541
+ onClick={() => handleExpand()}
542
+ className="absolute right-5 flex justify-center items-center desktop:hidden p-2"
543
+ >
544
+ <div className="rounded-full bg-white w-6 h-6 flex justify-center items-center desktop:hidden">
545
+ <CropIcon className="text-primary" />
546
+ </div>
547
+ </div>
548
+
549
+ <CameraCustom
550
+ show={showCamera}
551
+ onClose={() => setShowCamera(false)}
552
+ />
553
+ </div>
554
+ {requestImages.length < 3 && isMultiImageSearchEnabled && (
555
+ <p className="text-[10px] pb-4 w-full text-center bg-primary text-white -mt-[1px]">
556
+ Add up to three photos for a more accurate visual search.
557
+ </p>
558
+ )}
559
+ </div>
560
+ </>
561
+ );
562
+ }
563
+ const ImagePreview = React.memo(ImagePreviewComponent);
564
+ export default ImagePreview;
@@ -67,7 +67,7 @@ function InquiryBanner({
67
67
  <div>
68
68
  <img
69
69
  src={getCroppedCanvas(
70
- requestImage?.canvas,
70
+ requestImage,
71
71
  selectedRegion,
72
72
  )?.toDataURL()}
73
73
  alt="request_image"
@@ -70,9 +70,9 @@ export default function InquiryModal({
70
70
 
71
71
  const handleInquiry = async (e: { preventDefault: () => void }) => {
72
72
  e.preventDefault();
73
- const { canvas }: any = requestImage || {};
74
- const croppedImage = canvas
75
- ? getCroppedCanvas(canvas, selectedRegion)
73
+
74
+ const croppedImage = requestImage
75
+ ? getCroppedCanvas(requestImage, selectedRegion)
76
76
  : null;
77
77
  const serviceId = 'service_zfsxshi';
78
78
  setIsInquiryModalOpen(false);
@@ -200,10 +200,7 @@ export default function InquiryModal({
200
200
  }}
201
201
  >
202
202
  <img
203
- src={getCroppedCanvas(
204
- requestImage?.canvas,
205
- selectedRegion,
206
- )?.toDataURL()}
203
+ src={getCroppedCanvas(requestImage, selectedRegion)?.toDataURL()}
207
204
  alt="request_image"
208
205
  style={{ maxHeight: '200px' }}
209
206
  />
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'components/common';
2
- import React, { memo, useEffect, useState } from 'react';
2
+ import React, { memo, useEffect } from 'react';
3
3
  import { useMediaQuery } from 'react-responsive';
4
4
  import { useHistory } from 'react-router-dom';
5
5
  import {
@@ -11,11 +11,9 @@ import { useAppDispatch, useAppSelector } from 'Store/Store';
11
11
  import { AppState } from '../types';
12
12
  import './appMobile.scss';
13
13
  import './common.scss';
14
- import FooterMobile from './FooterMobile';
15
14
  import HeaderMobile from './HeaderMobile';
16
15
  import Header from './Header';
17
- import { isUndefined } from 'lodash';
18
- import AppMobile from './AppMobile';
16
+ import MobileLayout from './MobileLayout';
19
17
  import jQuery from 'jquery';
20
18
  import Loading from './Loading';
21
19
  import i18n from 'i18next';
@@ -26,6 +24,8 @@ import InstantSearchProvider from './Provider/InstantSearchProvider';
26
24
  import PoweredByNyris from './PoweredByNyris';
27
25
  import { useQuery } from 'hooks/useQuery';
28
26
 
27
+ import packageJson from '../../package.json';
28
+
29
29
  declare var psol: any;
30
30
 
31
31
  jQuery(document).ready(function () {
@@ -65,7 +65,6 @@ function Layout({ children }: ReactNode): JSX.Element {
65
65
  const { settings, search } = useAppSelector<AppState>((state: any) => state);
66
66
  const { loadingSearchAlgolia } = search;
67
67
  const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
68
- const [isOpenFilter, setOpenFilter] = useState<boolean>(false);
69
68
  const history = useHistory();
70
69
  const query = useQuery();
71
70
  const searchQuery = query.get('query') || '';
@@ -96,11 +95,9 @@ function Layout({ children }: ReactNode): JSX.Element {
96
95
  }, [user, dispatch, settings.shouldUseUserMetadata]);
97
96
 
98
97
  let HeaderApp: any;
99
- let FooterApp: any;
100
98
  let classNameBoxVersion: string = 'newVersion';
101
99
  if (isMobile) {
102
100
  classNameBoxVersion = 'mobile';
103
- FooterApp = FooterMobile;
104
101
  HeaderApp = HeaderMobile;
105
102
  } else {
106
103
  HeaderApp = Header;
@@ -112,6 +109,8 @@ function Layout({ children }: ReactNode): JSX.Element {
112
109
  document.documentElement.style.setProperty('--vh', `${vh}px`);
113
110
 
114
111
  useEffect(() => {
112
+ console.log('App version:', packageJson.version);
113
+
115
114
  const handleResize = () => {
116
115
  let vh = window.innerHeight * 0.01;
117
116
  document.documentElement.style.setProperty('--vh', `${vh}px`);
@@ -142,7 +141,7 @@ function Layout({ children }: ReactNode): JSX.Element {
142
141
  </div>
143
142
  )}
144
143
  <InstantSearchProvider>
145
- {isMobile && showApp && <AppMobile>{children}</AppMobile>}
144
+ {isMobile && showApp && <MobileLayout>{children}</MobileLayout>}
146
145
  {!isMobile && showApp && (
147
146
  <div className={`layout-main-${classNameBoxVersion}`}>
148
147
  <div
@@ -159,21 +158,13 @@ function Layout({ children }: ReactNode): JSX.Element {
159
158
  : {}),
160
159
  }}
161
160
  >
162
- <HeaderApp
163
- onToggleFilterMobile={(show: boolean) => {
164
- setOpenFilter(isUndefined(show) ? !isOpenFilter : show);
165
- }}
166
- />
161
+ <HeaderApp />
167
162
  </div>
168
163
 
169
164
  <div className={`box-body-${classNameBoxVersion}-wrap-main`}>
170
165
  {children}
171
166
  </div>
172
- {isMobile && (
173
- <div className="footer-wrap-main">
174
- <FooterApp />
175
- </div>
176
- )}
167
+
177
168
  {showPoweredByNyris && <PoweredByNyris />}
178
169
  </div>
179
170
  )}