@nyris/nyris-webapp 0.3.90 → 0.3.92

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 (106) 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.5ea01690.css +4 -0
  6. package/build/static/css/main.5ea01690.css.map +1 -0
  7. package/build/static/js/main.36b77705.js +3 -0
  8. package/build/static/js/{main.cede3ae1.js.map → main.36b77705.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/ImagePreview.tsx +32 -17
  36. package/src/components/ImageUpload.tsx +16 -8
  37. package/src/components/Inquiry/InquiryBanner.tsx +1 -1
  38. package/src/components/Inquiry/InquiryModal.tsx +35 -29
  39. package/src/components/ItemSpecification.tsx +58 -126
  40. package/src/components/LocationInfoPopup.tsx +33 -33
  41. package/src/components/MatchNotificationBanner.tsx +90 -36
  42. package/src/components/PostFilter/PostFilter.tsx +1 -1
  43. package/src/components/PostFilter/PostFilterComponent.tsx +0 -1
  44. package/src/components/PostFilter/PostFilterFindApi.tsx +0 -1
  45. package/src/components/PoweredBy.tsx +1 -1
  46. package/src/components/PreFilter/PreFilter.tsx +14 -3
  47. package/src/components/PreFilter/PreFilterModal.tsx +0 -1
  48. package/src/components/Product/Product.tsx +15 -11
  49. package/src/components/Product/ProductAttribute.tsx +4 -5
  50. package/src/components/Product/ProductDetailViewModal.tsx +2 -4
  51. package/src/components/Product/ProductList.tsx +26 -13
  52. package/src/components/Rfq/RfqModal.tsx +1 -1
  53. package/src/components/SidePanel.tsx +124 -91
  54. package/src/components/SmartFilter.tsx +320 -0
  55. package/src/components/TextSearch.tsx +134 -70
  56. package/src/components/UploadDisclaimer.tsx +1 -1
  57. package/src/hooks/useBadResultsRecovery.ts +407 -0
  58. package/src/hooks/useEffectiveGroundingResults.ts +54 -0
  59. package/src/hooks/useGoodResultsChat.ts +651 -0
  60. package/src/hooks/useGroundedSearch.ts +88 -0
  61. package/src/hooks/useImageSearch.ts +139 -187
  62. package/src/hooks/useResultEvaluator.ts +417 -0
  63. package/src/index.css +1 -1
  64. package/src/index.tsx +0 -1
  65. package/src/layouts/AppLayout.tsx +53 -2
  66. package/src/pages/Home.tsx +11 -52
  67. package/src/pages/Login.tsx +1 -2
  68. package/src/pages/Logout.tsx +1 -1
  69. package/src/pages/Result.tsx +198 -200
  70. package/src/providers/AuthProvider.tsx +0 -1
  71. package/src/services/Feedback.ts +1 -1
  72. package/src/services/visualSearch.ts +0 -21
  73. package/src/services/vizo.ts +192 -4
  74. package/src/stores/chat/chatStore.ts +150 -0
  75. package/src/stores/chat/conversationStore.ts +300 -0
  76. package/src/stores/request/Misc/misc.slice.ts +2 -2
  77. package/src/stores/request/filter/filter.slice.ts +8 -8
  78. package/src/stores/request/query/query.slice.ts +2 -2
  79. package/src/stores/request/requestImage/requestImage.slice.ts +6 -6
  80. package/src/stores/request/specifications/specifications.slice.ts +10 -7
  81. package/src/stores/result/detectedRegions/detectedRegions.slice.ts +1 -1
  82. package/src/stores/result/prodcuts/products.initialState.ts +12 -0
  83. package/src/stores/result/prodcuts/products.slice.ts +28 -8
  84. package/src/stores/result/session/session.slice.ts +2 -2
  85. package/src/stores/smartFilters/smartFiltersStore.ts +270 -0
  86. package/src/stores/types.ts +41 -0
  87. package/src/stores/ui/ai/ai.initialState.ts +5 -0
  88. package/src/stores/ui/ai/ai.slice.ts +15 -0
  89. package/src/stores/ui/banner/banner.initialState.ts +6 -0
  90. package/src/stores/ui/banner/banner.slice.ts +14 -0
  91. package/src/stores/ui/feedback/feedback.slice.ts +1 -1
  92. package/src/stores/ui/loading/loading.slice.ts +4 -4
  93. package/src/stores/ui/uiStore.ts +7 -1
  94. package/src/styles/product.scss +0 -2
  95. package/src/types.ts +3 -7
  96. package/src/utils/cropImageToBase64.ts +32 -0
  97. package/src/utils/fetchProductImage.ts +109 -0
  98. package/src/utils/imageConverters.ts +124 -0
  99. package/src/utils/relatedParts.ts +35 -0
  100. package/src/utils/specificationFilter.ts +1 -5
  101. package/tailwind.config.js +3 -2
  102. package/build/static/css/main.734b52e1.css +0 -4
  103. package/build/static/css/main.734b52e1.css.map +0 -1
  104. package/build/static/js/main.cede3ae1.js +0 -3
  105. package/src/utils/addAssets.ts +0 -40
  106. /package/build/static/js/{main.cede3ae1.js.LICENSE.txt → main.36b77705.js.LICENSE.txt} +0 -0
@@ -1,25 +1,31 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
1
+ import { useEffectiveGroundingResults } from 'hooks/useEffectiveGroundingResults';
2
+ import { useGroundedSearch } from 'hooks/useGroundedSearch';
3
+ import React, { useEffect, useMemo } from 'react';
2
4
  import { find } from 'services/visualSearch';
3
5
  import useRequestStore from 'stores/request/requestStore';
4
6
  import useResultStore from 'stores/result/resultStore';
7
+ import useUiStore from 'stores/ui/uiStore';
5
8
  import { twMerge } from 'tailwind-merge';
6
9
 
7
10
  const MatchNotificationBanner = ({ className }: { className?: string }) => {
8
- const [goBack, setGoBack] = useState(false);
9
- const [textSearch, setTextSearch] = useState<
10
- 'loading' | 'resolved' | 'pending'
11
- >('pending');
11
+ const goBack = useUiStore(state => state.bannerGoBack);
12
+ const setGoBack = useUiStore(state => state.setBannerGoBack);
13
+ const textSearch = useUiStore(state => state.bannerTextSearch);
14
+ const setTextSearch = useUiStore(state => state.setBannerTextSearch);
12
15
 
13
- const [prevResult, setPrevResult] = useState<any[]>([]);
16
+ const [prevResult, setPrevResult] = React.useState<any[]>([]);
14
17
 
15
18
  const productsFromFindApi = useResultStore(
16
19
  state => state.productsFromFindApi,
17
20
  );
21
+ const groundingError = useResultStore(state => state.groundingError);
18
22
 
19
23
  const groundingFilterResult = useResultStore(
20
24
  state => state.groundingFilterResult,
21
25
  );
22
26
 
27
+ const { groundedSearch } = useGroundedSearch();
28
+
23
29
  const showBanner = useMemo(() => {
24
30
  if (groundingFilterResult.length !== productsFromFindApi.length) {
25
31
  return true;
@@ -36,11 +42,6 @@ const MatchNotificationBanner = ({ className }: { className?: string }) => {
36
42
  return false;
37
43
  }, [groundingFilterResult, productsFromFindApi]);
38
44
 
39
- useEffect(() => {
40
- setGoBack(false);
41
- setTextSearch('pending');
42
- }, []);
43
-
44
45
  useEffect(() => {
45
46
  if (textSearch === 'pending') {
46
47
  setPrevResult(productsFromFindApi || []);
@@ -54,10 +55,46 @@ const MatchNotificationBanner = ({ className }: { className?: string }) => {
54
55
  state => state.showingGroundingFilterResult,
55
56
  );
56
57
 
58
+ const effectiveGroundingResults = useEffectiveGroundingResults();
59
+
57
60
  const groundingQuery = useRequestStore(state => state.groundingQuery);
58
61
  if (goBack) {
59
62
  return <></>;
60
63
  }
64
+
65
+ if (groundingError) {
66
+ return (
67
+ <div
68
+ className={twMerge([
69
+ 'flex items-center justify-between w-full bg-[#FFF8FB] rounded-[16px] p-2 pl-2 shadow-sm',
70
+ className,
71
+ ])}
72
+ >
73
+ {/* Left Section: Icon and Text */}
74
+ <div className="flex items-center gap-3">
75
+ <div className="h-2.5 w-2.5 rounded-full bg-[#E31B5D]" />
76
+ <p className="text-[#4a5568] text-sm md:text-base">
77
+ No results from web search.
78
+ </p>
79
+ </div>
80
+
81
+ {/* Right Section: Action Button */}
82
+ <button
83
+ className="bg-[#373b53] hover:bg-[#2d3145] text-white px-6 py-3 rounded-lg text-xs font-semibold transition-colors duration-200"
84
+ onClick={() => {
85
+ const requestImages = useRequestStore.getState().requestImages;
86
+ const requestImage = requestImages?.[0];
87
+ const findApiProducts =
88
+ useResultStore.getState().productsFromFindApi;
89
+ groundedSearch(requestImage, findApiProducts || []);
90
+ }}
91
+ >
92
+ Search again
93
+ </button>
94
+ </div>
95
+ );
96
+ }
97
+
61
98
  if (
62
99
  (!groundingFilterResult || groundingFilterResult?.length === 0) &&
63
100
  !groundingQuery
@@ -79,13 +116,13 @@ const MatchNotificationBanner = ({ className }: { className?: string }) => {
79
116
  <div className="h-2.5 w-2.5 rounded-full bg-[#10b981]" />
80
117
  {textSearch === 'resolved' && (
81
118
  <p className="text-[#4a5568] text-sm md:text-base">
82
- Didn't find the correct product?
119
+ Didn't find what you're looking for?
83
120
  </p>
84
121
  )}
85
122
  {textSearch !== 'resolved' && (
86
123
  <p className="text-[#4a5568] text-sm md:text-base">
87
- Are you looking for ?{' '}
88
- <span className="font-bold">{groundingQuery}</span>
124
+ Are you looking for
125
+ <span className="font-bold px-0.5">{groundingQuery}</span> ?
89
126
  </p>
90
127
  )}
91
128
  </div>
@@ -99,7 +136,7 @@ const MatchNotificationBanner = ({ className }: { className?: string }) => {
99
136
  useResultStore.getState().setFindApiProducts(prevResult);
100
137
  }}
101
138
  >
102
- Show all results
139
+ Show original results
103
140
  </button>
104
141
  )}
105
142
 
@@ -108,16 +145,22 @@ const MatchNotificationBanner = ({ className }: { className?: string }) => {
108
145
  className="bg-[#373b53] hover:bg-[#2d3145] text-white px-6 py-3 rounded-lg text-xs font-semibold transition-colors duration-200"
109
146
  onClick={() => {
110
147
  setTextSearch('loading');
148
+ useUiStore.getState().setIsFindApiLoading(true);
149
+
111
150
  find({
112
151
  settings: window.settings,
113
152
  text: groundingQuery,
114
- }).then(res => {
115
- useResultStore.getState().setFindApiProducts(res?.results);
116
- setTextSearch('resolved');
117
- });
153
+ })
154
+ .then(res => {
155
+ useResultStore.getState().setFindApiProducts(res?.results);
156
+ setTextSearch('resolved');
157
+ })
158
+ .finally(() => {
159
+ useUiStore.getState().setIsFindApiLoading(false);
160
+ });
118
161
  }}
119
162
  >
120
- Search now
163
+ Search catalog
121
164
  </button>
122
165
  )}
123
166
  </div>
@@ -131,7 +174,7 @@ const MatchNotificationBanner = ({ className }: { className?: string }) => {
131
174
  return (
132
175
  <div
133
176
  className={twMerge([
134
- 'flex items-center justify-between w-full bg-[#F2FBF3] rounded-[16px] p-2 pl-2 shadow-sm',
177
+ 'flex items-center justify-between w-full bg-[#F2FBF3] rounded-[16px] p-2 pl-2 shadow-sm min-h-[56px]',
135
178
  className,
136
179
  ])}
137
180
  >
@@ -139,25 +182,36 @@ const MatchNotificationBanner = ({ className }: { className?: string }) => {
139
182
  <div className="flex items-center gap-3">
140
183
  <div className="h-2.5 w-2.5 rounded-full bg-[#10b981]" />
141
184
  <p className="text-[#4a5568] text-sm md:text-base">
142
- {showingGroundingFilterResult ? 'Showing' : 'Found'}{' '}
143
- <span className="font-bold text-slate-900 pr-1">
144
- {groundingFilterResult?.length}
145
- </span>
146
- matching product in results
185
+ {effectiveGroundingResults.length === 0 && (
186
+ <span>Matched 0 results from Internet search</span>
187
+ )}
188
+ {effectiveGroundingResults.length > 0 && (
189
+ <>
190
+ {showingGroundingFilterResult ? 'Showing' : 'Found'}{' '}
191
+ <span className="font-bold text-slate-900 pr-0.5">
192
+ {effectiveGroundingResults.length}
193
+ </span>
194
+ {showingGroundingFilterResult
195
+ ? 'result from Internet search'
196
+ : ' matching result from Internet search'}
197
+ </>
198
+ )}
147
199
  </p>
148
200
  </div>
149
201
 
150
202
  {/* Right Section: Action Button */}
151
- <button
152
- className="bg-[#373b53] hover:bg-[#2d3145] text-white px-6 py-3 rounded-lg text-xs font-semibold transition-colors duration-200"
153
- onClick={() => {
154
- useResultStore.getState().setShowingGroundingFilterResult();
155
- }}
156
- >
157
- {showingGroundingFilterResult
158
- ? 'Show all results'
159
- : 'Show only matching results'}
160
- </button>
203
+ {effectiveGroundingResults.length > 0 && (
204
+ <button
205
+ className="bg-[#373b53] hover:bg-[#2d3145] text-white px-6 py-3 rounded-lg text-xs font-semibold transition-colors duration-200"
206
+ onClick={() => {
207
+ useResultStore.getState().setShowingGroundingFilterResult();
208
+ }}
209
+ >
210
+ {showingGroundingFilterResult
211
+ ? 'Show original results'
212
+ : 'Show matched results'}
213
+ </button>
214
+ )}
161
215
  </div>
162
216
  );
163
217
  };
@@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
11
11
  function PostFilter({
12
12
  attribute,
13
13
  label,
14
- searchable,
14
+ searchable: _searchable,
15
15
  onVisibilityChange,
16
16
  isLastVisible,
17
17
  }: {
@@ -53,7 +53,6 @@ function PostFilterComponent({ className }: { className?: string }) {
53
53
  return (
54
54
  <div
55
55
  className={twMerge([
56
- 'mt-4',
57
56
  'w-full',
58
57
  'px-4',
59
58
  'flex',
@@ -90,7 +90,6 @@ function PostFilterFindApi({
90
90
  }),
91
91
  );
92
92
  }, [attribute, postFilterSelections, productsForFilters]);
93
- console.log({ filteredProductsForCounts });
94
93
 
95
94
  const valueCounts = useMemo(() => {
96
95
  const counts: Record<string, number> = {};
@@ -5,7 +5,7 @@ export default function PoweredBy({ className }: { className?: string }) {
5
5
  return (
6
6
  <div className={twMerge(className)}>
7
7
  <Icon
8
- className="fill-black group-hover:fill-[url(#powered_by_nyris_colored_svg__gradient)] hover:fill-[url(#powered_by_nyris_colored_svg__gradient)] cursor-pointer w-[110px] h-5"
8
+ className="fill-[#989BB7] group-hover:fill-[url(#powered_by_nyris_colored_svg__gradient)] hover:fill-[url(#powered_by_nyris_colored_svg__gradient)] cursor-pointer w-[110px] h-5"
9
9
  name="powered_by_nyris"
10
10
  onClick={() => {
11
11
  window.open('https://www.nyris.io', '_blank');
@@ -9,7 +9,6 @@ import useRequestStore from 'stores/request/requestStore';
9
9
  import { truncateString } from 'utils/truncateString';
10
10
  import { twMerge } from 'tailwind-merge';
11
11
  import Tooltip from 'components/Tooltip/TooltipComponent';
12
- import { useNavigate } from 'react-router';
13
12
  import useResultStore from '../../stores/result/resultStore';
14
13
 
15
14
  interface Props {
@@ -41,11 +40,11 @@ const PreFilterComponent = (props: Props) => {
41
40
  const specification = useRequestStore(state => state.specifications);
42
41
  const setSpecifications = useRequestStore(state => state.setSpecifications);
43
42
  const setImageAnalysis = useResultStore(state => state.setImageAnalysis);
43
+ const regions = useRequestStore(state => state.regions);
44
44
 
45
45
  const [keyFilter, setKeyFilter] = useState<Record<string, boolean>>(
46
46
  keyFilterState || {},
47
47
  );
48
- const navigate = useNavigate();
49
48
 
50
49
  const selectedFilter = useMemo(
51
50
  () =>
@@ -158,6 +157,18 @@ const PreFilterComponent = (props: Props) => {
158
157
  }
159
158
 
160
159
  handleClose();
160
+ const query = useRequestStore.getState().query;
161
+ if (!window.settings?.algolia.enabled && requestImages.length === 0) {
162
+ singleImageSearch({
163
+ image: requestImages[0],
164
+ imageRegion: regions[0],
165
+ text: query,
166
+ settings,
167
+ showFeedback: false,
168
+ newSearch: false,
169
+ preFilterParams: preFilter,
170
+ });
171
+ }
161
172
 
162
173
  if (requestImages.length === 0) {
163
174
  return;
@@ -170,7 +181,7 @@ const PreFilterComponent = (props: Props) => {
170
181
  imageRegion: imageRegions[0],
171
182
  preFilterParams: preFilter,
172
183
  compress: false,
173
- }).then(res => {});
184
+ }).then(() => {});
174
185
  } else {
175
186
  }
176
187
  };
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { Dialog, DialogContent, DialogTitle } from '../Modal/Dialog';
3
2
  import PreFilterComponent from './PreFilter';
4
3
  import { useMediaQuery } from 'react-responsive';
@@ -80,7 +80,7 @@ function Product(props: Props) {
80
80
  img.src = url;
81
81
  };
82
82
 
83
- const handlerToggleModal = (item: any) => {
83
+ const handlerToggleModal = (_item?: any) => {
84
84
  setOpenDetailedView('image');
85
85
  };
86
86
 
@@ -123,10 +123,18 @@ function Product(props: Props) {
123
123
  onSearchImage={onSearchImage}
124
124
  openDetailedView={openDetailedView}
125
125
  setOpenDetailedView={setOpenDetailedView}
126
- main_image_link={main_image_link}
127
126
  onAddToCart={onAddToCart}
128
127
  />
129
- <div className="wrap-main-item-result max-w-[190px] w-[180px] desktop:w-[190px] border border-solid border-[#E0E0E0] scroll-pt-5">
128
+ <div
129
+ className={twMerge(
130
+ 'wrap-main-item-result',
131
+ 'max-w-[190px] w-[180px] desktop:w-[190px]',
132
+ 'border border-solid border-[#E7E8F1]',
133
+ 'scroll-pt-5',
134
+ 'shadow-ds-1',
135
+ 'rounded-2xl',
136
+ )}
137
+ >
130
138
  <div className="relative h-fit">
131
139
  {!isHover && main_image_link && !settings.noSimilarSearch && (
132
140
  <div
@@ -434,7 +442,7 @@ function Product(props: Props) {
434
442
  ? 8
435
443
  : 0,
436
444
  gridGap: 8,
437
- color: '#2B2C46',
445
+ color: '#3B3E5F',
438
446
  }}
439
447
  >
440
448
  {settings.attributes?.attributeOneValue && (
@@ -445,7 +453,6 @@ function Product(props: Props) {
445
453
  settings.attributes?.attributeOneValue || '',
446
454
  )}
447
455
  padding={'4px 8px'}
448
- backgroundColor={'#E0E0E0'}
449
456
  isTitleVisible={
450
457
  !!settings.attributes?.attributeOneLabelValue
451
458
  }
@@ -459,7 +466,6 @@ function Product(props: Props) {
459
466
  settings.attributes?.attributeTwoValue || '',
460
467
  )}
461
468
  padding={'4px 8px'}
462
- backgroundColor={'#E0E0E0'}
463
469
  isTitleVisible={
464
470
  !!settings.attributes?.attributeTwoLabelValue
465
471
  }
@@ -473,7 +479,6 @@ function Product(props: Props) {
473
479
  settings.attributes?.attributeThreeValue || '',
474
480
  )}
475
481
  padding={'4px 8px'}
476
- backgroundColor={'#E0E0E0'}
477
482
  isTitleVisible={
478
483
  !!settings.attributes?.attributeThreeLabelValue
479
484
  }
@@ -488,7 +493,6 @@ function Product(props: Props) {
488
493
  settings.attributes?.attributeFourValue || '',
489
494
  )}
490
495
  padding={'4px 8px'}
491
- backgroundColor={'#E0E0E0'}
492
496
  isTitleVisible={
493
497
  !!settings.attributes?.attributeFourLabelValue
494
498
  }
@@ -507,7 +511,7 @@ function Product(props: Props) {
507
511
  background:
508
512
  settings.secondaryCTAButton?.secondaryCTAButtonColor ||
509
513
  '#2B2C46',
510
- borderRadius: 4,
514
+ borderRadius: 6,
511
515
  paddingLeft: '8px',
512
516
  marginBottom: settings.CTAButton?.CTAButton ? 8 : 0,
513
517
  display: 'flex',
@@ -591,7 +595,7 @@ function Product(props: Props) {
591
595
  background:
592
596
  settings.CTAButton?.CTAButtonColor ||
593
597
  settings.theme?.primaryColor,
594
- borderRadius: 4,
598
+ borderRadius: 6,
595
599
  paddingLeft: '8px',
596
600
  display: 'flex',
597
601
  justifyItems: 'center',
@@ -677,7 +681,7 @@ function Product(props: Props) {
677
681
  background:
678
682
  settings.CTAButton?.CTAButtonColor ||
679
683
  settings.theme?.primaryColor,
680
- borderRadius: 4,
684
+ borderRadius: 6,
681
685
  paddingLeft: '8px',
682
686
  display: 'flex',
683
687
  justifyItems: 'center',
@@ -1,5 +1,4 @@
1
1
  import Tooltip from 'components/Tooltip/TooltipComponent';
2
- import React from 'react';
3
2
 
4
3
  interface Props {
5
4
  title: any;
@@ -26,16 +25,16 @@ function ProductAttribute(props: Props) {
26
25
  display: 'flex',
27
26
  flexDirection: 'column',
28
27
  justifyContent: 'center',
29
- borderRadius: 2,
28
+ borderRadius: 6,
30
29
  height: '36px',
31
- backgroundColor: props.backgroundColor || '#E0E0E0',
30
+ backgroundColor: props.backgroundColor || '#F3F3F5',
32
31
  padding: padding,
33
32
  }}
34
33
  >
35
34
  {isTitleVisible ? (
36
35
  <div
37
36
  style={{
38
- color: '#2B2C46',
37
+ color: '#3B3E5F',
39
38
  overflow: 'hidden',
40
39
  textOverflow: 'ellipsis',
41
40
  whiteSpace: 'nowrap',
@@ -51,7 +50,7 @@ function ProductAttribute(props: Props) {
51
50
  <Tooltip content={value} disabled={!value} sideOffset={10}>
52
51
  <div
53
52
  style={{
54
- color: '#2B2C46',
53
+ color: '#3B3E5F',
55
54
  overflow: 'hidden',
56
55
  textOverflow: 'ellipsis',
57
56
  whiteSpace: 'nowrap',
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
  import { Dialog, DialogContent, DialogTitle } from 'components/Modal/Dialog';
3
3
  import ProductDetailView from './ProductDetailView';
4
4
 
@@ -8,7 +8,6 @@ function ProductDetailViewModal({
8
8
  dataItem,
9
9
  handlerFeedback,
10
10
  onSearchImage,
11
- main_image_link,
12
11
  onAddToCart,
13
12
  }: {
14
13
  openDetailedView: '3d' | 'image' | undefined;
@@ -16,7 +15,6 @@ function ProductDetailViewModal({
16
15
  dataItem: any;
17
16
  handlerFeedback: (value: string) => void;
18
17
  onSearchImage: (url: string) => void;
19
- main_image_link: string;
20
18
  onAddToCart: (item: any) => void;
21
19
  }) {
22
20
  useEffect(() => {
@@ -35,7 +33,7 @@ function ProductDetailViewModal({
35
33
  return (
36
34
  <Dialog
37
35
  open={openDetailedView === '3d' || openDetailedView === 'image'}
38
- onOpenChange={(e: any) => {
36
+ onOpenChange={() => {
39
37
  setOpenDetailedView(undefined);
40
38
  }}
41
39
  modal={true}
@@ -1,4 +1,4 @@
1
- import { useMemo } from 'react';
1
+ import { useEffect, useMemo, useState } from 'react';
2
2
  import useResultStore from 'stores/result/resultStore';
3
3
  import Product from './Product';
4
4
  import { useImageSearch } from 'hooks/useImageSearch';
@@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next';
8
8
  import { twMerge } from 'tailwind-merge';
9
9
  import { filterProducts } from 'utils/specificationFilter';
10
10
  import { filterProductsByText } from 'utils/textSearchFilter';
11
+ import { useEffectiveGroundingResults } from 'hooks/useEffectiveGroundingResults';
11
12
 
12
13
  interface Props {
13
14
  sendFeedBackAction?: any;
@@ -25,16 +26,17 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
25
26
  state => state.productsFromFindApi,
26
27
  );
27
28
 
28
- const groundingFilterResult = useResultStore(
29
- state => state.groundingFilterResult,
30
- );
31
-
32
29
  const showingGroundingFilterResult = useResultStore(
33
30
  state => state.showingGroundingFilterResult,
34
31
  );
35
32
 
33
+ const effectiveGroundingResults = useEffectiveGroundingResults();
34
+
36
35
  const isAlgoliaLoading = useUiStore(state => state.isAlgoliaLoading);
37
36
  const isFindApiLoading = useUiStore(state => state.isFindApiLoading);
37
+ const isAiModeOpen = useUiStore(state => state.isAiModeOpen);
38
+
39
+ const [products, setProducts] = useState<any[]>([]);
38
40
 
39
41
  const setQuery = useRequestStore(state => state.setQuery);
40
42
  const query = useRequestStore(state => state.query);
@@ -96,7 +98,8 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
96
98
  }).then(() => {});
97
99
  };
98
100
  const isAlgoliaEnabled = window.settings?.algolia?.enabled;
99
- const products = useMemo(() => {
101
+
102
+ const baseProducts = useMemo(() => {
100
103
  const filter = Object.values(specificationFilter || {})[0];
101
104
  const baseFindApiProducts = postFilteredProducts;
102
105
 
@@ -124,22 +127,27 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
124
127
  postFilteredProducts,
125
128
  ]);
126
129
 
130
+ useEffect(() => {
131
+ setProducts(baseProducts || []);
132
+ }, [baseProducts, specificationFilter]);
133
+
127
134
  const groundedFilteredProducts = useMemo(() => {
128
- if (groundingFilterResult && groundingFilterResult.length > 0) {
135
+ if (effectiveGroundingResults && effectiveGroundingResults.length > 0) {
129
136
  const filtered = products.filter((product: any) =>
130
- groundingFilterResult.some((result: any) => product.sku === result.sku),
137
+ effectiveGroundingResults.some(
138
+ (result: any) => product.sku === result.sku,
139
+ ),
131
140
  );
132
141
  if (filtered.length > 0) {
133
142
  return filtered;
134
143
  }
135
144
  return products;
136
145
  }
137
- return () => {};
138
- // eslint-disable-next-line react-hooks/exhaustive-deps
139
- }, [groundingFilterResult]);
146
+ return products;
147
+ }, [effectiveGroundingResults, products]);
140
148
 
141
149
  const renderItem = useMemo(() => {
142
- const productsToRender =
150
+ const productsToRender: any[] =
143
151
  groundedFilteredProducts &&
144
152
  groundedFilteredProducts.length > 0 &&
145
153
  showingGroundingFilterResult
@@ -185,7 +193,12 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
185
193
  </div>
186
194
  );
187
195
  // eslint-disable-next-line react-hooks/exhaustive-deps
188
- }, [products, groundedFilteredProducts, showingGroundingFilterResult]);
196
+ }, [
197
+ products,
198
+ groundedFilteredProducts,
199
+ showingGroundingFilterResult,
200
+ isAiModeOpen,
201
+ ]);
189
202
 
190
203
  if (
191
204
  products?.length === 0 &&
@@ -81,7 +81,7 @@ export default function RfqModal({
81
81
  setRfqStatus('inactive');
82
82
 
83
83
  toast(
84
- t => {
84
+ () => {
85
85
  return (
86
86
  <div
87
87
  style={{