@nyris/nyris-webapp 0.3.91 → 0.3.93

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 (107) hide show
  1. package/build/asset-manifest.json +6 -6
  2. package/build/data/related-parts.json +83 -0
  3. package/build/index.html +1 -1
  4. package/build/js/settings.example.js +3 -0
  5. package/build/static/css/main.fed38059.css +4 -0
  6. package/build/static/css/main.fed38059.css.map +1 -0
  7. package/build/static/js/main.c9bbbcb9.js +3 -0
  8. package/build/static/js/{main.f2255597.js.map → main.c9bbbcb9.js.map} +1 -1
  9. package/package.json +4 -3
  10. package/public/data/related-parts.json +83 -0
  11. package/public/js/settings.example.js +3 -0
  12. package/src/App.test.tsx +0 -1
  13. package/src/App.tsx +0 -1
  14. package/src/assets/arrow_down_expanded.svg +3 -0
  15. package/src/assets/arrow_enter.svg +3 -0
  16. package/src/assets/camera.svg +3 -0
  17. package/src/assets/close.svg +3 -0
  18. package/src/assets/enter.svg +3 -0
  19. package/src/assets/refresh.svg +3 -0
  20. package/src/assets/vizo_avatar.svg +16 -0
  21. package/src/components/Cadenas/CadenasWebViewer.tsx +1 -1
  22. package/src/components/Cart.tsx +48 -36
  23. package/src/components/ChatAssistant/ChatAssistant.tsx +289 -0
  24. package/src/components/ChatAssistant/MobileChatAssistant.tsx +291 -0
  25. package/src/components/ChatAssistant/OptionChip.tsx +78 -0
  26. package/src/components/ChatAssistant/index.ts +3 -0
  27. package/src/components/ChatAssistant/useChatAssistantLogic.ts +745 -0
  28. package/src/components/CurrentRefinements.tsx +2 -2
  29. package/src/components/CustomCameraDrawer.tsx +56 -13
  30. package/src/components/DragDropFile.tsx +5 -5
  31. package/src/components/ExperienceVisualSearch/ExperienceVisualSearch.tsx +1 -1
  32. package/src/components/Header.tsx +116 -96
  33. package/src/components/Hint.tsx +1 -2
  34. package/src/components/HitsPerPage.tsx +9 -3
  35. package/src/components/IdentifiedAttributes.tsx +159 -0
  36. package/src/components/ImagePreview.tsx +32 -17
  37. package/src/components/ImageUpload.tsx +16 -8
  38. package/src/components/Inquiry/InquiryBanner.tsx +1 -1
  39. package/src/components/Inquiry/InquiryModal.tsx +35 -29
  40. package/src/components/ItemSpecification.tsx +58 -126
  41. package/src/components/LocationInfoPopup.tsx +33 -33
  42. package/src/components/MatchNotificationBanner.tsx +90 -36
  43. package/src/components/PostFilter/PostFilter.tsx +1 -1
  44. package/src/components/PostFilter/PostFilterComponent.tsx +0 -1
  45. package/src/components/PostFilter/PostFilterFindApi.tsx +0 -1
  46. package/src/components/PoweredBy.tsx +1 -1
  47. package/src/components/PreFilter/PreFilter.tsx +14 -3
  48. package/src/components/PreFilter/PreFilterModal.tsx +0 -1
  49. package/src/components/Product/Product.tsx +15 -11
  50. package/src/components/Product/ProductAttribute.tsx +4 -5
  51. package/src/components/Product/ProductDetailViewModal.tsx +2 -4
  52. package/src/components/Product/ProductList.tsx +26 -13
  53. package/src/components/Rfq/RfqModal.tsx +1 -1
  54. package/src/components/SidePanel.tsx +128 -46
  55. package/src/components/SmartFilter.tsx +320 -0
  56. package/src/components/TextSearch.tsx +134 -70
  57. package/src/components/UploadDisclaimer.tsx +1 -1
  58. package/src/hooks/useBadResultsRecovery.ts +407 -0
  59. package/src/hooks/useEffectiveGroundingResults.ts +54 -0
  60. package/src/hooks/useGoodResultsChat.ts +651 -0
  61. package/src/hooks/useGroundedSearch.ts +88 -0
  62. package/src/hooks/useImageSearch.ts +139 -187
  63. package/src/hooks/useResultEvaluator.ts +417 -0
  64. package/src/index.css +1 -1
  65. package/src/index.tsx +0 -1
  66. package/src/layouts/AppLayout.tsx +53 -2
  67. package/src/pages/Home.tsx +11 -52
  68. package/src/pages/Login.tsx +1 -2
  69. package/src/pages/Logout.tsx +1 -1
  70. package/src/pages/Result.tsx +254 -200
  71. package/src/providers/AuthProvider.tsx +0 -1
  72. package/src/services/Feedback.ts +1 -1
  73. package/src/services/visualSearch.ts +0 -21
  74. package/src/services/vizo.ts +192 -4
  75. package/src/stores/chat/chatStore.ts +150 -0
  76. package/src/stores/chat/conversationStore.ts +300 -0
  77. package/src/stores/request/Misc/misc.slice.ts +2 -2
  78. package/src/stores/request/filter/filter.slice.ts +8 -8
  79. package/src/stores/request/query/query.slice.ts +2 -2
  80. package/src/stores/request/requestImage/requestImage.slice.ts +6 -6
  81. package/src/stores/request/specifications/specifications.slice.ts +10 -7
  82. package/src/stores/result/detectedRegions/detectedRegions.slice.ts +1 -1
  83. package/src/stores/result/prodcuts/products.initialState.ts +12 -0
  84. package/src/stores/result/prodcuts/products.slice.ts +28 -8
  85. package/src/stores/result/session/session.slice.ts +2 -2
  86. package/src/stores/smartFilters/smartFiltersStore.ts +270 -0
  87. package/src/stores/types.ts +41 -0
  88. package/src/stores/ui/ai/ai.initialState.ts +5 -0
  89. package/src/stores/ui/ai/ai.slice.ts +15 -0
  90. package/src/stores/ui/banner/banner.initialState.ts +6 -0
  91. package/src/stores/ui/banner/banner.slice.ts +14 -0
  92. package/src/stores/ui/feedback/feedback.slice.ts +1 -1
  93. package/src/stores/ui/loading/loading.slice.ts +4 -4
  94. package/src/stores/ui/uiStore.ts +7 -1
  95. package/src/styles/product.scss +0 -2
  96. package/src/types.ts +4 -8
  97. package/src/utils/cropImageToBase64.ts +32 -0
  98. package/src/utils/fetchProductImage.ts +109 -0
  99. package/src/utils/imageConverters.ts +124 -0
  100. package/src/utils/relatedParts.ts +35 -0
  101. package/src/utils/specificationFilter.ts +1 -5
  102. package/tailwind.config.js +3 -2
  103. package/build/static/css/main.734b52e1.css +0 -4
  104. package/build/static/css/main.734b52e1.css.map +0 -1
  105. package/build/static/js/main.f2255597.js +0 -3
  106. package/src/utils/addAssets.ts +0 -40
  107. /package/build/static/js/{main.f2255597.js.LICENSE.txt → main.c9bbbcb9.js.LICENSE.txt} +0 -0
