@nyris/nyris-webapp 0.3.89 → 0.3.91
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.
- package/build/_headers +2 -0
- package/build/asset-manifest.json +6 -6
- package/build/index.html +1 -1
- package/build/js/settings.example.js +17 -0
- package/build/static/css/main.734b52e1.css +4 -0
- package/build/static/css/main.734b52e1.css.map +1 -0
- package/build/static/js/main.f2255597.js +3 -0
- package/build/static/js/{main.ca8b95bc.js.map → main.f2255597.js.map} +1 -1
- package/package.json +3 -3
- package/public/_headers +2 -0
- package/public/index.html +1 -1
- package/public/js/settings.example.js +17 -0
- package/src/App.tsx +5 -3
- package/src/components/Cart.tsx +321 -0
- package/src/components/CustomCameraDrawer.tsx +4 -22
- package/src/components/DragDropFile.tsx +57 -38
- package/src/components/ExperienceVisualSearch/ExperienceVisualSearch.tsx +6 -1
- package/src/components/ExperienceVisualSearch/ExperienceVisualSearchTrigger.tsx +2 -2
- package/src/components/GroundingSpecs.tsx +47 -0
- package/src/components/Header.tsx +94 -93
- package/src/components/HitsPerPage.tsx +4 -2
- package/src/components/ImagePreview.tsx +64 -31
- package/src/components/ImageUpload.tsx +247 -0
- package/src/components/ItemSpecification.tsx +164 -0
- package/src/components/MatchNotificationBanner.tsx +165 -0
- package/src/components/PostFilter/PostFilter.tsx +22 -1
- package/src/components/PostFilter/PostFilterComponent.tsx +59 -26
- package/src/components/PostFilter/PostFilterFindApi.tsx +242 -0
- package/src/components/PoweredBy.tsx +16 -0
- package/src/components/PreFilter/PreFilter.tsx +77 -54
- package/src/components/Product/Product.tsx +186 -28
- package/src/components/Product/ProductAttribute.tsx +2 -2
- package/src/components/Product/ProductDetailView.tsx +123 -18
- package/src/components/Product/ProductDetailViewModal.tsx +3 -0
- package/src/components/Product/ProductList.tsx +78 -8
- package/src/components/SidePanel.tsx +212 -120
- package/src/components/TextSearch.tsx +82 -203
- package/src/components/Toaster.tsx +34 -15
- package/src/helpers/ToastHelper.ts +6 -2
- package/src/hooks/useCadSearch.ts +5 -0
- package/src/hooks/useImageSearch.ts +102 -13
- package/src/index.css +59 -0
- package/src/layouts/AppLayout.tsx +16 -14
- package/src/pages/Home.tsx +61 -13
- package/src/pages/Result.tsx +287 -295
- package/src/services/vizo.ts +161 -0
- package/src/stores/request/Misc/misc.initialstate.ts +1 -0
- package/src/stores/request/Misc/misc.slice.ts +1 -0
- package/src/stores/request/filter/filter.initialState.ts +3 -0
- package/src/stores/request/filter/filter.slice.ts +23 -0
- package/src/stores/result/prodcuts/products.initialState.ts +4 -0
- package/src/stores/result/prodcuts/products.slice.ts +15 -0
- package/src/stores/types.ts +27 -1
- package/src/stores/ui/loading/loading.initialState.ts +1 -0
- package/src/stores/ui/loading/loading.slice.ts +4 -0
- package/src/stores/ui/sidePanel/sidePanel.initialState.ts +5 -0
- package/src/stores/ui/sidePanel/sidePanel.slice.ts +11 -0
- package/src/stores/ui/uiStore.ts +4 -1
- package/src/styles/Cart.scss +210 -0
- package/src/styles/common.scss +10 -0
- package/src/translations.ts +4 -4
- package/src/types.ts +11 -3
- package/src/utils/prepareImageList.ts +6 -5
- package/src/utils/textSearchFilter.ts +203 -0
- package/tailwind.config.js +1 -0
- package/build/static/css/main.ba1c7479.css +0 -4
- package/build/static/css/main.ba1c7479.css.map +0 -1
- package/build/static/js/main.ca8b95bc.js +0 -3
- package/src/components/Footer.tsx +0 -21
- /package/build/static/js/{main.ca8b95bc.js.LICENSE.txt → main.f2255597.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
|
|
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
|
-
|
|
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-
|
|
289
|
+
wordBreak: 'break-word',
|
|
248
290
|
paddingRight: '8px',
|
|
249
291
|
}}
|
|
250
292
|
>
|
|
251
|
-
{get(
|
|
252
|
-
|
|
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
|
|
330
|
+
className="relative text-primary flex flex-col justify-between h-full"
|
|
291
331
|
style={{ color: '#FFFFFF' }}
|
|
292
332
|
>
|
|
293
|
-
<div>
|
|
294
|
-
<div className="
|
|
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
|
-
<
|
|
297
|
-
<
|
|
298
|
-
|
|
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
|
-
</
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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
|
-
|
|
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-
|
|
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={
|
|
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-
|
|
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-
|
|
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 {
|
|
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
|
-
<
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
<
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
207
|
-
|
|
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
|
|
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>
|