@riosst100/pwa-marketplace 3.0.4 → 3.0.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/components/CrossSeller/item.js +3 -4
- package/src/components/LinkToOtherStores/index.js +4 -4
- package/src/components/ProductListTab/productListTab.js +1 -1
- package/src/components/commons/Select/index.js +8 -4
- package/src/overwrites/peregrine/lib/talons/CartPage/PriceSummary/priceSummaryFragments.gql.js +3 -0
- package/src/overwrites/peregrine/lib/talons/CartPage/ProductListing/productListingFragments.gql.js +4 -0
- package/src/overwrites/peregrine/lib/talons/CheckoutPage/checkoutPage.extended.gql.js +20 -1
- package/src/overwrites/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js +3 -3
- package/src/overwrites/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +7 -8
- package/src/overwrites/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js +5 -0
- package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.js +20 -1
- package/src/overwrites/venia-ui/lib/components/CartPage/ProductListing/product.js +41 -1
- package/src/overwrites/venia-ui/lib/components/CartPage/ProductListing/product.module.css +1 -1
- package/src/overwrites/venia-ui/lib/components/CartPage/ProductListingBySeller/productListingBySeller.js +41 -8
- package/src/overwrites/venia-ui/lib/components/CheckoutPage/ItemsReview/item.js +43 -2
- package/src/overwrites/venia-ui/lib/components/CheckoutPage/ItemsReview/item.module.css +36 -0
- package/src/overwrites/venia-ui/lib/components/CheckoutPage/ItemsReview/itemsReview.js +8 -2
- package/src/overwrites/venia-ui/lib/components/CheckoutPage/checkoutPage.js +7 -2
- package/src/overwrites/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilters.js +1 -1
- package/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js +1 -1
- package/src/overwrites/venia-ui/lib/components/Gallery/item.js +2 -10
- package/src/overwrites/venia-ui/lib/components/Gallery/item.module.css +5 -4
- package/src/overwrites/venia-ui/lib/components/ProductFullDetail/CustomAttributes/customAttributes.js +10 -2
- package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/preOrderDetail.js +195 -37
- package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +194 -63
- package/src/talons/ProductContent/productContent.gql.js +11 -1
- package/src/talons/ProductContent/useProductContent.js +14 -2
|
@@ -13,14 +13,22 @@ const CustomAttributes = props => {
|
|
|
13
13
|
const { customAttributes, showLabels } = props;
|
|
14
14
|
const classes = useStyle(defaultClasses, props.classes);
|
|
15
15
|
|
|
16
|
+
const systemAttributes = [
|
|
17
|
+
'term_and_conditions',
|
|
18
|
+
'sale',
|
|
19
|
+
'sku'
|
|
20
|
+
];
|
|
21
|
+
|
|
16
22
|
const list = useMemo(
|
|
17
23
|
() =>
|
|
18
24
|
customAttributes.reduce((previousAttribute, currentAttribute) => {
|
|
25
|
+
console.log('currentAttribute',currentAttribute)
|
|
26
|
+
const attrCode = currentAttribute.attribute_metadata?.code;
|
|
19
27
|
const usedInComponents =
|
|
20
28
|
currentAttribute.attribute_metadata?.used_in_components ||
|
|
21
29
|
[];
|
|
22
30
|
// Visible on front attributes only
|
|
23
|
-
if (usedInComponents.includes(IS_VISIBLE_ON_FRONT)) {
|
|
31
|
+
if (!systemAttributes.includes(attrCode) && usedInComponents.includes(IS_VISIBLE_ON_FRONT)) {
|
|
24
32
|
const attributeContent = (
|
|
25
33
|
<li
|
|
26
34
|
key={currentAttribute.attribute_metadata.uid}
|
|
@@ -38,7 +46,7 @@ const CustomAttributes = props => {
|
|
|
38
46
|
|
|
39
47
|
return previousAttribute;
|
|
40
48
|
}, []),
|
|
41
|
-
[classes, customAttributes, showLabels]
|
|
49
|
+
[classes, customAttributes, showLabels, systemAttributes]
|
|
42
50
|
);
|
|
43
51
|
|
|
44
52
|
if (list.length === 0) {
|
package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/preOrderDetail.js
CHANGED
|
@@ -1,58 +1,216 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import cn from 'classnames';
|
|
3
3
|
import Divider from '@riosst100/pwa-marketplace/src/components/Divider';
|
|
4
|
+
import Select from '@riosst100/pwa-marketplace/src/components/commons/Select';
|
|
4
5
|
|
|
5
6
|
const preOrderInfo = (props) => {
|
|
6
|
-
const { className,
|
|
7
|
-
|
|
8
|
-
const preorderData = productDetails ? productDetails.preorder : null;
|
|
7
|
+
const { className, preOrder, preOrderDate, preOrderDeposite, preOrderNote, preOrderPaymentType, releaseDate, onPaymentTypeChange, paymentTypeOptions = [] } = props;
|
|
9
8
|
|
|
10
|
-
const isPreOrder =
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const isPreOrder = typeof preOrder === 'string'
|
|
10
|
+
? preOrder.trim().toLowerCase() === 'pre orders'
|
|
11
|
+
: !!(preOrder && preOrder.is_preorder);
|
|
12
|
+
|
|
13
|
+
const normalizePaymentType = (value = '') => {
|
|
14
|
+
const normalized = value.toString().trim().toLowerCase();
|
|
15
|
+
|
|
16
|
+
if (normalized === 'full payment') {
|
|
17
|
+
return 'full';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (normalized === 'deposit' || normalized === 'full') {
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return '';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const initialPaymentType = useMemo(
|
|
28
|
+
() => normalizePaymentType(preOrderPaymentType),
|
|
29
|
+
[preOrderPaymentType]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const [selectedPaymentType, setSelectedPaymentType] = useState(initialPaymentType || 'full');
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
setSelectedPaymentType(initialPaymentType || 'full');
|
|
36
|
+
}, [initialPaymentType]);
|
|
37
|
+
|
|
38
|
+
// Propagate initial and subsequent selection to parent so submit has value
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (typeof onPaymentTypeChange === 'function' && selectedPaymentType) {
|
|
41
|
+
onPaymentTypeChange(selectedPaymentType);
|
|
42
|
+
}
|
|
43
|
+
}, [selectedPaymentType, onPaymentTypeChange]);
|
|
44
|
+
|
|
45
|
+
const normalizedPaymentOptions = useMemo(() => {
|
|
46
|
+
return paymentTypeOptions.reduce((acc, option) => {
|
|
47
|
+
const normalizedValue = normalizePaymentType(option.value);
|
|
48
|
+
if (!normalizedValue) {
|
|
49
|
+
return acc;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (acc.find(item => item.value === normalizedValue)) {
|
|
53
|
+
return acc;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const label = option.label || (normalizedValue === 'full' ? 'Full Payment' : 'Deposit');
|
|
57
|
+
acc.push({ label, value: normalizedValue });
|
|
58
|
+
return acc;
|
|
59
|
+
}, []);
|
|
60
|
+
}, [paymentTypeOptions]);
|
|
61
|
+
|
|
62
|
+
const paymentOptions = useMemo(() => {
|
|
63
|
+
const optionsToUse = normalizedPaymentOptions;
|
|
64
|
+
|
|
65
|
+
if (!optionsToUse.length) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (initialPaymentType === 'full') {
|
|
70
|
+
return optionsToUse.filter(option => option.value === 'full');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (initialPaymentType === 'deposit') {
|
|
74
|
+
return optionsToUse;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return optionsToUse;
|
|
78
|
+
}, [initialPaymentType, normalizedPaymentOptions]);
|
|
79
|
+
|
|
80
|
+
const handlePaymentTypeChange = (event) => {
|
|
81
|
+
if (!paymentOptions.length) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const nextValue = event.target.value;
|
|
86
|
+
const availableValues = paymentOptions.map(option => option.value);
|
|
87
|
+
const appliedValue = availableValues.includes(nextValue)
|
|
88
|
+
? nextValue
|
|
89
|
+
: availableValues[0];
|
|
90
|
+
|
|
91
|
+
setSelectedPaymentType(appliedValue);
|
|
92
|
+
|
|
93
|
+
if (typeof onPaymentTypeChange === 'function') {
|
|
94
|
+
onPaymentTypeChange(appliedValue);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const formatDate = (value) => {
|
|
99
|
+
if (!value) {
|
|
100
|
+
return '-';
|
|
101
|
+
}
|
|
13
102
|
|
|
14
|
-
|
|
103
|
+
const parsed = value instanceof Date ? value : new Date(value);
|
|
104
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
105
|
+
return '-';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const day = String(parsed.getDate()).padStart(2, '0');
|
|
109
|
+
const month = String(parsed.getMonth() + 1).padStart(2, '0');
|
|
110
|
+
const year = parsed.getFullYear();
|
|
111
|
+
|
|
112
|
+
return `${day}/${month}/${year}`;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const preOrderNotes = preOrderNote || null;
|
|
116
|
+
const preOrderAvailableDate = releaseDate || null;
|
|
117
|
+
const preOrderClosingDate = preOrderDate || null;
|
|
118
|
+
|
|
119
|
+
const normalizeDeposit = (value) => {
|
|
120
|
+
if (!value && value !== 0) {
|
|
121
|
+
return '-';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const numeric = typeof value === 'number'
|
|
125
|
+
? value
|
|
126
|
+
: Number.parseFloat(value.toString().replace(/[^0-9.-]/g, ''));
|
|
127
|
+
|
|
128
|
+
if (Number.isNaN(numeric)) {
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Tampilkan sebagai persentase tanpa 0 di belakang koma dan tambahkan '%'
|
|
133
|
+
// Contoh: 10.00 -> '10%'; 12.5 -> '12.5%'; 12.50 -> '12.5%'
|
|
134
|
+
let str = numeric.toString();
|
|
135
|
+
if (str.includes('.')) {
|
|
136
|
+
// hapus trailing zeros di desimal
|
|
137
|
+
str = str.replace(/\.0+$/,'');
|
|
138
|
+
str = str.replace(/\.(\d*[1-9])0+$/,'.$1');
|
|
139
|
+
}
|
|
140
|
+
return `${str}%`;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const depositValue = normalizeDeposit(preOrderDeposite);
|
|
144
|
+
const showDepositRow = selectedPaymentType === 'deposit';
|
|
145
|
+
|
|
146
|
+
const isPreOrderClosed = (() => {
|
|
147
|
+
if (!preOrderClosingDate) return false;
|
|
148
|
+
|
|
149
|
+
const parsed = preOrderClosingDate instanceof Date
|
|
150
|
+
? preOrderClosingDate
|
|
151
|
+
: new Date(preOrderClosingDate);
|
|
152
|
+
|
|
153
|
+
if (Number.isNaN(parsed.getTime())) return false;
|
|
154
|
+
|
|
155
|
+
parsed.setHours(23, 59, 59, 999);
|
|
156
|
+
return new Date().getTime() > parsed.getTime();
|
|
157
|
+
})();
|
|
15
158
|
|
|
16
159
|
return isPreOrder ? (
|
|
17
160
|
<>
|
|
18
161
|
<div
|
|
19
162
|
className={cn(
|
|
20
|
-
'flex flex-col border border-gray-100 rounded-lg p-5 gap-
|
|
163
|
+
'flex flex-col border border-gray-100 rounded-lg p-5 m-4 gap-4 bg-white',
|
|
21
164
|
className
|
|
22
165
|
)}
|
|
23
166
|
>
|
|
24
|
-
<div className='
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
167
|
+
<div className='text-[16px] font-semibold text-colorDefault'>
|
|
168
|
+
Pre Order Details
|
|
169
|
+
</div>
|
|
170
|
+
<div className='flex flex-col gap-3'>
|
|
171
|
+
{showDepositRow && (
|
|
172
|
+
<>
|
|
173
|
+
<div className='flex justify-between text-[13px] text-gray-600'>
|
|
174
|
+
<span className='font-medium text-gray-500'>Deposit:</span>
|
|
175
|
+
<span className='font-medium text-colorDefault'>{depositValue}</span>
|
|
176
|
+
</div>
|
|
177
|
+
<Divider className='h-px w-full bg-gray-100' />
|
|
178
|
+
</>
|
|
179
|
+
)}
|
|
180
|
+
<div className='flex justify-between items-center text-[13px] text-gray-600 gap-4'>
|
|
181
|
+
<span className='font-medium text-gray-500'>Payment Type:</span>
|
|
182
|
+
<Select
|
|
183
|
+
wrapperClassname='justify-end w-full max-w-[180px]'
|
|
184
|
+
className='w-full text-[13px]'
|
|
185
|
+
options={paymentOptions}
|
|
186
|
+
value={selectedPaymentType}
|
|
187
|
+
onChange={handlePaymentTypeChange}
|
|
188
|
+
showPlaceholder={false}
|
|
189
|
+
/>
|
|
33
190
|
</div>
|
|
34
|
-
<Divider className=
|
|
35
|
-
<div className='flex
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
</div>
|
|
39
|
-
<div className='text-[16px] font-medium text-colorDefault text-center'>
|
|
40
|
-
31, March 2024
|
|
41
|
-
</div>
|
|
191
|
+
<Divider className='h-px w-full bg-gray-100' />
|
|
192
|
+
<div className='flex justify-between text-[13px] text-gray-600'>
|
|
193
|
+
<span className='font-medium text-gray-500'>Pre Order Closing Date:</span>
|
|
194
|
+
<span className='font-medium text-colorDefault'>{formatDate(preOrderClosingDate)}</span>
|
|
42
195
|
</div>
|
|
43
|
-
<Divider className=
|
|
44
|
-
<div className='flex
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
</div>
|
|
48
|
-
<div className='text-[16px] font-medium text-colorDefault text-center'>
|
|
49
|
-
40%
|
|
50
|
-
</div>
|
|
196
|
+
<Divider className='h-px w-full bg-gray-100' />
|
|
197
|
+
<div className='flex justify-between text-[13px] text-gray-600'>
|
|
198
|
+
<span className='font-medium text-gray-500'>Release Date:</span>
|
|
199
|
+
<span className='font-medium text-colorDefault'>{formatDate(preOrderAvailableDate)}</span>
|
|
51
200
|
</div>
|
|
201
|
+
<Divider className='h-px w-full bg-gray-100' />
|
|
202
|
+
<div className='flex justify-between text-[13px] text-gray-600'>
|
|
203
|
+
<span className='font-medium text-gray-500'>Note:</span>
|
|
204
|
+
<span className='font-medium text-colorDefault text-right'>
|
|
205
|
+
{preOrderNotes || '-'}
|
|
206
|
+
</span>
|
|
207
|
+
</div>
|
|
208
|
+
{isPreOrderClosed && (
|
|
209
|
+
<div className='mt-1 text-[12px] text-red-500 font-medium'>
|
|
210
|
+
Pre order is closed. You can no longer place a pre-order for this product.
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
52
213
|
</div>
|
|
53
|
-
{preOrderNotes && <p>
|
|
54
|
-
Notes : {preOrderNotes}
|
|
55
|
-
</p>}
|
|
56
214
|
</div>
|
|
57
215
|
</>
|
|
58
216
|
) : ''
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useState, useMemo, Fragment, Suspense, useRef, useEffect } from 'react';
|
|
2
|
+
import { useQuery } from '@apollo/client';
|
|
2
3
|
import { FormattedMessage, useIntl } from 'react-intl';
|
|
3
4
|
import { arrayOf, bool, number, shape, string } from 'prop-types';
|
|
4
5
|
import { Form } from 'informed';
|
|
@@ -26,6 +27,7 @@ import RichText from '@magento/venia-ui/lib/components/RichText';
|
|
|
26
27
|
import { Star1, Verify, Sms, Message, Shop, ArrowUp2 } from 'iconsax-react';
|
|
27
28
|
import { Link } from "react-router-dom";
|
|
28
29
|
import Divider from '@riosst100/pwa-marketplace/src/components/Divider';
|
|
30
|
+
import { GET_PAYMENT_TYPES } from '@riosst100/pwa-marketplace/src/talons/ProductContent/productContent.gql.js';
|
|
29
31
|
|
|
30
32
|
const WishlistButton = React.lazy(() => import('@magento/venia-ui/lib/components/Wishlist/AddToListButton'));
|
|
31
33
|
const Options = React.lazy(() => import('@magento/venia-ui/lib/components/ProductOptions'));
|
|
@@ -112,9 +114,16 @@ const ProductFullDetail = props => {
|
|
|
112
114
|
|
|
113
115
|
const { formatMessage } = useIntl();
|
|
114
116
|
|
|
117
|
+
const { data: paymentTypeData } = useQuery(GET_PAYMENT_TYPES, {
|
|
118
|
+
fetchPolicy: 'cache-and-network'
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const paymentTypeOptions = paymentTypeData?.getPaymentType || [];
|
|
122
|
+
|
|
115
123
|
const classes = useStyle(defaultClasses, props.classes);
|
|
116
124
|
|
|
117
125
|
const [hoveredMedia, setHoveredMedia] = useState(null);
|
|
126
|
+
const [selectedPaymentType, setSelectedPaymentType] = useState(null);
|
|
118
127
|
|
|
119
128
|
const options = isProductConfigurable(product) ? (
|
|
120
129
|
<Suspense fallback={<ProductOptionsShimmer />}>
|
|
@@ -133,6 +142,7 @@ const ProductFullDetail = props => {
|
|
|
133
142
|
<Breadcrumbs
|
|
134
143
|
categoryId={breadcrumbCategoryId}
|
|
135
144
|
currentProduct={productDetails.name}
|
|
145
|
+
currentProductCustomTableMetadata={productDetails.custom_table_metadata}
|
|
136
146
|
/>
|
|
137
147
|
) : null;
|
|
138
148
|
|
|
@@ -221,62 +231,6 @@ const ProductFullDetail = props => {
|
|
|
221
231
|
};
|
|
222
232
|
}, [customAttributes, productDetails.sku, formatMessage]);
|
|
223
233
|
|
|
224
|
-
const cartCallToActionText =
|
|
225
|
-
!isEverythingOutOfStock || !isOutOfStock ? (
|
|
226
|
-
<FormattedMessage
|
|
227
|
-
id="productFullDetail.addItemToCart"
|
|
228
|
-
defaultMessage="Add to Cart"
|
|
229
|
-
/>
|
|
230
|
-
) : (
|
|
231
|
-
<FormattedMessage
|
|
232
|
-
id="productFullDetail.itemOutOfStock"
|
|
233
|
-
defaultMessage="Out of Stock"
|
|
234
|
-
/>
|
|
235
|
-
);
|
|
236
|
-
// Error message for screen reader
|
|
237
|
-
const cartActionContent = isSupportedProductType ? (
|
|
238
|
-
<section className={cn(classes.actButton, 'justify-between flex gap-x-[20px] px-[15px] py-0')}>
|
|
239
|
-
<RFQ
|
|
240
|
-
disabled={isAddToCartDisabled}
|
|
241
|
-
classes={{ rfqButton: cn(classes.rfqButton, "w-full") }}
|
|
242
|
-
/>
|
|
243
|
-
<Button
|
|
244
|
-
data-cy="ProductFullDetail-addToCartButton"
|
|
245
|
-
disabled={isAddToCartDisabled}
|
|
246
|
-
aria-disabled={isAddToCartDisabled}
|
|
247
|
-
aria-label={
|
|
248
|
-
isEverythingOutOfStock
|
|
249
|
-
? formatMessage({
|
|
250
|
-
id: 'productFullDetail.outOfStockProduct',
|
|
251
|
-
defaultMessage:
|
|
252
|
-
'This item is currently out of stock'
|
|
253
|
-
})
|
|
254
|
-
: ''
|
|
255
|
-
}
|
|
256
|
-
classes={{
|
|
257
|
-
rootClass: '!px-0 !py-3',
|
|
258
|
-
content: 'w-full normal-case font-medium text-[14px]'
|
|
259
|
-
}}
|
|
260
|
-
priority="high"
|
|
261
|
-
type="submit"
|
|
262
|
-
>
|
|
263
|
-
{cartCallToActionText}
|
|
264
|
-
</Button>
|
|
265
|
-
</section>
|
|
266
|
-
) : (
|
|
267
|
-
<div className={classes.unavailableContainer}>
|
|
268
|
-
<Info />
|
|
269
|
-
<p>
|
|
270
|
-
<FormattedMessage
|
|
271
|
-
id={'productFullDetail.unavailableProduct'}
|
|
272
|
-
defaultMessage={
|
|
273
|
-
'This product is currently unavailable for purchase.'
|
|
274
|
-
}
|
|
275
|
-
/>
|
|
276
|
-
</p>
|
|
277
|
-
</div>
|
|
278
|
-
);
|
|
279
|
-
|
|
280
234
|
const shortDescription = productDetails.shortDescription ? (
|
|
281
235
|
<RichText
|
|
282
236
|
rootClassName="px-0"
|
|
@@ -392,7 +346,6 @@ const ProductFullDetail = props => {
|
|
|
392
346
|
const code = attr.attribute_metadata?.code || "";
|
|
393
347
|
return useSuffix ? code.endsWith(key) : code === key;
|
|
394
348
|
});
|
|
395
|
-
|
|
396
349
|
if (!attr) return "";
|
|
397
350
|
|
|
398
351
|
if (attr.selected_attribute_options?.attribute_option?.length) {
|
|
@@ -403,6 +356,12 @@ const ProductFullDetail = props => {
|
|
|
403
356
|
return attr.entered_attribute_value?.value || "";
|
|
404
357
|
};
|
|
405
358
|
|
|
359
|
+
const preOrder = getAttributeValue(customAttributesDetails, "card_pre_orders", true);
|
|
360
|
+
const preOrderDate = getAttributeValue(customAttributesDetails, "pre_order_date", true);
|
|
361
|
+
const preOrderDeposite = getAttributeValue(customAttributesDetails, "pre_order_deposit", true);
|
|
362
|
+
const preOrderNote = getAttributeValue(customAttributesDetails, "pre_order_notes", true);
|
|
363
|
+
const preOrderPaymentType = getAttributeValue(customAttributesDetails, "pre_order_payment_type", true);
|
|
364
|
+
const releaseDate = getAttributeValue(customAttributesDetails, "release_date", true);
|
|
406
365
|
const setNameValue = getAttributeValue(customAttributesDetails, "card_set", true);
|
|
407
366
|
const cardNameValue = getAttributeValue(customAttributesDetails, "card_name", true);
|
|
408
367
|
const cardNumberValue = getAttributeValue(customAttributesDetails, "card_number", true);
|
|
@@ -410,6 +369,104 @@ const ProductFullDetail = props => {
|
|
|
410
369
|
const featureValue = getAttributeValue(customAttributesDetails, "card_feature", true);
|
|
411
370
|
const conditionValue = getAttributeValue(customAttributesDetails, "card_condition", true);
|
|
412
371
|
const foilValue = getAttributeValue(customAttributesDetails, "card_foil", true);
|
|
372
|
+
|
|
373
|
+
const isPreOrderProduct = typeof preOrder === 'string'
|
|
374
|
+
? preOrder.trim().toLowerCase() === 'pre orders'
|
|
375
|
+
: !!(preOrder && preOrder.is_preorder);
|
|
376
|
+
|
|
377
|
+
const parsePreOrderClosingDate = (value) => {
|
|
378
|
+
if (!value) return null;
|
|
379
|
+
|
|
380
|
+
const parsed = value instanceof Date ? value : new Date(value);
|
|
381
|
+
if (Number.isNaN(parsed.getTime())) return null;
|
|
382
|
+
|
|
383
|
+
// Normalise to end of day for fairness
|
|
384
|
+
parsed.setHours(23, 59, 59, 999);
|
|
385
|
+
return parsed;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const preOrderClosingDateObj = parsePreOrderClosingDate(preOrderDate);
|
|
389
|
+
const isPreOrderClosed =
|
|
390
|
+
isPreOrderProduct &&
|
|
391
|
+
preOrderClosingDateObj !== null &&
|
|
392
|
+
new Date().getTime() > preOrderClosingDateObj.getTime();
|
|
393
|
+
|
|
394
|
+
const cartCallToActionText =
|
|
395
|
+
isPreOrderClosed
|
|
396
|
+
? (
|
|
397
|
+
<FormattedMessage
|
|
398
|
+
id="productFullDetail.preOrderClosed"
|
|
399
|
+
defaultMessage="Pre Order Closed"
|
|
400
|
+
/>
|
|
401
|
+
)
|
|
402
|
+
: isPreOrderProduct
|
|
403
|
+
? (
|
|
404
|
+
<FormattedMessage
|
|
405
|
+
id="productFullDetail.preOrderNow"
|
|
406
|
+
defaultMessage="Pre Order Now"
|
|
407
|
+
/>
|
|
408
|
+
)
|
|
409
|
+
: !isEverythingOutOfStock || !isOutOfStock
|
|
410
|
+
? (
|
|
411
|
+
<FormattedMessage
|
|
412
|
+
id="productFullDetail.addItemToCart"
|
|
413
|
+
defaultMessage="Add to Cart"
|
|
414
|
+
/>
|
|
415
|
+
)
|
|
416
|
+
: (
|
|
417
|
+
<FormattedMessage
|
|
418
|
+
id="productFullDetail.itemOutOfStock"
|
|
419
|
+
defaultMessage="Out of Stock"
|
|
420
|
+
/>
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
// Error message for screen reader
|
|
424
|
+
const cartActionContent = isSupportedProductType ? (
|
|
425
|
+
<section className={cn(classes.actButton, 'justify-between flex gap-x-[20px] px-[15px] py-0')}>
|
|
426
|
+
<RFQ
|
|
427
|
+
disabled={isAddToCartDisabled || isPreOrderClosed}
|
|
428
|
+
classes={{ rfqButton: cn(classes.rfqButton, "w-full") }}
|
|
429
|
+
/>
|
|
430
|
+
<Button
|
|
431
|
+
data-cy="ProductFullDetail-addToCartButton"
|
|
432
|
+
disabled={isAddToCartDisabled || isPreOrderClosed}
|
|
433
|
+
aria-disabled={isAddToCartDisabled || isPreOrderClosed}
|
|
434
|
+
aria-label={
|
|
435
|
+
isPreOrderClosed
|
|
436
|
+
? 'Pre order closing date has passed'
|
|
437
|
+
: isPreOrderProduct
|
|
438
|
+
? ''
|
|
439
|
+
: isEverythingOutOfStock
|
|
440
|
+
? formatMessage({
|
|
441
|
+
id: 'productFullDetail.outOfStockProduct',
|
|
442
|
+
defaultMessage:
|
|
443
|
+
'This item is currently out of stock'
|
|
444
|
+
})
|
|
445
|
+
: ''
|
|
446
|
+
}
|
|
447
|
+
classes={{
|
|
448
|
+
rootClass: '!px-0 !py-3',
|
|
449
|
+
content: 'w-full normal-case font-medium text-[14px]'
|
|
450
|
+
}}
|
|
451
|
+
priority="high"
|
|
452
|
+
type="submit"
|
|
453
|
+
>
|
|
454
|
+
{cartCallToActionText}
|
|
455
|
+
</Button>
|
|
456
|
+
</section>
|
|
457
|
+
) : (
|
|
458
|
+
<div className={classes.unavailableContainer}>
|
|
459
|
+
<Info />
|
|
460
|
+
<p>
|
|
461
|
+
<FormattedMessage
|
|
462
|
+
id={'productFullDetail.unavailableProduct'}
|
|
463
|
+
defaultMessage={
|
|
464
|
+
'This product is currently unavailable for purchase.'
|
|
465
|
+
}
|
|
466
|
+
/>
|
|
467
|
+
</p>
|
|
468
|
+
</div>
|
|
469
|
+
);
|
|
413
470
|
const ExpandableSection = ({ children, maxHeight = 480 }) => {
|
|
414
471
|
const [expanded, setExpanded] = useState(false);
|
|
415
472
|
const [showButton, setShowButton] = useState(false);
|
|
@@ -554,9 +611,33 @@ const ProductFullDetail = props => {
|
|
|
554
611
|
|
|
555
612
|
const AlertCircleIcon = <Icon src={ShoppingCart} attrs={{ width: 20 }} />;
|
|
556
613
|
|
|
557
|
-
const handleAddToCart = async
|
|
614
|
+
const handleAddToCart = async formValues => {
|
|
558
615
|
try {
|
|
559
|
-
|
|
616
|
+
const parseDepositNumber = (value) => {
|
|
617
|
+
if (value === null || value === undefined) return undefined;
|
|
618
|
+
if (typeof value === 'number') return value;
|
|
619
|
+
const numeric = Number.parseFloat(value.toString().replace(/[^0-9.-]/g, ''));
|
|
620
|
+
return Number.isFinite(numeric) ? numeric : undefined;
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
let preorderPayload;
|
|
624
|
+
if (selectedPaymentType) {
|
|
625
|
+
preorderPayload = {
|
|
626
|
+
pre_order_payment_type: selectedPaymentType
|
|
627
|
+
};
|
|
628
|
+
if (selectedPaymentType === 'deposit') {
|
|
629
|
+
const dep = parseDepositNumber(preOrderDeposite);
|
|
630
|
+
if (dep !== undefined) {
|
|
631
|
+
preorderPayload.pre_order_deposit = dep;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const nextValues = {
|
|
637
|
+
...formValues,
|
|
638
|
+
preorder: preorderPayload
|
|
639
|
+
};
|
|
640
|
+
await originalHandleAddToCart(nextValues);
|
|
560
641
|
} catch (e) {
|
|
561
642
|
addToast({
|
|
562
643
|
type: 'error',
|
|
@@ -686,7 +767,7 @@ const ProductFullDetail = props => {
|
|
|
686
767
|
data-cy="ProductFullDetail-productPrice"
|
|
687
768
|
className={cn(
|
|
688
769
|
classes.productPrice,
|
|
689
|
-
'text-[
|
|
770
|
+
'text-[24px] font-medium leading-[32px] mb-0',
|
|
690
771
|
)}
|
|
691
772
|
>
|
|
692
773
|
<Price
|
|
@@ -697,11 +778,10 @@ const ProductFullDetail = props => {
|
|
|
697
778
|
</p>
|
|
698
779
|
</div>
|
|
699
780
|
<AuctionDetail className="auction_detail-container" />
|
|
700
|
-
<PreorderDetail className={'preorder_detail-container'} />
|
|
701
781
|
<small className='shipping-calculation-notes text-gray-200 text-xs'>
|
|
702
782
|
Shipping is calculated on checkout
|
|
703
783
|
</small>
|
|
704
|
-
<div className='flex flex-col xs_items-center md_items-start gap-[6px] relative'>
|
|
784
|
+
{/* <div className='flex flex-col xs_items-center md_items-start gap-[6px] relative'>
|
|
705
785
|
<div className="gap-x-[10px] gap-y-1 flex xs_flex-col md_flex-row xs_items-center md_items-start relative">
|
|
706
786
|
<Link to={"/seller/"+sellerDetails.url_key} class="flex items-center justify-center gap-[5px] py-1 relative bg-white">
|
|
707
787
|
<div class="flex items-center justify-center gap-[5px] relative">
|
|
@@ -712,7 +792,7 @@ const ProductFullDetail = props => {
|
|
|
712
792
|
</div>
|
|
713
793
|
</Link>
|
|
714
794
|
</div>
|
|
715
|
-
</div>
|
|
795
|
+
</div> */}
|
|
716
796
|
</div>
|
|
717
797
|
</div>
|
|
718
798
|
<FormError
|
|
@@ -743,11 +823,62 @@ const ProductFullDetail = props => {
|
|
|
743
823
|
/>
|
|
744
824
|
</Suspense>
|
|
745
825
|
</div>
|
|
826
|
+
<PreorderDetail
|
|
827
|
+
preOrder={preOrder}
|
|
828
|
+
preOrderDate={preOrderDate}
|
|
829
|
+
preOrderDeposite={preOrderDeposite}
|
|
830
|
+
preOrderNote={preOrderNote}
|
|
831
|
+
preOrderPaymentType={preOrderPaymentType}
|
|
832
|
+
releaseDate={releaseDate}
|
|
833
|
+
className={'preorder_detail-container'}
|
|
834
|
+
paymentTypeOptions={paymentTypeOptions}
|
|
835
|
+
onPaymentTypeChange={setSelectedPaymentType}
|
|
836
|
+
/>
|
|
746
837
|
<div className='product_actions-wrapper'>
|
|
747
838
|
{cartActionContent}
|
|
748
839
|
</div>
|
|
749
840
|
</section>
|
|
750
841
|
</div>
|
|
842
|
+
<div className={cn('product_otherlink-section', 'border border-[lightgray] rounded-[10px] mb-[15px] p-4')}>
|
|
843
|
+
<div className='flex flex-col xs_items-center md_items-start gap-[6px] relative'>
|
|
844
|
+
<div className="gap-x-[10px] gap-y-1 flex xs_flex-col md_flex-row xs_items-center md_items-start relative">
|
|
845
|
+
<Link to={"/seller/"+sellerDetails.url_key} class="flex items-center justify-center gap-[5px] py-1 relative bg-white">
|
|
846
|
+
<div class="flex items-center justify-center gap-[5px] relative">
|
|
847
|
+
<p>Sold by</p>
|
|
848
|
+
<div class="relative xs_hidden lg_flex w-fit font-medium text-[#f76b1c] text-[14px] tracking-[0] leading-[20px] whitespace-nowrap">
|
|
849
|
+
{sellerDetails ? sellerDetails.name : ''}
|
|
850
|
+
</div>
|
|
851
|
+
</div>
|
|
852
|
+
</Link>
|
|
853
|
+
</div>
|
|
854
|
+
</div>
|
|
855
|
+
<div className='flex flex-col xs_items-center md_items-start gap-[6px] relative'>
|
|
856
|
+
<div className="gap-x-[10px] gap-y-1 flex xs_flex-col md_flex-row xs_items-center md_items-start relative">
|
|
857
|
+
<div class="flex items-center justify-center gap-[5px] py-1 relative bg-white">
|
|
858
|
+
<div class="flex items-center justify-center gap-[5px] relative">
|
|
859
|
+
<div className='flex flex-wrap items-start gap-4 relative'>
|
|
860
|
+
<Link to='#' class="flex items-center justify-center gap-[5px] py-1 px-5 relative bg-white rounded-[30px] border border-solid border-[#f76b1c]">
|
|
861
|
+
<div class="flex items-center justify-center gap-[10px] relative">
|
|
862
|
+
<Sms color="#f76b1c" size={14} variant="Outline" className='stroke-[#f76b1c]' />
|
|
863
|
+
<div class="relative xs_hidden lg_flex w-fit font-medium text-[#f76b1c] text-[14px] tracking-[0] leading-[20px] whitespace-nowrap">
|
|
864
|
+
Send Message
|
|
865
|
+
</div>
|
|
866
|
+
</div>
|
|
867
|
+
</Link>
|
|
868
|
+
<Link to={"/seller/"+sellerDetails.url_key} class="flex items-center justify-center gap-[5px] py-1 px-5 relative bg-white rounded-[30px] border border-solid border-[#f76b1c]">
|
|
869
|
+
<div class="flex items-center justify-center gap-[10px] relative">
|
|
870
|
+
<Shop color="#f76b1c" size={14} variant="Outline" className='stroke-[#f76b1c]' />
|
|
871
|
+
<div class="relative xs_hidden lg_flex w-fit font-medium text-[#f76b1c] text-[14px] tracking-[0] leading-[20px] whitespace-nowrap">
|
|
872
|
+
Visit Store
|
|
873
|
+
</div>
|
|
874
|
+
</div>
|
|
875
|
+
</Link>
|
|
876
|
+
</div>
|
|
877
|
+
</div>
|
|
878
|
+
</div>
|
|
879
|
+
</div>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
751
882
|
<div className={cn('product_otherlink-section', 'border border-[lightgray] rounded-[10px] mb-[15px]')}>
|
|
752
883
|
<LinkToOtherStores productDetails={productDetails} />
|
|
753
884
|
</div>
|
|
@@ -89,10 +89,20 @@ export const GET_STORE_CONFIG_DATA = gql`
|
|
|
89
89
|
}
|
|
90
90
|
`;
|
|
91
91
|
|
|
92
|
+
export const GET_PAYMENT_TYPES = gql`
|
|
93
|
+
query getPaymentTypeOptions {
|
|
94
|
+
getPaymentType {
|
|
95
|
+
label
|
|
96
|
+
value
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
|
|
92
101
|
|
|
93
102
|
export default {
|
|
94
103
|
getCategoryContentQuery: GET_CATEGORY_CONTENT,
|
|
95
104
|
getProductFiltersByCategoryQuery: GET_PRODUCT_FILTERS_BY_CATEGORY,
|
|
96
105
|
getCategoryAvailableSortMethodsQuery: GET_CATEGORY_AVAILABLE_SORT_METHODS,
|
|
97
|
-
getStoreConfigQuery: GET_STORE_CONFIG_DATA
|
|
106
|
+
getStoreConfigQuery: GET_STORE_CONFIG_DATA,
|
|
107
|
+
getPaymentTypesQuery: GET_PAYMENT_TYPES
|
|
98
108
|
};
|