@@ -108,7 +108,7 @@ function ImagePreviewComponent({
108
108
  compress: false,
109
109
  }).then((res: any) => {
110
110
  const specificationPrefilter =
111
- res.image_analysis?.specification?.prefilter_value || null;
111
+ res?.image_analysis?.specification?.prefilter_value || null;
112
112
  const hasPrefilter = preFilterList.filter((filter: any) =>
113
113
  filter.values.includes(specificationPrefilter),
114
114
  );
@@ -188,7 +188,7 @@ function ImagePreviewComponent({
188
188
 
189
189
  // eslint-disable-next-line react-hooks/exhaustive-deps
190
190
  const debouncedOnImageSelectionChange = useCallback(
191
- debounce((r: RectCoords, index?: number) => {
191
+ debounce((r: RectCoords, _index?: number) => {
192
192
  findItemsInSelection(r, requestImages[0]);
193
193
  }, 0),
194
194
  [findItemsInSelection, requestImages, updateRegion],
@@ -200,7 +200,7 @@ function ImagePreviewComponent({
200
200
  <div
201
201
  ref={previewWrapperRef}
202
202
  className={twMerge([
203
- 'bg-primary',
203
+ 'bg-[#545987]',
204
204
  'justify-center',
205
205
  isVisible ? (!isMultiImageSearchEnabled ? 'py-5' : 'pt-6') : '',
206
206
  'px-7',
@@ -211,6 +211,7 @@ function ImagePreviewComponent({
211
211
  'hidden',
212
212
  'desktop:flex',
213
213
  // 'rounded',
214
+ 'rounded-[24px]',
214
215
  ])}
215
216
  // style={{
216
217
  // height: '100%',
@@ -228,9 +229,9 @@ function ImagePreviewComponent({
228
229
  >
229
230
  <div
230
231
  className={twMerge([
231
- // 'w-[243px]',
232
232
  'w-full',
233
- 'bg-[#55566b] aspect-square flex just items-center',
233
+ 'bg-[#545987] aspect-square flex just items-center',
234
+ 'rounded-[24px]',
234
235
  ])}
235
236
  >
236
237
  <Preview
@@ -295,7 +296,7 @@ function ImagePreviewComponent({
295
296
  {/* Image preview Mobile*/}
296
297
  <div
297
298
  className={twMerge([
298
- 'bg-primary',
299
+ 'bg-[#545987]',
299
300
  'justify-center',
300
301
  'p-5',
301
302
  !editActive ? 'py-2' : '',
@@ -303,11 +304,13 @@ function ImagePreviewComponent({
303
304
  'relative',
304
305
  'flex',
305
306
  'desktop:hidden',
307
+ !editActive && 'rounded-[32px]',
308
+ editActive && 'rounded-[24px]',
306
309
  ])}
307
310
  >
308
311
  <div
309
312
  className={`w-full ${
310
- editActive ? 'bg-[#55566b] ' : ''
313
+ editActive ? 'bg-[#545987] ' : ''
311
314
  } flex just items-center`}
312
315
  >
313
316
  <Preview
@@ -318,12 +321,12 @@ function ImagePreviewComponent({
318
321
  selection={regions[currentIndex] || DEFAULT_REGION}
319
322
  regions={detectedRegions[currentIndex] || []}
320
323
  minWidth={Math.min(
321
- 80 *
324
+ 40 *
322
325
  (requestImages[currentIndex]?.width /
323
326
  requestImages[currentIndex]?.height),
324
327
  200,
325
328
  )}
326
- minHeight={80}
329
+ minHeight={40}
327
330
  dotColor={editActive ? '#FBD914' : ''}
328
331
  minCropWidth={editActive ? 30 : 5}
329
332
  minCropHeight={editActive ? 30 : 5}
@@ -370,26 +373,38 @@ function ImagePreviewComponent({
370
373
  )}
371
374
  <div
372
375
  onClick={() => setEditActive(s => !s)}
373
- className={`absolute right-1 ${
374
- editActive ? 'bottom-1' : 'bottom-8'
375
- } flex justify-center items-center desktop:hidden p-1`}
376
+ className={twMerge(
377
+ `absolute right-1 ${
378
+ editActive ? 'bottom-1' : 'bottom-1'
379
+ } flex justify-center items-center desktop:hidden p-1`,
380
+ )}
376
381
  >
377
- <div className="rounded-full bg-white w-6 h-6 flex justify-center items-center desktop:hidden">
382
+ <div
383
+ className={twMerge(
384
+ 'rounded-full w-6 h-6 flex justify-center items-center desktop:hidden',
385
+ editActive ? 'w-8 h-8 bg-white' : 'w-10 h-10 bg-transparent',
386
+ )}
387
+ >
378
388
  {editActive && <Icon name="collapse" className="text-primary" />}
379
- {!editActive && <Icon name="crop" className="text-primary" />}
389
+ {!editActive && <Icon name="crop" className="text-white" />}
380
390
  </div>
381
391
  </div>
382
392
 
383
393
  <div
384
394
  onClick={() => onImageRemove()}
385
395
  className={`absolute left-1 ${
386
- editActive ? 'top-1' : 'top-8'
396
+ editActive ? 'top-1' : 'top-1'
387
397
  } flex justify-center items-center desktop:hidden p-1`}
388
398
  >
389
- <div className="rounded-full bg-white w-6 h-6 flex justify-center items-center desktop:hidden">
399
+ <div
400
+ className={twMerge(
401
+ 'rounded-full bg-[#FFE5EF] flex justify-center items-center desktop:hidden',
402
+ editActive ? 'w-8 h-8' : 'w-10 h-10',
403
+ )}
404
+ >
390
405
  <Icon
391
406
  name="trash"
392
- className="text-primary"
407
+ className="text-[#E31B5D]"
393
408
  width={14}
394
409
  height={14}
395
410
  />
@@ -12,7 +12,13 @@ import { useCadSearch } from 'hooks/useCadSearch';
12
12
  import { isCadFile } from '@nyris/nyris-api';
13
13
  import UploadDisclaimer from './UploadDisclaimer';
14
14
 
15
- function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
15
+ function ImageUpload({
16
+ onCameraClick,
17
+ disableDisclaimer,
18
+ }: {
19
+ onCameraClick?: () => void;
20
+ disableDisclaimer?: boolean;
21
+ }) {
16
22
  const settings = window.settings;
17
23
  const user = useAuth0().user;
18
24
 
@@ -66,12 +72,13 @@ function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
66
72
  ]);
67
73
 
68
74
  const showDisclaimerDisabled = useMemo(() => {
75
+ if (disableDisclaimer) return true;
69
76
  const disclaimer = localStorage.getItem('upload-disclaimer-suite');
70
77
  if (requestImages.length === 0) return true;
71
78
  if (!disclaimer) return false;
72
79
  return disclaimer === 'dont-show';
73
80
  // eslint-disable-next-line react-hooks/exhaustive-deps
74
- }, [showDisclaimer, requestImages]);
81
+ }, [showDisclaimer, requestImages, disableDisclaimer]);
75
82
 
76
83
  const { singleImageSearch } = useImageSearch();
77
84
  const { cadSearch } = useCadSearch();
@@ -81,7 +88,7 @@ function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
81
88
  setQuery('');
82
89
 
83
90
  if (isCadFile(files[0])) {
84
- cadSearch({ file: files[0], settings, newSearch: true }).then(res => {});
91
+ cadSearch({ file: files[0], settings, newSearch: true }).then(() => {});
85
92
 
86
93
  return;
87
94
  }
@@ -191,14 +198,15 @@ function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
191
198
  <Tooltip content={t('Search with an image')}>
192
199
  <label
193
200
  className={twMerge(
194
- 'mr-2 desktop:mr-1',
195
- 'w-10 h-10 flex justify-center items-center cursor-pointer rounded-full bg-gray-100 desktop:bg-transparent hover:bg-gray-100',
201
+ 'min-w-10 min-h-10 flex justify-center items-center cursor-pointer rounded-full bg-gray-100 desktop:bg-transparent hover:bg-gray-100',
196
202
  location.pathname === '/result' && 'desktop:w-8 desktop:h-8',
203
+ location.pathname !== '/result' &&
204
+ 'desktop:bg-[#FAFAFA] desktop:hover:bg-[#FAFAFA]',
197
205
  )}
198
206
  htmlFor={
199
207
  showDisclaimerDisabled && !onCameraClick ? 'icon-button-file' : ''
200
208
  }
201
- onClick={e => {
209
+ onClick={() => {
202
210
  if (!showDisclaimerDisabled) {
203
211
  // disclaimer
204
212
  setShowDisclaimer(true);
@@ -207,12 +215,12 @@ function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
207
215
  }
208
216
  }}
209
217
  >
210
- <Icon name="camera_simple" width={16} height={16} fill="#2B2C46" />
218
+ <Icon name="camera_ai" width={16} height={16} fill="#3B3E5F" />
211
219
  </label>
212
220
  </Tooltip>
213
221
  </div>
214
222
 
215
- {showDisclaimer && (
223
+ {showDisclaimer && !disableDisclaimer && (
216
224
  <UploadDisclaimer
217
225
  onClose={() => {
218
226
  setShowDisclaimer(false);
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import { useEffect, useState } from 'react';
2
2
  import { useMediaQuery } from 'react-responsive';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Icon } from '@nyris/nyris-react-components';
@@ -66,20 +66,22 @@ export default function InquiryModal({
66
66
  useEffect(() => {
67
67
  function omitKeys(obj: any, keys: string[]) {
68
68
  return Object.fromEntries(
69
- Object.entries(obj).filter(([key]) => !keys.includes(key))
69
+ Object.entries(obj).filter(([key]) => !keys.includes(key)),
70
70
  );
71
71
  }
72
- const omittedSpecification = omitKeys(specifications, ['is_nameplate', 'prefilter_value']);
72
+ const omittedSpecification = omitKeys(specifications, [
73
+ 'is_nameplate',
74
+ 'prefilter_value',
75
+ ]);
73
76
  setInformation(
74
- Object
75
- .entries(omittedSpecification)
76
- .reduce((acc, [k, v]) =>
77
- v == null || (typeof v === 'string' && v.trim() === '')
78
- ? acc
79
- : `${acc}${k}: ${v}\n`,
80
- ''
81
- )
82
- );
77
+ Object.entries(omittedSpecification).reduce(
78
+ (acc, [k, v]) =>
79
+ v == null || (typeof v === 'string' && v.trim() === '')
80
+ ? acc
81
+ : `${acc}${k}: ${v}\n`,
82
+ '',
83
+ ),
84
+ );
83
85
  }, [specifications]);
84
86
 
85
87
  useEffect(() => emailjs.init('SMGihPnuEGcYLm0V4'), []);
@@ -94,32 +96,36 @@ export default function InquiryModal({
94
96
  const croppedImage = requestImage
95
97
  ? getCroppedCanvas(requestImage, selectedRegion)
96
98
  : null;
97
- const nameplateImageCanvas = nameplateImage ? await createImage(nameplateImage) : null;
98
-
99
+ const nameplateImageCanvas = nameplateImage
100
+ ? await createImage(nameplateImage)
101
+ : null;
102
+
99
103
  const serviceId = 'service_zfsxshi';
100
104
  setIsInquiryModalOpen(false);
101
105
  const templateId = settings.support?.emailTemplateId;
102
106
  if (templateId) {
103
- const body = !specifications?.is_nameplate ? {
104
- email_id: email.trim(),
105
- information_text: information ? information : '<not specified>',
106
- request_image: croppedImage?.toDataURL(),
107
- prefilter_values: preFilterValues?.length
108
- ? preFilterValues.join(', ')
109
- : '<not specified>',
110
- } : {
111
- email_id: email.trim(),
112
- information_text: information,
113
- request_image: croppedImage?.toDataURL() || '',
114
- typeplate_image: nameplateImageCanvas?.toDataURL() || '',
115
- prefilter_values: specifications.prefilter_value,
116
- };
107
+ const body = !specifications?.is_nameplate
108
+ ? {
109
+ email_id: email.trim(),
110
+ information_text: information ? information : '<not specified>',
111
+ request_image: croppedImage?.toDataURL(),
112
+ prefilter_values: preFilterValues?.length
113
+ ? preFilterValues.join(', ')
114
+ : '<not specified>',
115
+ }
116
+ : {
117
+ email_id: email.trim(),
118
+ information_text: information,
119
+ request_image: croppedImage?.toDataURL() || '',
120
+ typeplate_image: nameplateImageCanvas?.toDataURL() || '',
121
+ prefilter_values: specifications.prefilter_value,
122
+ };
117
123
  try {
118
124
  await emailjs.send(serviceId, templateId, body);
119
125
  ToastHelper.success(t('Request sent successfully'));
120
126
  } catch (error) {
121
127
  toast(
122
- t => {
128
+ () => {
123
129
  return (
124
130
  <div
125
131
  style={{
@@ -174,7 +180,7 @@ export default function InquiryModal({
174
180
  return (
175
181
  <Dialog
176
182
  open={isInquiryModalOpen}
177
- onOpenChange={(e: any) => {
183
+ onOpenChange={() => {
178
184
  setIsInquiryModalOpen(false);
179
185
  }}
180
186
  >
@@ -4,7 +4,6 @@ import { twMerge } from 'tailwind-merge';
4
4
  import useRequestStore from '../stores/request/requestStore';
5
5
  import useResultStore from '../stores/result/resultStore';
6
6
  import { Icon } from '@nyris/nyris-react-components';
7
- import { useMediaQuery } from 'react-responsive';
8
7
 
9
8
  const ItemSpecification = ({
10
9
  attr,
@@ -16,7 +15,6 @@ const ItemSpecification = ({
16
15
  return ref.header.toLocaleLowerCase() === attr.toLocaleLowerCase();
17
16
  });
18
17
  const attribute = refinement?.length ? refinement[0].attribute : 'none';
19
- const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
20
18
  const { refine } = useRefinementList({
21
19
  attribute,
22
20
  operator: 'or',
@@ -24,140 +22,74 @@ const ItemSpecification = ({
24
22
  limit: 1,
25
23
  });
26
24
 
27
- return isMobile ? (
28
- <>
25
+ return (
26
+ <Tooltip
27
+ content={
28
+ specificationFilter[attr]
29
+ ? 'Filter applied. Clear to choose a different value.'
30
+ : 'Click to apply as a search filter.'
31
+ }
32
+ delayDuration={1000}
33
+ disabled={!value}
34
+ >
29
35
  <div
30
- className="inline-flex flex-col justify-center items-start "
31
- key={attr}
36
+ className={twMerge(
37
+ `px-3.5 py-2 bg-[#E7E8F1] rounded-lg flex justify-center items-center gap-1.5`,
38
+ 'border border-solid border-transparent hover:border-[#E7E8F1]',
39
+ 'cursor-pointer',
40
+ 'w-fit',
41
+ specificationFilter[attr]
42
+ ? ' bg-[#545987] '
43
+ : 'hover:shadow-[0_0_6px_0_#DDDEE7]',
44
+ )}
45
+ onClick={() => {
46
+ if (!value) {
47
+ return;
48
+ }
49
+ if (attribute !== 'none') {
50
+ refine(value);
51
+ } else {
52
+ const setSpecificationFilter =
53
+ useRequestStore.getState().setSpecificationFilter;
54
+
55
+ const setSpecificationFilteredProducts =
56
+ useResultStore.getState().setSpecificationFilteredProducts;
57
+
58
+ if (specificationFilter[attr]) {
59
+ setSpecificationFilter({});
60
+ setSpecificationFilteredProducts([]);
61
+ // setProducts(results);
62
+ } else {
63
+ setSpecificationFilter({
64
+ [attr]: value,
65
+ });
66
+ }
67
+ }
68
+ }}
32
69
  >
33
- <div className="pl-1 inline-flex justify-center items-center gap-2.5">
34
- <div className="justify-start text-[#3B3E5F] text-sm font-semibold">
35
- {attr}
36
- </div>
37
- </div>
38
70
  <div
39
71
  className={twMerge(
40
- `p-3 bg-[#e4e3ff] rounded-lg inline-flex justify-center items-center gap-1.5`,
41
- 'text-[#3e36dc]',
72
+ 'justify-start text-[#767A9F] text-sm leading-none px-0.5',
73
+ 'font-normal',
42
74
  specificationFilter[attr]
43
- ? 'border-[#3E36DC] bg-[#3E36DC] text-white'
44
- : '',
75
+ ? 'text-white hover:px-0.5'
76
+ : 'hover:font-semibold',
77
+ 'leading-4',
78
+ 'flex gap-2 items-center',
45
79
  )}
46
- onClick={() => {
47
- if (!value) {
48
- return;
49
- }
50
- if (attribute !== 'none') {
51
- refine(value);
52
- } else {
53
- const setSpecificationFilter =
54
- useRequestStore.getState().setSpecificationFilter;
55
-
56
- const setSpecificationFilteredProducts =
57
- useResultStore.getState().setSpecificationFilteredProducts;
58
-
59
- if (specificationFilter[attr]) {
60
- setSpecificationFilter({});
61
- setSpecificationFilteredProducts([]);
62
- // setProducts(results);
63
- } else {
64
- setSpecificationFilter({
65
- [attr]: value,
66
- });
67
- }
68
- }
69
- }}
70
80
  >
71
- <div className="justify-start text-sm font-medium leading-none flex gap-2">
72
- {imageAnalysis?.specification[attr] || 'N/A'}
73
- <div>
74
- <Icon
75
- name="close"
76
- className={twMerge(
77
- 'w-3 h-3 text-white',
78
- specificationFilter[attr] ? 'block' : 'hidden',
79
- )}
80
- />
81
- </div>
82
- </div>
83
- </div>
84
- </div>
85
- </>
86
- ) : (
87
- <div key={attr} className="flex justify-between w-full gap-2 items-center">
88
- <div className="self-stretch inline-flex justify-start items-center gap-1.5">
89
- <div className="justify-start text-[#3B3E5F] text-xs font-semibold">
90
- {attr}:
91
- </div>
92
- <Tooltip
93
- content={
94
- specificationFilter[attr]
95
- ? 'Filter applied. Clear to choose a different value.'
96
- : 'Click to apply as a search filter.'
97
- }
98
- delayDuration={1000}
99
- disabled={!value}
100
- >
101
- <div
81
+ {imageAnalysis?.specification[attr] || 'N/A'}
82
+
83
+ <Icon
84
+ name="close"
102
85
  className={twMerge(
103
- `px-1 py-1 bg-[#e4e3ff] rounded-[1px] flex justify-center items-center gap-1.5`,
104
- 'border border-solid border-transparent hover:border-[#3E36DC]',
105
- 'cursor-pointer',
106
- specificationFilter[attr] ? 'border-[#3E36DC] bg-[#3E36DC] ' : '',
86
+ 'w-[10px] h-[10px] text-white',
87
+ specificationFilter[attr] ? 'block' : 'hidden',
107
88
  )}
108
- onClick={() => {
109
- if (!value) {
110
- return;
111
- }
112
- if (attribute !== 'none') {
113
- refine(value);
114
- } else {
115
- const setSpecificationFilter =
116
- useRequestStore.getState().setSpecificationFilter;
117
-
118
- const setSpecificationFilteredProducts =
119
- useResultStore.getState().setSpecificationFilteredProducts;
120
-
121
- if (specificationFilter[attr]) {
122
- setSpecificationFilter({});
123
- setSpecificationFilteredProducts([]);
124
- // setProducts(results);
125
- } else {
126
- setSpecificationFilter({
127
- [attr]: value,
128
- });
129
- }
130
- }
131
- }}
132
- >
133
- <div
134
- className={twMerge(
135
- 'justify-start text-[#3e36dc] text-[10px] leading-none px-0.5',
136
- 'font-normal hover:font-bold hover:px-0',
137
- specificationFilter[attr]
138
- ? 'font-bold text-white hover:px-0.5'
139
- : '',
140
- 'max-line-1',
141
- )}
142
- >
143
- {imageAnalysis?.specification[attr] || 'N/A'}
144
- </div>
145
- </div>
146
- </Tooltip>
147
- </div>
148
- <div
149
- onClick={() => {
150
- navigator.clipboard.writeText(
151
- imageAnalysis?.specification[attr] || '',
152
- );
153
- }}
154
- >
155
- <Icon
156
- name="copy"
157
- className="text-[#AAABB5] w-[12px] h-[12px] hover:text-[#3E36DC] cursor-pointer"
158
- />
89
+ />
90
+ </div>
159
91
  </div>
160
- </div>
92
+ </Tooltip>
161
93
  );
162
94
  };
163
95
 
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import { useEffect, useState } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { createPortal } from 'react-dom';
4
4
 
@@ -10,49 +10,49 @@ const LocationInfoPopup = () => {
10
10
  const shown = sessionStorage.getItem('locationNoticeShown');
11
11
  if (shown) return;
12
12
 
13
- navigator.permissions
14
- .query({ name: 'geolocation' })
15
- .then((result) => {
16
- if (result.state === 'prompt') {
17
- setShowPopup(true);
18
- } else if (result.state === 'granted') {
19
- navigator.geolocation.getCurrentPosition(console.log, console.error);
20
- sessionStorage.setItem('locationNoticeShown', 'true');
21
- } else {
22
- sessionStorage.setItem('locationNoticeShown', 'true');
23
- }
24
- });
13
+ navigator.permissions.query({ name: 'geolocation' }).then(result => {
14
+ if (result.state === 'prompt') {
15
+ setShowPopup(true);
16
+ } else if (result.state === 'granted') {
17
+ navigator.geolocation.getCurrentPosition(console.log, console.error);
18
+ sessionStorage.setItem('locationNoticeShown', 'true');
19
+ } else {
20
+ sessionStorage.setItem('locationNoticeShown', 'true');
21
+ }
22
+ });
25
23
  }, []);
26
24
 
27
25
  const closePopup = () => {
28
26
  navigator.geolocation.getCurrentPosition(console.log, console.error);
29
27
  sessionStorage.setItem('locationNoticeShown', 'true');
30
28
  setShowPopup(false);
31
- }
32
-
29
+ };
30
+
33
31
  return (
34
32
  <div>
35
- {showPopup && createPortal(
36
- <div
37
- className="custom-modal"
38
- onClick={closePopup}
39
- >
40
- <div
41
- className="custom-modal-body geolocation"
42
- onClick={(e) => {
33
+ {showPopup &&
34
+ createPortal(
35
+ <div className="custom-modal" onClick={closePopup}>
36
+ <div
37
+ className="custom-modal-body geolocation"
38
+ onClick={e => {
43
39
  e.preventDefault();
44
40
  e.stopPropagation();
45
- }}
46
- >
47
- <div className="geolocation-title">{t('Please enable location services when prompted.')}</div>
41
+ }}
42
+ >
43
+ <div className="geolocation-title">
44
+ {t('Please enable location services when prompted.')}
45
+ </div>
48
46
  <div>{window.settings.geoLocationMessage}</div>
49
- <button type="button" onClick={closePopup}>{t('I understand')}</button>
50
- </div>
51
- </div>,
52
- document.body
53
- )}
47
+ <button type="button" onClick={closePopup}>
48
+ {t('I understand')}
49
+ </button>
50
+ </div>
51
+ </div>,
52
+ document.body,
53
+ )}
54
54
  </div>
55
- )
56
- }
55
+ );
56
+ };
57
57
 
58
58
  export default LocationInfoPopup;