@nyris/nyris-webapp 0.3.88 → 0.3.90

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 (70) hide show
  1. package/build/_headers +2 -0
  2. package/build/asset-manifest.json +6 -6
  3. package/build/index.html +1 -1
  4. package/build/js/settings.example.js +17 -0
  5. package/build/static/css/main.734b52e1.css +4 -0
  6. package/build/static/css/main.734b52e1.css.map +1 -0
  7. package/build/static/js/main.cede3ae1.js +3 -0
  8. package/build/static/js/{main.e861b336.js.map → main.cede3ae1.js.map} +1 -1
  9. package/package.json +3 -3
  10. package/public/_headers +2 -0
  11. package/public/index.html +1 -1
  12. package/public/js/settings.example.js +17 -0
  13. package/src/App.tsx +5 -3
  14. package/src/components/Cart.tsx +321 -0
  15. package/src/components/CustomCameraDrawer.tsx +4 -22
  16. package/src/components/DragDropFile.tsx +57 -38
  17. package/src/components/ExperienceVisualSearch/ExperienceVisualSearch.tsx +6 -1
  18. package/src/components/ExperienceVisualSearch/ExperienceVisualSearchTrigger.tsx +2 -2
  19. package/src/components/GroundingSpecs.tsx +47 -0
  20. package/src/components/Header.tsx +94 -93
  21. package/src/components/HitsPerPage.tsx +4 -2
  22. package/src/components/ImagePreview.tsx +64 -31
  23. package/src/components/ImageUpload.tsx +247 -0
  24. package/src/components/ItemSpecification.tsx +164 -0
  25. package/src/components/MatchNotificationBanner.tsx +165 -0
  26. package/src/components/PostFilter/PostFilter.tsx +22 -1
  27. package/src/components/PostFilter/PostFilterComponent.tsx +59 -26
  28. package/src/components/PostFilter/PostFilterFindApi.tsx +242 -0
  29. package/src/components/PoweredBy.tsx +16 -0
  30. package/src/components/PreFilter/PreFilter.tsx +77 -54
  31. package/src/components/Product/Product.tsx +186 -28
  32. package/src/components/Product/ProductAttribute.tsx +2 -2
  33. package/src/components/Product/ProductDetailView.tsx +123 -18
  34. package/src/components/Product/ProductDetailViewModal.tsx +3 -0
  35. package/src/components/Product/ProductList.tsx +78 -8
  36. package/src/components/SidePanel.tsx +212 -120
  37. package/src/components/TextSearch.tsx +82 -203
  38. package/src/components/Toaster.tsx +34 -15
  39. package/src/helpers/ToastHelper.ts +6 -2
  40. package/src/hooks/useCadSearch.ts +5 -0
  41. package/src/hooks/useImageSearch.ts +102 -13
  42. package/src/index.css +59 -0
  43. package/src/layouts/AppLayout.tsx +16 -14
  44. package/src/pages/Home.tsx +61 -13
  45. package/src/pages/Result.tsx +287 -295
  46. package/src/services/vizo.ts +161 -0
  47. package/src/stores/request/Misc/misc.initialstate.ts +1 -0
  48. package/src/stores/request/Misc/misc.slice.ts +1 -0
  49. package/src/stores/request/filter/filter.initialState.ts +3 -0
  50. package/src/stores/request/filter/filter.slice.ts +23 -0
  51. package/src/stores/result/prodcuts/products.initialState.ts +4 -0
  52. package/src/stores/result/prodcuts/products.slice.ts +15 -0
  53. package/src/stores/types.ts +27 -1
  54. package/src/stores/ui/loading/loading.initialState.ts +1 -0
  55. package/src/stores/ui/loading/loading.slice.ts +4 -0
  56. package/src/stores/ui/sidePanel/sidePanel.initialState.ts +5 -0
  57. package/src/stores/ui/sidePanel/sidePanel.slice.ts +11 -0
  58. package/src/stores/ui/uiStore.ts +4 -1
  59. package/src/styles/Cart.scss +210 -0
  60. package/src/styles/common.scss +10 -0
  61. package/src/translations.ts +4 -4
  62. package/src/types.ts +11 -3
  63. package/src/utils/prepareImageList.ts +6 -5
  64. package/src/utils/textSearchFilter.ts +203 -0
  65. package/tailwind.config.js +1 -0
  66. package/build/static/css/main.ba1c7479.css +0 -4
  67. package/build/static/css/main.ba1c7479.css.map +0 -1
  68. package/build/static/js/main.e861b336.js +0 -3
  69. package/src/components/Footer.tsx +0 -21
  70. /package/build/static/js/{main.e861b336.js.LICENSE.txt → main.cede3ae1.js.LICENSE.txt} +0 -0
