@riosst100/pwa-marketplace 2.1.3 → 2.1.5
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/package.json +1 -1
- package/src/componentOverrideMapping.js +6 -0
- package/src/components/FavoriteSeller/AddToListButton/addToListButton.js +54 -54
- package/src/components/FavoriteSeller/AddToListButton/addToListButton.module.css +17 -17
- package/src/components/FavoriteSeller/AddToListButton/index.js +1 -1
- package/src/components/FavoriteSeller/AddToListButton/useCommonToasts.js +33 -33
- package/src/components/FilterTop/CustomFilters/customFilters.js +130 -132
- package/src/components/FilterTop/filterTop.js +1 -8
- package/src/components/LinkToOtherStores/index.js +61 -0
- package/src/components/NonSportCardsSets/nonSportCardsSets.js +0 -2
- package/src/components/RFQ/index.js +6 -3
- package/src/components/SellerDetail/sellerDetail.js +18 -1
- package/src/components/SellerInformation/sellerInformation.js +5 -3
- package/src/components/SellerSocialMedia/index.js +96 -0
- package/src/components/SetsData/index.js +2 -0
- package/src/components/SetsData/setsData.js +349 -0
- package/src/components/SetsData/setsData.module.css +76 -0
- package/src/components/SetsData/setsData.shimmer.js +50 -0
- package/src/components/ShopBy/shopBy.js +3 -2
- package/src/overwrites/pagebuilder/lib/ContentTypes/Products/products.js +13 -0
- package/src/overwrites/peregrine/lib/talons/MagentoRoute/magentoRoute.gql.js +27 -0
- package/src/overwrites/peregrine/lib/talons/MagentoRoute/useMagentoRoute.js +193 -0
- package/src/overwrites/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +40 -9
- package/src/overwrites/peregrine/lib/talons/ProductImageCarousel/useProductImageCarousel.js +77 -0
- package/src/overwrites/peregrine/lib/talons/ProductOptions/useOption.js +59 -0
- package/src/overwrites/peregrine/lib/talons/ProductOptions/useTile.js +47 -0
- package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryFragments.gql.js +13 -0
- package/src/overwrites/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js +23 -0
- package/src/overwrites/venia-ui/lib/RootComponents/Category/category.js +11 -3
- package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +0 -6
- package/src/overwrites/venia-ui/lib/components/AccountInformationPage/accountInformationPage.js +0 -1
- package/src/overwrites/venia-ui/lib/components/AccountInformationPage/editForm.js +0 -1
- package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.js +0 -3
- package/src/overwrites/venia-ui/lib/components/CheckoutPage/OrderSummary/orderSummary.js +0 -1
- package/src/overwrites/venia-ui/lib/components/Gallery/item.js +17 -3
- package/src/overwrites/venia-ui/lib/components/Price/price.js +113 -0
- package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/auctionDetail.js +1 -1
- package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +77 -104
- package/src/overwrites/venia-ui/lib/components/ProductImageCarousel/carousel.js +3 -1
- package/src/overwrites/venia-ui/lib/components/ProductOptions/option.js +112 -0
- package/src/overwrites/venia-ui/lib/components/ProductOptions/option.module.css +30 -0
- package/src/overwrites/venia-ui/lib/components/ProductOptions/options.js +49 -0
- package/src/overwrites/venia-ui/lib/components/ProductOptions/tile.js +118 -0
- package/src/overwrites/venia-ui/lib/components/ProductOptions/tile.module.css +68 -0
- package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.js +78 -0
- package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.module.css +6 -0
- package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.shimmer.js +32 -0
- package/src/talons/CustomFilters/useCustomFilters.js +0 -2
- package/src/talons/FavoriteSeller/AddToListButton/addToListButton.gql.js +30 -30
- package/src/talons/FavoriteSeller/AddToListButton/useAddToFavoriteListButton.js +0 -1
- package/src/talons/LegoSets/useLegoSets.js +0 -5
- package/src/talons/Seller/useSeller.js +1 -1
- package/src/talons/SetsData/setsData.gql.js +70 -0
- package/src/talons/SetsData/useSetsData.js +225 -0
- package/src/talons/ShopBy/shopBy.gql.js +12 -6
- package/src/talons/ShopBy/useShopBy.js +13 -4
- package/src/talons/TrainsSets/useTrainsSets.js +0 -3
|
@@ -81,7 +81,8 @@ const GalleryItem = props => {
|
|
|
81
81
|
|
|
82
82
|
// fallback to regular price when final price is unavailable
|
|
83
83
|
const priceSource =
|
|
84
|
-
price_range.
|
|
84
|
+
price_range.minimum_price?.final_price ||
|
|
85
|
+
price_range.minimum_price?.regular_price || price_range.maximum_price.final_price ||
|
|
85
86
|
price_range.maximum_price.regular_price;
|
|
86
87
|
|
|
87
88
|
// Hide the Rating component until it is updated with the new look and feel (PWA-2512).
|
|
@@ -144,12 +145,12 @@ const GalleryItem = props => {
|
|
|
144
145
|
currencyCode={priceSource.currency}
|
|
145
146
|
/>
|
|
146
147
|
</p>
|
|
147
|
-
<p className='old-price text-gray-400 text-xs line-through font-normal !mb-0'>
|
|
148
|
+
{/* <p className='old-price text-gray-400 text-xs line-through font-normal !mb-0'>
|
|
148
149
|
<Price
|
|
149
150
|
value={priceSource.value}
|
|
150
151
|
currencyCode={priceSource.currency}
|
|
151
152
|
/>
|
|
152
|
-
</p>
|
|
153
|
+
</p> */}
|
|
153
154
|
</div>
|
|
154
155
|
|
|
155
156
|
<div data-cy="GalleryItem-Rating" className={cn('flex gap-[5px] items-center mb-2')}>
|
|
@@ -212,6 +213,19 @@ GalleryItem.propTypes = {
|
|
|
212
213
|
discount: shape({
|
|
213
214
|
amount_off: number.isRequired
|
|
214
215
|
}).isRequired
|
|
216
|
+
}).isRequired,
|
|
217
|
+
minimum_price: shape({
|
|
218
|
+
final_price: shape({
|
|
219
|
+
value: number.isRequired,
|
|
220
|
+
currency: string.isRequired
|
|
221
|
+
}),
|
|
222
|
+
regular_price: shape({
|
|
223
|
+
value: number.isRequired,
|
|
224
|
+
currency: string.isRequired
|
|
225
|
+
}).isRequired,
|
|
226
|
+
discount: shape({
|
|
227
|
+
amount_off: number.isRequired
|
|
228
|
+
}).isRequired
|
|
215
229
|
}).isRequired
|
|
216
230
|
}).isRequired
|
|
217
231
|
}),
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React, { Fragment } from 'react';
|
|
2
|
+
import { number, string, shape } from 'prop-types';
|
|
3
|
+
import { useIntl } from 'react-intl';
|
|
4
|
+
import patches from '@magento/peregrine/lib/util/intlPatches';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The **Price** component is used anywhere a price needs to be displayed.
|
|
8
|
+
*
|
|
9
|
+
* Formatting of prices and currency symbol selection is handled entirely by the ECMAScript Internationalization API available in modern browsers.
|
|
10
|
+
*
|
|
11
|
+
* A [polyfill][] is required for any JavaScript runtime that does not have [Intl.NumberFormat.prototype.formatToParts][].
|
|
12
|
+
*
|
|
13
|
+
* [polyfill]: https://www.npmjs.com/package/intl
|
|
14
|
+
* [Intl.NumberFormat.prototype.formatToParts]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const Price = props => {
|
|
18
|
+
const { locale } = useIntl();
|
|
19
|
+
const { value, currencyCode, classes } = props;
|
|
20
|
+
|
|
21
|
+
// Check if the value contains " - "
|
|
22
|
+
if (typeof value === 'string' && value.includes(" - ")) {
|
|
23
|
+
const explodedValues = value.split(" - ");
|
|
24
|
+
const parts = patches.toParts.call(
|
|
25
|
+
new Intl.NumberFormat(locale, {
|
|
26
|
+
style: 'currency',
|
|
27
|
+
currency: currencyCode
|
|
28
|
+
}),
|
|
29
|
+
explodedValues[0]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const children = parts.map((part, i) => {
|
|
33
|
+
const partClass = classes[part.type];
|
|
34
|
+
const key = `${i}-${part.value}`;
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<span key={key} className={partClass}>
|
|
38
|
+
{part.value}
|
|
39
|
+
</span>
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const parts2 = patches.toParts.call(
|
|
44
|
+
new Intl.NumberFormat(locale, {
|
|
45
|
+
style: 'currency',
|
|
46
|
+
currency: currencyCode
|
|
47
|
+
}),
|
|
48
|
+
explodedValues[1]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const children2 = parts2.map((part, i) => {
|
|
52
|
+
const partClass = classes[part.type];
|
|
53
|
+
const key = `${i}-${part.value}`;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<span key={key} className={partClass}>
|
|
57
|
+
{part.value}
|
|
58
|
+
</span>
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return <Fragment>{children} - {children2}</Fragment>;
|
|
63
|
+
} else {
|
|
64
|
+
const parts = patches.toParts.call(
|
|
65
|
+
new Intl.NumberFormat(locale, {
|
|
66
|
+
style: 'currency',
|
|
67
|
+
currency: currencyCode
|
|
68
|
+
}),
|
|
69
|
+
value
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const children = parts.map((part, i) => {
|
|
73
|
+
const partClass = classes[part.type];
|
|
74
|
+
const key = `${i}-${part.value}`;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<span key={key} className={partClass}>
|
|
78
|
+
{part.value}
|
|
79
|
+
</span>
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return <Fragment>{children}</Fragment>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
Price.propTypes = {
|
|
90
|
+
/**
|
|
91
|
+
* Class names to use when styling this component
|
|
92
|
+
*/
|
|
93
|
+
classes: shape({
|
|
94
|
+
currency: string,
|
|
95
|
+
integer: string,
|
|
96
|
+
decimal: string,
|
|
97
|
+
fraction: string
|
|
98
|
+
}),
|
|
99
|
+
/**
|
|
100
|
+
* The numeric price
|
|
101
|
+
*/
|
|
102
|
+
value: number.isRequired,
|
|
103
|
+
/**
|
|
104
|
+
* A string with any of the currency code supported by Intl.NumberFormat
|
|
105
|
+
*/
|
|
106
|
+
currencyCode: string.isRequired
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
Price.defaultProps = {
|
|
110
|
+
classes: {}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default Price;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useMemo, Fragment, Suspense } from 'react';
|
|
1
|
+
import React, { useState, useMemo, Fragment, Suspense } from 'react';
|
|
2
2
|
import { FormattedMessage, useIntl } from 'react-intl';
|
|
3
3
|
import { arrayOf, bool, number, shape, string } from 'prop-types';
|
|
4
4
|
import { Form } from 'informed';
|
|
@@ -36,6 +36,7 @@ import CrossSeller from '@riosst100/pwa-marketplace/src/components/CrossSeller';
|
|
|
36
36
|
import RelatedProduct from '@riosst100/pwa-marketplace/src/components/RelatedProduct';
|
|
37
37
|
import ProductLabel from '@riosst100/pwa-marketplace/src/components/ProductLabel';
|
|
38
38
|
import RFQ from '@riosst100/pwa-marketplace/src/components/RFQ';
|
|
39
|
+
import LinkToOtherStores from '@riosst100/pwa-marketplace/src/components/LinkToOtherStores';
|
|
39
40
|
import Collapsible from '@riosst100/pwa-marketplace/src/components/commons/Collapsible';
|
|
40
41
|
|
|
41
42
|
// Correlate a GQL error message to a field. GQL could return a longer error
|
|
@@ -87,22 +88,26 @@ const ProductFullDetail = props => {
|
|
|
87
88
|
isAddToCartDisabled,
|
|
88
89
|
isSupportedProductType,
|
|
89
90
|
mediaGalleryEntries,
|
|
91
|
+
selectedMedia,
|
|
90
92
|
productDetails,
|
|
91
93
|
customAttributes,
|
|
92
94
|
wishlistButtonProps,
|
|
93
95
|
sellerDetails
|
|
94
96
|
} = talonProps;
|
|
95
|
-
console.log("🚀 ~ ProductFullDetail ~ talonProps:", talonProps)
|
|
96
97
|
|
|
97
98
|
const { formatMessage } = useIntl();
|
|
98
99
|
|
|
99
100
|
const classes = useStyle(defaultClasses, props.classes);
|
|
100
101
|
|
|
102
|
+
const [hoveredMedia, setHoveredMedia] = useState(null);
|
|
103
|
+
|
|
101
104
|
const options = isProductConfigurable(product) ? (
|
|
102
105
|
<Suspense fallback={<ProductOptionsShimmer />}>
|
|
103
106
|
<Options
|
|
104
107
|
onSelectionChange={handleSelectionChange}
|
|
105
108
|
options={product.configurable_options}
|
|
109
|
+
setHoveredMedia={setHoveredMedia}
|
|
110
|
+
variants={product.variants}
|
|
106
111
|
isEverythingOutOfStock={isEverythingOutOfStock}
|
|
107
112
|
outOfStockVariants={outOfStockVariants}
|
|
108
113
|
/>
|
|
@@ -223,28 +228,7 @@ const ProductFullDetail = props => {
|
|
|
223
228
|
// Error message for screen reader
|
|
224
229
|
const cartActionContent = isSupportedProductType ? (
|
|
225
230
|
<section className={cn(classes.actButton, 'flex gap-x-[30px]')}>
|
|
226
|
-
<
|
|
227
|
-
data-cy="ProductFullDetail-addToCartButton"
|
|
228
|
-
disabled={isAddToCartDisabled}
|
|
229
|
-
aria-disabled={isAddToCartDisabled}
|
|
230
|
-
aria-label={
|
|
231
|
-
isEverythingOutOfStock
|
|
232
|
-
? formatMessage({
|
|
233
|
-
id: 'productFullDetail.outOfStockProduct',
|
|
234
|
-
defaultMessage:
|
|
235
|
-
'This item is currently out of stock'
|
|
236
|
-
})
|
|
237
|
-
: ''
|
|
238
|
-
}
|
|
239
|
-
classes={{
|
|
240
|
-
content: 'normal-case font-medium text-[16px]'
|
|
241
|
-
}}
|
|
242
|
-
priority="low"
|
|
243
|
-
type="submit"
|
|
244
|
-
>
|
|
245
|
-
Make An Offer
|
|
246
|
-
</Button>
|
|
247
|
-
|
|
231
|
+
<RFQ disabled={isAddToCartDisabled} />
|
|
248
232
|
<Button
|
|
249
233
|
data-cy="ProductFullDetail-addToCartButton"
|
|
250
234
|
disabled={isAddToCartDisabled}
|
|
@@ -399,43 +383,60 @@ const ProductFullDetail = props => {
|
|
|
399
383
|
|
|
400
384
|
|
|
401
385
|
const dataTabs =
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
386
|
+
[
|
|
387
|
+
{
|
|
388
|
+
id: 'product-detail',
|
|
389
|
+
title: 'Description',
|
|
390
|
+
content: <ProductDescription />
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
id: 'product-more-info',
|
|
394
|
+
title: 'Details',
|
|
395
|
+
content: <ProductMoreInfo />
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: 'product-tnc',
|
|
399
|
+
title: 'Term & Conditions',
|
|
400
|
+
content: <ProductTNC />
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
id: 'product-shipping-policy',
|
|
404
|
+
title: 'Shipping Policy',
|
|
405
|
+
content: <ShippingPolicy />
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
id: 'product-return-policy',
|
|
409
|
+
title: 'Return Policy',
|
|
410
|
+
content: <ReturnPolicy />
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
id: 'product-faq',
|
|
414
|
+
title: 'FAQ',
|
|
415
|
+
content: <ProductFAQ />
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
id: 'product-reviews',
|
|
419
|
+
title: 'Reviews',
|
|
420
|
+
content: <ProductReviews className={cn(contentContainerClass)} />
|
|
421
|
+
}
|
|
422
|
+
];
|
|
423
|
+
|
|
424
|
+
const getSellerAddressDisplay = (seller) => {
|
|
425
|
+
let city = seller?.city;
|
|
426
|
+
let country = seller?.country;
|
|
427
|
+
|
|
428
|
+
let result = '';
|
|
429
|
+
if (city && country) {
|
|
430
|
+
result = `${city}, ${country}`;
|
|
431
|
+
}
|
|
432
|
+
if (city && !country) {
|
|
433
|
+
result = `${city}`;
|
|
434
|
+
}
|
|
435
|
+
if (!city && country) {
|
|
436
|
+
result = `${country}`;
|
|
437
|
+
}
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
439
440
|
|
|
440
441
|
return (
|
|
441
442
|
<Fragment>
|
|
@@ -447,7 +448,7 @@ const ProductFullDetail = props => {
|
|
|
447
448
|
>
|
|
448
449
|
<section className={cn(classes.leftContainer, 'relative')}>
|
|
449
450
|
<ProductLabel item={productDetails} />
|
|
450
|
-
<Carousel images={mediaGalleryEntries} />
|
|
451
|
+
<Carousel images={mediaGalleryEntries} hoveredMedia={hoveredMedia} selectedMedia={selectedMedia}/>
|
|
451
452
|
<div className='product_group-actions flex gap-x-[18px] gap-y-4 justify-center items-center mt-2 lg_mt-5 mb-6 lg_mb-0'>
|
|
452
453
|
<Suspense fallback={null}>
|
|
453
454
|
<WishlistButton
|
|
@@ -469,7 +470,7 @@ const ProductFullDetail = props => {
|
|
|
469
470
|
<div
|
|
470
471
|
className={cn(
|
|
471
472
|
classes.title,
|
|
472
|
-
'mb-
|
|
473
|
+
'mb-xs',
|
|
473
474
|
)}
|
|
474
475
|
>
|
|
475
476
|
<h1
|
|
@@ -500,7 +501,7 @@ const ProductFullDetail = props => {
|
|
|
500
501
|
<div
|
|
501
502
|
className={cn(
|
|
502
503
|
'product_short_description',
|
|
503
|
-
'mb-
|
|
504
|
+
'mb-xs',
|
|
504
505
|
)}
|
|
505
506
|
>
|
|
506
507
|
{shortDescription}
|
|
@@ -514,7 +515,7 @@ const ProductFullDetail = props => {
|
|
|
514
515
|
<div
|
|
515
516
|
className={cn(
|
|
516
517
|
'product_price_container',
|
|
517
|
-
'py-
|
|
518
|
+
'py-xs flex justify-between flex-wrap gap-y-5'
|
|
518
519
|
)}
|
|
519
520
|
>
|
|
520
521
|
<div className='flex flex-col gap-y-5 max-w-[448px]'>
|
|
@@ -528,10 +529,11 @@ const ProductFullDetail = props => {
|
|
|
528
529
|
>
|
|
529
530
|
<Price
|
|
530
531
|
currencyCode={productDetails.price.currency}
|
|
532
|
+
priceRange={productDetails.price_range}
|
|
531
533
|
value={productDetails.price.value}
|
|
532
534
|
/>
|
|
533
535
|
</p>
|
|
534
|
-
<p
|
|
536
|
+
{/* <p
|
|
535
537
|
data-cy="ProductFullDetail-productOldPrice"
|
|
536
538
|
className={cn(
|
|
537
539
|
classes.productPrice,
|
|
@@ -542,7 +544,7 @@ const ProductFullDetail = props => {
|
|
|
542
544
|
currencyCode={productDetails.price.currency}
|
|
543
545
|
value={productDetails.price.value}
|
|
544
546
|
/>
|
|
545
|
-
</p>
|
|
547
|
+
</p> */}
|
|
546
548
|
</div>
|
|
547
549
|
<AuctionDetail className="auction_detail-container" />
|
|
548
550
|
<PreorderDetail className={'preorder_detail-container'} />
|
|
@@ -564,7 +566,7 @@ const ProductFullDetail = props => {
|
|
|
564
566
|
errors={errors.get('form') || []}
|
|
565
567
|
/>
|
|
566
568
|
<section className={classes.options}>{options}</section>
|
|
567
|
-
<section className={cn(classes.quantity, 'py-
|
|
569
|
+
<section className={cn(classes.quantity, 'py-xs !border-none')}>
|
|
568
570
|
{/* <span
|
|
569
571
|
data-cy="ProductFullDetail-quantityTitle"
|
|
570
572
|
className={classes.quantityTitle}
|
|
@@ -579,7 +581,7 @@ const ProductFullDetail = props => {
|
|
|
579
581
|
message={errors.get('quantity')}
|
|
580
582
|
/>
|
|
581
583
|
|
|
582
|
-
<div className='product_shipping-information mb-[30px] leading-[18px] mt-[25px]'>
|
|
584
|
+
{/* <div className='product_shipping-information mb-[30px] leading-[18px] mt-[25px]'>
|
|
583
585
|
{sellerDetails &&
|
|
584
586
|
<div className='text-xs'>
|
|
585
587
|
Ship From <span className='font-medium'>{sellerDetails.country}</span>
|
|
@@ -590,48 +592,19 @@ const ProductFullDetail = props => {
|
|
|
590
592
|
<div className='text-xs'>
|
|
591
593
|
Shiping Method <span className='font-medium'>Store Pick Up | Meet Up</span>
|
|
592
594
|
</div>
|
|
593
|
-
</div>
|
|
595
|
+
</div> */}
|
|
594
596
|
|
|
595
597
|
<div className='product_actions-wrapper'>
|
|
596
598
|
{cartActionContent}
|
|
597
599
|
</div>
|
|
598
600
|
</section>
|
|
599
|
-
<Divider />
|
|
601
|
+
{/* <Divider />
|
|
600
602
|
<section className={cn(classes.quantity, 'py-[30px] !border-none')}>
|
|
601
603
|
<RFQ />
|
|
602
|
-
</section>
|
|
603
|
-
<Divider />
|
|
604
|
-
<section className='product_from-other-platform py-[30px]'>
|
|
605
|
-
<div className='text-sm flex mb-5'>
|
|
606
|
-
Also available in
|
|
607
|
-
</div>
|
|
608
|
-
<div className='platform-container flex gap-x-2.5'>
|
|
609
|
-
<Link
|
|
610
|
-
className='platform_logo-wrapper rounded-md px-5 py-2.5 border border-gray-100 flex items-center'
|
|
611
|
-
to=""
|
|
612
|
-
>
|
|
613
|
-
<img
|
|
614
|
-
alt="tokopedia"
|
|
615
|
-
width={60}
|
|
616
|
-
height={30}
|
|
617
|
-
src={'https://upload.wikimedia.org/wikipedia/commons/a/a7/Tokopedia.svg'}
|
|
618
|
-
/>
|
|
619
|
-
</Link>
|
|
620
|
-
<Link
|
|
621
|
-
className='platform_logo-wrapper rounded-md px-5 py-2.5 border border-gray-100 flex items-center'
|
|
622
|
-
to=""
|
|
623
|
-
>
|
|
624
|
-
<img
|
|
625
|
-
alt="shopee"
|
|
626
|
-
width={60}
|
|
627
|
-
height={30}
|
|
628
|
-
src={'https://upload.wikimedia.org/wikipedia/commons/f/fe/Shopee.svg'}
|
|
629
|
-
/>
|
|
630
|
-
</Link>
|
|
631
|
-
</div>
|
|
632
|
-
</section>
|
|
604
|
+
</section> */}
|
|
633
605
|
<Divider />
|
|
634
|
-
<
|
|
606
|
+
<LinkToOtherStores productDetails={productDetails} />
|
|
607
|
+
<section className='seller-information py-xs'>
|
|
635
608
|
<div className='flex xs_flex-col md_flex-row xs_items-center md_items-start items-start gap-[15px] relative'>
|
|
636
609
|
<div className='flex flex-row justify-between relative w-full'>
|
|
637
610
|
<div className='flex flex-col xs_items-center md_items-start gap-[6px] relative'>
|
|
@@ -644,7 +617,7 @@ const ProductFullDetail = props => {
|
|
|
644
617
|
</div>
|
|
645
618
|
</div>
|
|
646
619
|
<div class="relative w-fit font-normal text-[#999999] text-[12px] tracking-[0] leading-[14px] whitespace-nowrap">
|
|
647
|
-
{sellerDetails ? sellerDetails
|
|
620
|
+
{sellerDetails ? getSellerAddressDisplay(sellerDetails) : ''}
|
|
648
621
|
</div>
|
|
649
622
|
</div>
|
|
650
623
|
<div className='flex flex-wrap items-start gap-4 relative'>
|
|
@@ -32,10 +32,12 @@ const IMAGE_WIDTH = 495;
|
|
|
32
32
|
* @returns {React.Element} React carousel component that displays a product image
|
|
33
33
|
*/
|
|
34
34
|
const ProductImageCarousel = props => {
|
|
35
|
-
const { images } = props;
|
|
35
|
+
const { images, selectedMedia, hoveredMedia } = props;
|
|
36
36
|
const { formatMessage } = useIntl();
|
|
37
37
|
const talonProps = useProductImageCarousel({
|
|
38
38
|
images,
|
|
39
|
+
selectedMedia,
|
|
40
|
+
hoveredMedia,
|
|
39
41
|
imageWidth: IMAGE_WIDTH
|
|
40
42
|
});
|
|
41
43
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { FormattedMessage } from 'react-intl';
|
|
3
|
+
import {
|
|
4
|
+
arrayOf,
|
|
5
|
+
func,
|
|
6
|
+
number,
|
|
7
|
+
object,
|
|
8
|
+
oneOfType,
|
|
9
|
+
shape,
|
|
10
|
+
string
|
|
11
|
+
} from 'prop-types';
|
|
12
|
+
|
|
13
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
14
|
+
import getOptionType from '@magento/venia-ui/lib/components/ProductOptions/getOptionType';
|
|
15
|
+
import SwatchList from '@magento/venia-ui/lib/components/ProductOptions/swatchList';
|
|
16
|
+
import TileList from './tileList';
|
|
17
|
+
import defaultClasses from './option.module.css';
|
|
18
|
+
import { useOption } from '@magento/peregrine/lib/talons/ProductOptions/useOption';
|
|
19
|
+
|
|
20
|
+
const getItemKey = ({ value_index }) => value_index;
|
|
21
|
+
|
|
22
|
+
// TODO: get an explicit field from the API
|
|
23
|
+
// that identifies an attribute as a swatch
|
|
24
|
+
const getListComponent = (attribute_code, values) => {
|
|
25
|
+
const optionType = getOptionType({ attribute_code, values });
|
|
26
|
+
|
|
27
|
+
return optionType === 'swatch' ? SwatchList : TileList;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const Option = props => {
|
|
31
|
+
const {
|
|
32
|
+
attribute_code,
|
|
33
|
+
attribute_id,
|
|
34
|
+
label,
|
|
35
|
+
onSelectionChange,
|
|
36
|
+
selectedValue,
|
|
37
|
+
values,
|
|
38
|
+
variants,
|
|
39
|
+
setHoveredMedia,
|
|
40
|
+
isEverythingOutOfStock,
|
|
41
|
+
outOfStockVariants
|
|
42
|
+
} = props;
|
|
43
|
+
|
|
44
|
+
const talonProps = useOption({
|
|
45
|
+
attribute_id,
|
|
46
|
+
attribute_code,
|
|
47
|
+
label,
|
|
48
|
+
variants,
|
|
49
|
+
onSelectionChange,
|
|
50
|
+
selectedValue,
|
|
51
|
+
values
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
handleSelectionChange,
|
|
56
|
+
initialSelection,
|
|
57
|
+
selectedValueDescription,
|
|
58
|
+
filteredVariants
|
|
59
|
+
} = talonProps;
|
|
60
|
+
|
|
61
|
+
const ValueList = useMemo(() => getListComponent(attribute_code, values), [
|
|
62
|
+
attribute_code,
|
|
63
|
+
values
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const classes = useStyle(defaultClasses, props.classes);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className={classes.root} data-cy="ProductOptions-Option-root">
|
|
70
|
+
<span className={classes.title}>{label}</span>
|
|
71
|
+
<ValueList
|
|
72
|
+
getItemKey={getItemKey}
|
|
73
|
+
selectedValue={initialSelection}
|
|
74
|
+
items={values}
|
|
75
|
+
setHoveredMedia={setHoveredMedia}
|
|
76
|
+
onSelectionChange={handleSelectionChange}
|
|
77
|
+
isEverythingOutOfStock={isEverythingOutOfStock}
|
|
78
|
+
outOfStockVariants={outOfStockVariants}
|
|
79
|
+
filteredVariants={filteredVariants}
|
|
80
|
+
attributeLabel={label}
|
|
81
|
+
/>
|
|
82
|
+
<dl className={classes.selection} style={{ display: 'none' }}>
|
|
83
|
+
<dt
|
|
84
|
+
data-cy="ProductOptions-Option-selectedLabel"
|
|
85
|
+
className={classes.selectionLabel}
|
|
86
|
+
>
|
|
87
|
+
<FormattedMessage
|
|
88
|
+
id="productOptions.selectedLabel"
|
|
89
|
+
defaultMessage="Selected {label}:"
|
|
90
|
+
values={{ label }}
|
|
91
|
+
/>
|
|
92
|
+
</dt>
|
|
93
|
+
<dd>{selectedValueDescription}</dd>
|
|
94
|
+
</dl>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
Option.propTypes = {
|
|
100
|
+
attribute_code: string.isRequired,
|
|
101
|
+
attribute_id: string,
|
|
102
|
+
classes: shape({
|
|
103
|
+
root: string,
|
|
104
|
+
title: string
|
|
105
|
+
}),
|
|
106
|
+
label: string.isRequired,
|
|
107
|
+
onSelectionChange: func,
|
|
108
|
+
selectedValue: oneOfType([number, string]),
|
|
109
|
+
values: arrayOf(object).isRequired
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export default Option;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
composes: border-b from global;
|
|
3
|
+
composes: border-solid from global;
|
|
4
|
+
/* composes: border-subtle from global; */
|
|
5
|
+
/* composes: mx-sm from global; */
|
|
6
|
+
composes: my-0 from global;
|
|
7
|
+
composes: px-0 from global;
|
|
8
|
+
composes: py-xs from global;
|
|
9
|
+
|
|
10
|
+
border-color: rgb(230 233 234);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.title {
|
|
14
|
+
composes: block from global;
|
|
15
|
+
composes: font-semibold from global;
|
|
16
|
+
composes: leading-normal from global;
|
|
17
|
+
composes: mb-xs from global;
|
|
18
|
+
composes: text-colorDefault from global;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.selection {
|
|
22
|
+
composes: flex from global;
|
|
23
|
+
composes: leading-normal from global;
|
|
24
|
+
composes: mt-xs from global;
|
|
25
|
+
composes: text-colorDefault from global;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.selectionLabel {
|
|
29
|
+
composes: mr-xs from global;
|
|
30
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { array, func } from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import Option from './option';
|
|
5
|
+
import { useOptions } from '@magento/peregrine/lib/talons/ProductOptions/useOptions';
|
|
6
|
+
|
|
7
|
+
const Options = props => {
|
|
8
|
+
const {
|
|
9
|
+
classes,
|
|
10
|
+
onSelectionChange,
|
|
11
|
+
options,
|
|
12
|
+
variants,
|
|
13
|
+
setHoveredMedia,
|
|
14
|
+
selectedValues = [],
|
|
15
|
+
isEverythingOutOfStock,
|
|
16
|
+
outOfStockVariants
|
|
17
|
+
} = props;
|
|
18
|
+
|
|
19
|
+
const talonProps = useOptions({
|
|
20
|
+
onSelectionChange,
|
|
21
|
+
selectedValues,
|
|
22
|
+
options
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const { handleSelectionChange, selectedValueMap } = talonProps;
|
|
26
|
+
|
|
27
|
+
// Render a list of options passing in any pre-selected values.
|
|
28
|
+
return options.map(option => (
|
|
29
|
+
<Option
|
|
30
|
+
{...option}
|
|
31
|
+
classes={classes}
|
|
32
|
+
key={option.attribute_id}
|
|
33
|
+
variants={variants}
|
|
34
|
+
setHoveredMedia={setHoveredMedia}
|
|
35
|
+
onSelectionChange={handleSelectionChange}
|
|
36
|
+
selectedValue={selectedValueMap.get(option.label)}
|
|
37
|
+
isEverythingOutOfStock={isEverythingOutOfStock}
|
|
38
|
+
outOfStockVariants={outOfStockVariants}
|
|
39
|
+
/>
|
|
40
|
+
));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
Options.propTypes = {
|
|
44
|
+
onSelectionChange: func,
|
|
45
|
+
options: array.isRequired,
|
|
46
|
+
selectedValues: array
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default Options;
|