@nyris/nyris-webapp 0.3.91 → 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.f2255597.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.f2255597.js +0 -3
  105. package/src/utils/addAssets.ts +0 -40
  106. /package/build/static/js/{main.f2255597.js.LICENSE.txt → main.36b77705.js.LICENSE.txt} +0 -0
@@ -4,22 +4,26 @@ import { twMerge } from 'tailwind-merge';
4
4
  import { useAuth0 } from '@auth0/auth0-react';
5
5
  import { useLocation, useNavigate } from 'react-router';
6
6
  import { useTranslation } from 'react-i18next';
7
-
8
7
  import { Icon } from '@nyris/nyris-react-components';
9
-
10
8
  import { useImageSearch } from 'hooks/useImageSearch';
11
9
  import PreFilterModal from './PreFilter/PreFilterModal';
12
10
  import useRequestStore from 'stores/request/requestStore';
13
11
  import Tooltip from './Tooltip/TooltipComponent';
14
12
  import { useMediaQuery } from 'react-responsive';
15
13
  import ImageUpload from './ImageUpload';
14
+ import useUiStore from 'stores/ui/uiStore';
15
+ import useConversationStore from 'stores/chat/conversationStore';
16
16
 
17
17
  function TextSearch({
18
18
  className,
19
19
  onCameraClick,
20
+ showImageUpload = true,
21
+ aiMode = false,
20
22
  }: {
21
23
  className?: string;
22
24
  onCameraClick?: () => void;
25
+ showImageUpload?: boolean;
26
+ aiMode?: boolean;
23
27
  }) {
24
28
  const settings = window.settings;
25
29
  const user = useAuth0().user;
@@ -32,6 +36,13 @@ function TextSearch({
32
36
  const location = useLocation();
33
37
  const navigate = useNavigate();
34
38
 
39
+ const toggleAiMode = useUiStore(state => state.toggleAiMode);
40
+ const isAiModeOpen = useUiStore(state => state.isAiModeOpen);
41
+ const hasAiNotification = useConversationStore(
42
+ state => state.hasNotification,
43
+ );
44
+ const setIsChatOpen = useConversationStore(state => state.setIsOpen);
45
+
35
46
  const preFilter = useRequestStore(state => state.preFilter);
36
47
  const requestImages = useRequestStore(state => state.requestImages);
37
48
  const setQuery = useRequestStore(state => state.setQuery);
@@ -50,6 +61,7 @@ function TextSearch({
50
61
 
51
62
  const [isOpenModalFilterDesktop, setToggleModalFilterDesktop] =
52
63
  useState<boolean>(false);
64
+ const [isInputFocused, setIsInputFocused] = useState(false);
53
65
 
54
66
  const showPreFilter = useMemo(() => {
55
67
  if (settings.shouldUseUserMetadata && user) {
@@ -114,27 +126,26 @@ function TextSearch({
114
126
  return (
115
127
  <div
116
128
  className={twMerge(
117
- 'w-[426px]',
129
+ 'w-[636px]',
118
130
  'h-12',
119
- location.pathname === '/result' && 'desktop:h-10',
131
+ location.pathname === '/result' && 'desktop:h-[52px]',
120
132
  className,
121
133
  )}
122
134
  >
123
135
  <div
124
136
  className={twMerge([
125
137
  'bg-white',
126
- 'desktop:bg-gray-200',
127
- 'desktop:border border-solid',
128
- 'desktop:border-gray-300',
129
- 'desktop:focus-within:bg-white',
130
- 'desktop:focus-within:shadow-[0px_0px_6px_rgba(202,202,209,1)]',
131
- 'shadow-[0px_0px_16px_0px_rgba(170,171,181,0.50)]',
132
- 'desktop:shadow-none',
138
+ location.pathname === '/result' &&
139
+ 'desktop:bg-[#E7E8F1] desktop:has-[input:focus]:bg-[#FFFFFF] desktop:has-[input:focus]:shadow-ds-2',
140
+ 'border border-solid',
141
+ 'border-[#DDDEE7]',
142
+ location.pathname !== '/result' && 'shadow-ds-2',
143
+ location.pathname === '/result' && 'shadow-ds-2 desktop:shadow-none',
133
144
  'flex',
134
145
  'h-full',
135
146
  'justify-between',
136
- 'p-0',
137
- 'rounded-3xl',
147
+ 'pr-1.5',
148
+ 'rounded-[32px]',
138
149
  'w-full',
139
150
  ])}
140
151
  >
@@ -154,15 +165,12 @@ function TextSearch({
154
165
  'flex',
155
166
  'py-2',
156
167
  'px-2',
157
- 'desktop:py-1',
158
- 'desktop:pl-1',
159
- 'pr-2',
168
+ 'desktop:py-1.5',
169
+ 'desktop:pl-1.5',
170
+ 'pr-1.5',
160
171
  'h-full',
161
172
  'justify-center',
162
173
  'items-center',
163
- 'desktop:border-r',
164
- 'border-solid',
165
- 'border-[#CACAD1]',
166
174
  ])}
167
175
  style={{
168
176
  cursor: showPreFilter ? 'pointer' : 'default',
@@ -183,11 +191,11 @@ function TextSearch({
183
191
  ref={iconRef}
184
192
  className={twMerge(
185
193
  'p-2 desktop:p-3',
186
- location.pathname === '/result' && 'desktop:p-2',
194
+ location.pathname === '/result' && 'desktop:p-3',
187
195
  `flex rounded-full bg-[#f3f3f5]`,
188
196
  !isEmpty(preFilter)
189
197
  ? 'desktop:bg-theme-primary'
190
- : 'desktop:bg-[#2B2C46]',
198
+ : 'desktop:bg-[#FAFAFA]',
191
199
  )}
192
200
  >
193
201
  <Icon
@@ -196,7 +204,7 @@ function TextSearch({
196
204
  !isEmpty(preFilter)
197
205
  ? 'fill-theme-primary'
198
206
  : 'fill-[#2B2C46]',
199
- `desktop:fill-white`,
207
+ `desktop:fill-[#3B3E5F]`,
200
208
  )}
201
209
  />
202
210
  </div>
@@ -374,62 +382,118 @@ function TextSearch({
374
382
  fontSize: 14,
375
383
  color: '#2B2C46',
376
384
  }}
377
- className="peer desktop:bg-gray-200 focus:bg-white pl-1.5 outline-none"
385
+ className={twMerge(
386
+ 'peer pl-1.5 outline-none',
387
+ 'bg-white',
388
+ location.pathname === '/result' &&
389
+ 'desktop:bg-[#E7E8F1] focus:bg-[#FFFFFF]',
390
+ )}
378
391
  placeholder={t('Search')}
379
392
  value={valueInput}
380
393
  onChange={onChangeText}
394
+ onFocus={() => setIsInputFocused(true)}
395
+ onBlur={() => setIsInputFocused(false)}
381
396
  ref={focusInp}
382
397
  />
383
398
  </div>
384
-
385
- {location.pathname === '/result' && (valueInput || query) && (
386
- <Tooltip content={t('Clear text search')}>
387
- <button
388
- className={twMerge([
389
- 'flex',
390
- 'justify-center',
391
- 'items-center',
392
- 'rounded-full',
393
- 'cursor-pointer',
394
- 'min-w-10 min-h-10',
395
- 'z-10',
396
- 'hover:bg-gray-100',
397
- 'mr-2',
398
- location.pathname === '/result' &&
399
- 'desktop:min-w-8 desktop:min-h-8',
400
- ])}
401
- onClick={() => {
402
- if (visualSearch) {
403
- navigate('/result');
404
- setValueInput('');
405
- setQuery('');
406
- if (
407
- !window.settings?.algolia.enabled &&
408
- requestImages.length === 0
409
- ) {
410
- singleImageSearch({
411
- image: requestImages[0],
412
- imageRegion: regions[0],
413
- text: '',
414
- settings,
415
- showFeedback: false,
416
- newSearch: false,
417
- });
399
+ <div className="flex gap-2">
400
+ {location.pathname === '/result' && (valueInput || query) && (
401
+ <Tooltip content={t('Clear text search')}>
402
+ <button
403
+ className={twMerge([
404
+ 'flex',
405
+ 'justify-center',
406
+ 'items-center',
407
+ 'rounded-full',
408
+ 'cursor-pointer',
409
+ 'min-w-10 min-h-10',
410
+ 'z-10',
411
+ 'hover:bg-gray-100',
412
+ location.pathname === '/result' &&
413
+ 'desktop:min-w-10 desktop:min-h-10',
414
+ ])}
415
+ onPointerDown={event => {
416
+ // Keep focus on input until click runs so mobile AI button doesn't pop in first.
417
+ event.preventDefault();
418
+ }}
419
+ onClick={() => {
420
+ if (visualSearch) {
421
+ navigate('/result');
422
+ setValueInput('');
423
+ setQuery('');
424
+ if (
425
+ !window.settings?.algolia.enabled &&
426
+ requestImages.length === 0
427
+ ) {
428
+ singleImageSearch({
429
+ image: requestImages[0],
430
+ imageRegion: regions[0],
431
+ text: '',
432
+ settings,
433
+ showFeedback: false,
434
+ newSearch: false,
435
+ });
436
+ }
437
+ return;
418
438
  }
419
- return;
420
- }
421
- setSpecifications(null);
422
- setQuery('');
423
- setValueInput('');
424
- navigate('/');
425
- }}
426
- >
427
- <Icon name="close" className="w-3 h-3 text-primary" />
428
- </button>
429
- </Tooltip>
430
- )}
439
+ setSpecifications(null);
440
+ setQuery('');
441
+ setValueInput('');
442
+ navigate('/');
443
+ }}
444
+ >
445
+ <Icon name="close" className="w-3 h-3 text-primary" />
446
+ </button>
447
+ </Tooltip>
448
+ )}
449
+ {showImageUpload && <ImageUpload onCameraClick={onCameraClick} />}
450
+ {aiMode &&
451
+ window.settings.vizo?.chat &&
452
+ requestImages.length !== 0 &&
453
+ !(isMobile && isInputFocused) && (
454
+ <div className="relative">
455
+ <button
456
+ type="button"
457
+ className={twMerge(
458
+ 'px-[10px] h-10 rounded-full text-[14px] text-[#989BB7] flex items-center justify-center gap-2 whitespace-nowrap bg-[#FAFAFA] border border-solid border-transparent',
459
+ !isAiModeOpen &&
460
+ 'hover:bg-gradient-to-r hover:from-white hover:to-[#E4E3FF]',
461
+ !isAiModeOpen &&
462
+ 'hover:border-[#DDDEE7] hover:text-[#3B3E5F]',
463
+ isAiModeOpen &&
464
+ 'bg-[#3B3E5F] text-white hover:bg-[#3B3E5F]',
465
+ )}
466
+ onClick={() => {
467
+ const nextIsAiModeOpen = !isAiModeOpen;
468
+ toggleAiMode();
469
+ setIsChatOpen(nextIsAiModeOpen);
470
+ }}
471
+ >
472
+ <Icon
473
+ name="search_ai"
474
+ width={14}
475
+ height={14}
476
+ className={twMerge(
477
+ 'text-[#3B3E5F]',
478
+ isAiModeOpen && 'text-white',
479
+ )}
480
+ />
481
+ AI mode
482
+ </button>
431
483
 
432
- <ImageUpload onCameraClick={onCameraClick} />
484
+ {hasAiNotification && !isAiModeOpen && (
485
+ <>
486
+ <span
487
+ className="absolute -top-0.5 right-1 w-3 h-3 rounded-full border-2 border-solid border-white"
488
+ style={{
489
+ background: window.settings?.theme?.primaryColor,
490
+ }}
491
+ />
492
+ </>
493
+ )}
494
+ </div>
495
+ )}
496
+ </div>
433
497
  </div>
434
498
  </div>
435
499
 
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import { useState } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
3
 
4
4
  import { Icon, makeFileHandler } from '@nyris/nyris-react-components';