@@ -1,10 +1,11 @@
1
- import { memo, useEffect, useState } from 'react';
1
+ import { memo, useEffect, useRef, useState } from 'react';
2
2
  import { get } from 'lodash';
3
3
 
4
4
  import { Icon } from '@nyris/nyris-react-components';
5
5
 
6
6
  import ProductAttribute from './ProductAttribute';
7
7
  import { truncateString } from 'utils/truncateString';
8
+ import { ToastHelper } from 'helpers/ToastHelper';
8
9
 
9
10
  import '../../styles/product.scss';
10
11
  import { twMerge } from 'tailwind-merge';
@@ -37,6 +38,8 @@ function Product(props: Props) {
37
38
  } = props;
38
39
  const [urlImage, setUrlImage] = useState<string>('');
39
40
  const settings = window.settings;
41
+ const [copiedKey, setCopiedKey] = useState<'main' | 'secondary' | null>(null);
42
+ const copyTimeoutRef = useRef<number | undefined>(undefined);
40
43
 
41
44
  const [openDetailedView, setOpenDetailedView] = useState<
42
45
  '3d' | 'image' | undefined
@@ -48,6 +51,14 @@ function Product(props: Props) {
48
51
  }
49
52
  }, [main_image_link]);
50
53
 
54
+ useEffect(() => {
55
+ return () => {
56
+ if (copyTimeoutRef.current) {
57
+ window.clearTimeout(copyTimeoutRef.current);
58
+ }
59
+ };
60
+ }, []);
61
+
51
62
  const handlerCheckUrlImage = (url: any, timeout?: number) => {
52
63
  timeout = timeout || 5000;
53
64
  var timedOut = false,
@@ -73,6 +84,37 @@ function Product(props: Props) {
73
84
  setOpenDetailedView('image');
74
85
  };
75
86
 
87
+ const onAddToCart = (part: any): void => {
88
+ // Get current cart from sessionStorage
89
+ const cart = JSON.parse(sessionStorage.getItem('cart') || '[]');
90
+ // Find if part already exists in cart
91
+ const partIndex = cart.findIndex((p: any) => p.sku === part.sku);
92
+ if (partIndex === -1) {
93
+ // Add new part with quantity 1
94
+ cart.push({ ...part, quantity: 1 });
95
+ } else {
96
+ // Increment quantity
97
+ cart[partIndex] = {
98
+ ...cart[partIndex],
99
+ quantity: cart[partIndex].quantity + 1,
100
+ };
101
+ }
102
+ sessionStorage.setItem('cart', JSON.stringify(cart));
103
+ window.dispatchEvent(new Event('cart-updated'));
104
+ ToastHelper.success('Product added');
105
+ };
106
+
107
+ const handleCopy = (value: string, key: 'main' | 'secondary') => {
108
+ navigator.clipboard.writeText(value);
109
+ setCopiedKey(key);
110
+ if (copyTimeoutRef.current) {
111
+ window.clearTimeout(copyTimeoutRef.current);
112
+ }
113
+ copyTimeoutRef.current = window.setTimeout(() => {
114
+ setCopiedKey(null);
115
+ }, 1000);
116
+ };
117
+
76
118
  return (
77
119
  <>
78
120
  <ProductDetailViewModal
@@ -82,6 +124,7 @@ function Product(props: Props) {
82
124
  openDetailedView={openDetailedView}
83
125
  setOpenDetailedView={setOpenDetailedView}
84
126
  main_image_link={main_image_link}
127
+ onAddToCart={onAddToCart}
85
128
  />
86
129
  <div className="wrap-main-item-result max-w-[190px] w-[180px] desktop:w-[190px] border border-solid border-[#E0E0E0] scroll-pt-5">
87
130
  <div className="relative h-fit">
@@ -161,7 +204,9 @@ function Product(props: Props) {
161
204
  </div>
162
205
  </div>
163
206
  {settings.simpleCardView && (
164
- <div className={`info-container-card ${settings.CTAButton?.CTAButton ? 'w-cta' : ''}`}>
207
+ <div
208
+ className={`info-container-card ${settings.CTAButton?.CTAButton ? 'w-cta' : ''}`}
209
+ >
165
210
  <div className="info-sku">{dataItem.sku}</div>
166
211
  <span className="info-marking">{dataItem.Bezeichnung}</span>
167
212
  <Tooltip
@@ -191,7 +236,7 @@ function Product(props: Props) {
191
236
  settings.CTAButton?.CTAButtonColor ||
192
237
  settings.theme?.primaryColor,
193
238
  borderRadius: 2,
194
- padding: '0px 8px',
239
+ paddingLeft: '8px',
195
240
  display: 'flex',
196
241
  justifyItems: 'center',
197
242
  alignItems: 'center',
@@ -222,10 +267,7 @@ function Product(props: Props) {
222
267
  >
223
268
  <Tooltip
224
269
  content={
225
- get(
226
- dataItem,
227
- settings.CTAButton?.CTAButtonText || '',
228
- ) ||
270
+ get(dataItem, settings.CTAButton?.CTAButtonText || '') ||
229
271
  settings.CTAButton?.CTAButtonText ||
230
272
  ''
231
273
  }
@@ -244,14 +286,12 @@ function Product(props: Props) {
244
286
  settings.CTAButton?.CTAButtonTextColor || '#FFFFFF',
245
287
  fontSize: '12px',
246
288
  letterSpacing: '0.27px',
247
- wordBreak: 'break-all',
289
+ wordBreak: 'break-word',
248
290
  paddingRight: '8px',
249
291
  }}
250
292
  >
251
- {get(
252
- dataItem,
253
- settings.CTAButton?.CTAButtonText || '',
254
- ) || settings.CTAButton?.CTAButtonText}
293
+ {get(dataItem, settings.CTAButton?.CTAButtonText || '') ||
294
+ settings.CTAButton?.CTAButtonText}
255
295
  </div>
256
296
  </Tooltip>
257
297
  {settings.CTAButton?.CTAIcon && (
@@ -287,17 +327,42 @@ function Product(props: Props) {
287
327
  ])}
288
328
  >
289
329
  <div
290
- className="relative h-fit text-primary flex flex-col justify-between "
330
+ className="relative text-primary flex flex-col justify-between h-full"
291
331
  style={{ color: '#FFFFFF' }}
292
332
  >
293
- <div>
294
- <div className="max-h-[38px] h-fit">
333
+ <div className="h-full flex flex-col">
334
+ <div className="flex flex-col gap-1 mb-1.5">
295
335
  {dataItem[settings.mainTitle] && (
296
- <Tooltip content={dataItem[settings.mainTitle] || ''}>
297
- <div className="text-xs font-bold text-primary mb-1 ml-2 max-line-1 w-fit">
298
- {truncateString(dataItem[settings.mainTitle], 45)}
336
+ <div className="flex justify-between gap-1 items-center">
337
+ <Tooltip content={dataItem[settings.mainTitle] || ''}>
338
+ <div
339
+ className="text-xs font-bold text-primary ml-2 max-line-1 w-fit"
340
+ style={{
341
+ wordBreak: 'break-word',
342
+ maxHeight: 46,
343
+ WebkitLineClamp: 3,
344
+ }}
345
+ >
346
+ {truncateString(dataItem[settings.mainTitle], 92)}
347
+ </div>
348
+ </Tooltip>
349
+ <div
350
+ onClick={() => {
351
+ handleCopy(dataItem[settings.mainTitle], 'main');
352
+ }}
353
+ className="group relative min-w-6 h-6 desktop:min-w-[18px] desktop:w-[18px] desktop:h-[18px] bg-[#FAFAFA] hover:bg-gray-100 flex items-center justify-center rounded-md cursor-pointer"
354
+ >
355
+ {copiedKey === 'main' && (
356
+ <div className="absolute -top-3.5 -right-2 bg-[#2B2C46] text-white text-[10px] py-0 px-1 rounded shadow pointer-events-none">
357
+ Copied!
358
+ </div>
359
+ )}
360
+ <Icon
361
+ name="copy"
362
+ className="text-[#AAABB5] w-3 h-3 desktop:w-[8px] desktop:h-[8px] group-hover:text-[#5a5b61] "
363
+ />
299
364
  </div>
300
- </Tooltip>
365
+ </div>
301
366
  )}
302
367
  {dataItem[settings.secondaryTitle] && (
303
368
  <div
@@ -306,6 +371,7 @@ function Product(props: Props) {
306
371
  'justify-between',
307
372
  'flex-row',
308
373
  'text-primary',
374
+ 'gap-1',
309
375
  ])}
310
376
  >
311
377
  <Tooltip
@@ -315,13 +381,38 @@ function Product(props: Props) {
315
381
  !dataItem[settings.secondaryTitle]
316
382
  }
317
383
  >
318
- <div className="text-[10px] font-normal max-line-1 text-primary mb-2 ml-2">
384
+ <div
385
+ className="text-sm desktop:text-[10px] font-normal max-line-1 text-primary ml-2"
386
+ style={{
387
+ maxHeight: 45,
388
+ WebkitLineClamp: 3,
389
+ }}
390
+ >
319
391
  {truncateString(
320
392
  dataItem[settings.secondaryTitle],
321
- 40,
393
+ 114,
322
394
  )}
323
395
  </div>
324
396
  </Tooltip>
397
+ <div
398
+ onClick={() => {
399
+ handleCopy(
400
+ dataItem[settings.secondaryTitle],
401
+ 'secondary',
402
+ );
403
+ }}
404
+ className="group relative min-w-6 h-6 desktop:min-w-[18px] desktop:w-[18px] desktop:h-[18px] bg-[#FAFAFA] hover:bg-gray-100 flex items-center justify-center rounded-md cursor-pointer"
405
+ >
406
+ {copiedKey === 'secondary' && (
407
+ <div className="absolute -top-4 -right-2 bg-[#2B2C46] text-white text-[10px] py-0 px-1 rounded shadow pointer-events-none">
408
+ Copied!
409
+ </div>
410
+ )}
411
+ <Icon
412
+ name="copy"
413
+ className="text-[#AAABB5] w-3 h-3 desktop:w-[8px] desktop:h-[8px] group-hover:text-[#5a5b61] "
414
+ />
415
+ </div>
325
416
  </div>
326
417
  )}
327
418
  </div>
@@ -331,7 +422,7 @@ function Product(props: Props) {
331
422
  settings.attributes?.attributeThreeValue ||
332
423
  settings.attributes?.attributeFourValue) && (
333
424
  <div
334
- className="attribute-container"
425
+ className="attribute-container flex-grow content-end"
335
426
  style={{
336
427
  display: 'flex',
337
428
  justifyContent: 'space-between',
@@ -417,7 +508,7 @@ function Product(props: Props) {
417
508
  settings.secondaryCTAButton?.secondaryCTAButtonColor ||
418
509
  '#2B2C46',
419
510
  borderRadius: 4,
420
- padding: '0px 8px',
511
+ paddingLeft: '8px',
421
512
  marginBottom: settings.CTAButton?.CTAButton ? 8 : 0,
422
513
  display: 'flex',
423
514
  justifyItems: 'center',
@@ -467,7 +558,7 @@ function Product(props: Props) {
467
558
  fontWeight: 600,
468
559
  fontSize: '12px',
469
560
  letterSpacing: '0.27px',
470
- wordBreak: 'break-all',
561
+ wordBreak: 'break-word',
471
562
  color:
472
563
  settings.secondaryCTAButton
473
564
  .secondaryCTAButtonTextColor || '#FFFFFF',
@@ -479,7 +570,14 @@ function Product(props: Props) {
479
570
  </Tooltip>
480
571
  {settings.secondaryCTAButton.secondaryCTAIcon && (
481
572
  <div style={{ width: '16px' }}>
482
- <Icon name="settings" color="white" />
573
+ <Icon
574
+ name="settings"
575
+ fill={
576
+ settings.secondaryCTAButton
577
+ ?.secondaryCTAButtonTextColor || '#FFFFFF'
578
+ }
579
+ width={10}
580
+ />
483
581
  </div>
484
582
  )}
485
583
  </div>
@@ -494,7 +592,7 @@ function Product(props: Props) {
494
592
  settings.CTAButton?.CTAButtonColor ||
495
593
  settings.theme?.primaryColor,
496
594
  borderRadius: 4,
497
- padding: '0px 8px',
595
+ paddingLeft: '8px',
498
596
  display: 'flex',
499
597
  justifyItems: 'center',
500
598
  alignItems: 'center',
@@ -546,7 +644,7 @@ function Product(props: Props) {
546
644
  settings.CTAButton?.CTAButtonTextColor || '#FFFFFF',
547
645
  fontSize: '12px',
548
646
  letterSpacing: '0.27px',
549
- wordBreak: 'break-all',
647
+ wordBreak: 'break-word',
550
648
  paddingRight: '8px',
551
649
  }}
552
650
  >
@@ -563,13 +661,73 @@ function Product(props: Props) {
563
661
  fill={
564
662
  settings.CTAButton?.CTAButtonTextColor || '#FFFFFF'
565
663
  }
566
- width={16}
664
+ width={10}
567
665
  />
568
666
  </div>
569
667
  )}
570
668
  </div>
571
669
  </div>
572
670
  )}
671
+
672
+ {settings.cart && (
673
+ <div
674
+ style={{
675
+ boxShadow: '-2px 2px 4px rgba(170, 171, 181, 0.5)',
676
+ minHeight: 28,
677
+ background:
678
+ settings.CTAButton?.CTAButtonColor ||
679
+ settings.theme?.primaryColor,
680
+ borderRadius: 4,
681
+ paddingLeft: '8px',
682
+ display: 'flex',
683
+ justifyItems: 'center',
684
+ alignItems: 'center',
685
+ justifyContent: 'space-between',
686
+ marginTop: 8,
687
+ }}
688
+ >
689
+ <div
690
+ style={{
691
+ display: 'flex',
692
+ justifyContent: 'space-between',
693
+ alignItems: 'center',
694
+ width: '100%',
695
+ padding: 0,
696
+ cursor: 'pointer',
697
+ }}
698
+ onClick={() => {
699
+ onAddToCart(dataItem);
700
+ }}
701
+ >
702
+ <div
703
+ className={`max-line-1 ${'desktop:136px'}`}
704
+ style={{
705
+ overflow: 'hidden',
706
+ textOverflow: 'ellipsis',
707
+ fontWeight: 600,
708
+ color:
709
+ settings.CTAButton?.CTAButtonTextColor || '#FFFFFF',
710
+ fontSize: '12px',
711
+ letterSpacing: '0.27px',
712
+ wordBreak: 'break-word',
713
+ paddingRight: '8px',
714
+ }}
715
+ >
716
+ Add to cart
717
+ </div>
718
+ <div style={{ width: '16px' }}>
719
+ <Icon
720
+ name="plus"
721
+ fill={
722
+ settings.CTAButton?.CTAButtonTextColor || '#FFFFFF'
723
+ }
724
+ className={twMerge('text-white')}
725
+ width={10}
726
+ />
727
+ </div>
728
+ </div>
729
+ </div>
730
+ )}
573
731
  </div>
574
732
  </div>
575
733
  )}
@@ -40,7 +40,7 @@ function ProductAttribute(props: Props) {
40
40
  textOverflow: 'ellipsis',
41
41
  whiteSpace: 'nowrap',
42
42
  }}
43
- className="text-xs font-semibold"
43
+ className="text-xs font-normal"
44
44
  >
45
45
  {title}
46
46
  </div>
@@ -56,7 +56,7 @@ function ProductAttribute(props: Props) {
56
56
  textOverflow: 'ellipsis',
57
57
  whiteSpace: 'nowrap',
58
58
  }}
59
- className="text-xs font-normal"
59
+ className="text-xs font-semibold"
60
60
  >
61
61
  {value || '-'}
62
62
  </div>
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useEffect, useState, useRef } from 'react';
2
2
  import { get } from 'lodash';
3
3
  import { useMediaQuery } from 'react-responsive';
4
4
  import { useTranslation } from 'react-i18next';
@@ -28,10 +28,17 @@ interface Props {
28
28
  onHandlerModalShare?: any;
29
29
  show3dView?: boolean;
30
30
  onSearchImage?: any;
31
+ onAddToCart?: any;
31
32
  }
32
33
 
33
34
  function ProductDetailView(props: Props) {
34
- const { dataItem, handleClose, show3dView = false, onSearchImage } = props;
35
+ const {
36
+ dataItem,
37
+ handleClose,
38
+ show3dView = false,
39
+ onSearchImage,
40
+ onAddToCart,
41
+ } = props;
35
42
  const { sku } = dataItem;
36
43
  const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
37
44
  const { settings } = window;
@@ -47,6 +54,8 @@ function ProductDetailView(props: Props) {
47
54
  const extraDetailPropertyLength = isMobile ? 15 : 30;
48
55
  const extraDetailValueLength = isMobile ? 35 : 60;
49
56
 
57
+ const [copiedKey, setCopiedKey] = useState<'main' | 'secondary' | null>(null);
58
+ const copyTimeoutRef = useRef<number | undefined>(undefined);
50
59
  useEffect(() => {
51
60
  if (dataItem) {
52
61
  checkDataItemResult(dataItem);
@@ -58,6 +67,24 @@ function ProductDetailView(props: Props) {
58
67
  }
59
68
  }, [dataItem]);
60
69
 
70
+ useEffect(() => {
71
+ return () => {
72
+ if (copyTimeoutRef.current) {
73
+ window.clearTimeout(copyTimeoutRef.current);
74
+ }
75
+ };
76
+ }, []);
77
+ const handleCopy = (value: string, key: 'main' | 'secondary') => {
78
+ navigator.clipboard.writeText(value);
79
+ setCopiedKey(key);
80
+ if (copyTimeoutRef.current) {
81
+ window.clearTimeout(copyTimeoutRef.current);
82
+ }
83
+ copyTimeoutRef.current = window.setTimeout(() => {
84
+ setCopiedKey(null);
85
+ }, 1000);
86
+ };
87
+
61
88
  const handlerCheckUrlImage = (url: any, timeout?: number) => {
62
89
  timeout = timeout || 5000;
63
90
  let timedOut = false,
@@ -186,25 +213,66 @@ function ProductDetailView(props: Props) {
186
213
  <div className="box-top">
187
214
  <div className="bg-[#fff] flex flex-col justify-between">
188
215
  <div className="gap-1.5 flex flex-wrap w-full">
189
- <div className="w-full">
216
+ <div className="w-full flex flex-col gap-1">
190
217
  {settings.mainTitle && (
191
- <Tooltip content={dataItem[settings.mainTitle]}>
192
- <p className="text-base font-bold max-line-1 text-[#2B2C46] font-source-sans-3 leading-[22.78px] ml-2 w-fit">
193
- {truncateString(dataItem[settings.mainTitle], 45)}
194
- </p>
195
- </Tooltip>
218
+ <div className="flex justify-between">
219
+ <Tooltip content={dataItem[settings.mainTitle]}>
220
+ <p className="text-base font-bold max-line-1 text-[#2B2C46] font-source-sans-3 leading-[22.78px] ml-2 w-fit">
221
+ {truncateString(dataItem[settings.mainTitle], 90)}
222
+ </p>
223
+ </Tooltip>
224
+
225
+ <div
226
+ onClick={() => {
227
+ handleCopy(dataItem[settings.mainTitle], 'main');
228
+ }}
229
+ className="group relative w-[22px] h-[22px] bg-[#FAFAFA] hover:bg-gray-100 flex items-center justify-center rounded-md cursor-pointer"
230
+ >
231
+ {copiedKey === 'main' && (
232
+ <div className="absolute -top-3.5 -right-2 bg-[#2B2C46] text-white text-[10px] py-0 px-1 rounded shadow pointer-events-none">
233
+ Copied!
234
+ </div>
235
+ )}
236
+ <Icon
237
+ name="copy"
238
+ className="text-[#AAABB5] w-[12px] h-[12px] group-hover:text-[#5a5b61] "
239
+ />
240
+ </div>
241
+ </div>
196
242
  )}
197
243
  {settings.secondaryTitle && (
198
- <Tooltip
199
- content={dataItem[settings.secondaryTitle] || ''}
200
- >
201
- <p className="text-f14 max-line-1 fw-400 text-[#2B2C46] ml-2 text-base w-fit">
202
- {truncateString(
203
- dataItem[settings.secondaryTitle],
204
- isMobile ? 45 : 70,
244
+ <div className="flex justify-between">
245
+ <Tooltip
246
+ content={dataItem[settings.secondaryTitle] || ''}
247
+ >
248
+ <p className="text-f14 max-line-1 fw-400 text-[#2B2C46] ml-2 text-base w-fit h-[72px]">
249
+ {truncateString(
250
+ dataItem[settings.secondaryTitle],
251
+ isMobile ? 45 : 80,
252
+ )}
253
+ </p>
254
+ </Tooltip>
255
+
256
+ <div
257
+ onClick={() => {
258
+ handleCopy(
259
+ dataItem[settings.secondaryTitle],
260
+ 'secondary',
261
+ );
262
+ }}
263
+ className="group relative w-[22px] h-[22px] bg-[#FAFAFA] hover:bg-gray-100 flex items-center justify-center rounded-md cursor-pointer"
264
+ >
265
+ {copiedKey === 'secondary' && (
266
+ <div className="absolute -top-3.5 -right-2 bg-[#2B2C46] text-white text-[10px] py-0 px-1 rounded shadow pointer-events-none">
267
+ Copied!
268
+ </div>
205
269
  )}
206
- </p>
207
- </Tooltip>
270
+ <Icon
271
+ name="copy"
272
+ className="text-[#AAABB5] w-[12px] h-[12px] group-hover:text-[#5a5b61] "
273
+ />
274
+ </div>
275
+ </div>
208
276
  )}
209
277
  </div>
210
278
  {settings.attributes?.productAttributes && (
@@ -388,9 +456,46 @@ function ProductDetailView(props: Props) {
388
456
  </div>
389
457
  )}
390
458
  </div>
459
+ {settings.cart && (
460
+ <div
461
+ className="bg-[#2B2C46] rounded mt-2 flex justify-between items-center h-8 desktop:h-12 w-full btn-detail-item"
462
+ style={{
463
+ background:
464
+ settings.CTAButton?.CTAButtonColor ||
465
+ settings.theme?.primaryColor,
466
+ }}
467
+ >
468
+ <div
469
+ className="flex justify-between items-center w-full px-3 min-h-8 cursor-pointer"
470
+ onClick={() => {
471
+ onAddToCart(dataItem);
472
+ }}
473
+ >
474
+ <div className="text-f16 fw-600 max-line-1 text-white pr-1.5">
475
+ Add to cart
476
+ </div>
477
+ <div style={{ width: '16px' }}>
478
+ <Icon
479
+ name="plus"
480
+ fill={
481
+ settings.CTAButton?.CTAButtonTextColor ||
482
+ '#FFFFFF'
483
+ }
484
+ className={'text-white'}
485
+ width={16}
486
+ />
487
+ </div>
488
+ </div>
489
+ </div>
490
+ )}
391
491
  </div>
392
492
  {settings.productDetailsAttribute?.length && (
393
- <Accordion type="single" collapsible className="w-full">
493
+ <Accordion
494
+ type="single"
495
+ collapsible
496
+ className="w-full"
497
+ defaultValue="view-details"
498
+ >
394
499
  <AccordionItem value="view-details">
395
500
  <AccordionTrigger className="w-full button-hover bg-[#F3F3F5] text-[#2b2c46] flex justify-between mt-3 px-4 text-base normal-case">
396
501
  {t('View details')}
@@ -9,6 +9,7 @@ function ProductDetailViewModal({
9
9
  handlerFeedback,
10
10
  onSearchImage,
11
11
  main_image_link,
12
+ onAddToCart,
12
13
  }: {
13
14
  openDetailedView: '3d' | 'image' | undefined;
14
15
  setOpenDetailedView: (value: '3d' | 'image' | undefined) => void;
@@ -16,6 +17,7 @@ function ProductDetailViewModal({
16
17
  handlerFeedback: (value: string) => void;
17
18
  onSearchImage: (url: string) => void;
18
19
  main_image_link: string;
20
+ onAddToCart: (item: any) => void;
19
21
  }) {
20
22
  useEffect(() => {
21
23
  if (openDetailedView === '3d' || openDetailedView === 'image') {
@@ -63,6 +65,7 @@ function ProductDetailViewModal({
63
65
  onSearchImage={(url: string) => {
64
66
  onSearchImage(url);
65
67
  }}
68
+ onAddToCart={onAddToCart}
66
69
  />
67
70
  </DialogContent>
68
71
  </Dialog>