@riosst100/pwa-marketplace 3.0.3 → 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.
Files changed (58) hide show
  1. package/i18n/en_US.json +3 -3
  2. package/i18n/id_ID.json +2 -2
  3. package/package.json +1 -1
  4. package/src/componentOverrideMapping.js +1 -0
  5. package/src/components/CrossSeller/item.js +3 -4
  6. package/src/components/LinkToOtherStores/index.js +4 -4
  7. package/src/components/OrderDetail/components/itemsOrdered.js +1 -6
  8. package/src/components/ProductListTab/productListTab.js +1 -1
  9. package/src/components/RMAPage/components/productItem.js +13 -43
  10. package/src/components/SellerCoupon/index.js +1 -0
  11. package/src/components/SellerCoupon/sellerCoupon.js +41 -6
  12. package/src/components/SellerCoupon/sellerCoupon.module.css +22 -0
  13. package/src/components/SellerCoupon/sellerCouponCheckout.js +36 -19
  14. package/src/components/SellerCoupon/sellerCouponCheckout.shimmer.js +21 -0
  15. package/src/components/commons/Select/index.js +8 -4
  16. package/src/overwrites/peregrine/lib/talons/CartPage/PriceSummary/priceSummaryFragments.gql.js +3 -0
  17. package/src/overwrites/peregrine/lib/talons/CartPage/PriceSummary/usePriceSummary.js +2 -2
  18. package/src/overwrites/peregrine/lib/talons/CartPage/ProductListing/productListingFragments.gql.js +4 -0
  19. package/src/overwrites/peregrine/lib/talons/CheckoutPage/checkoutPage.extended.gql.js +20 -1
  20. package/src/overwrites/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js +3 -3
  21. package/src/overwrites/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +7 -8
  22. package/src/overwrites/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js +5 -0
  23. package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.js +20 -1
  24. package/src/overwrites/venia-ui/lib/components/CartPage/PriceAdjustments/CouponCode/couponCode.js +24 -27
  25. package/src/overwrites/venia-ui/lib/components/CartPage/PriceAdjustments/CouponCode/couponCode.module.css +2 -0
  26. package/src/overwrites/venia-ui/lib/components/CartPage/PriceSummary/priceSummary.js +14 -5
  27. package/src/overwrites/venia-ui/lib/components/CartPage/ProductListing/product.js +41 -1
  28. package/src/overwrites/venia-ui/lib/components/CartPage/ProductListing/product.module.css +1 -1
  29. package/src/overwrites/venia-ui/lib/components/CartPage/ProductListingBySeller/productListingBySeller.js +41 -8
  30. package/src/overwrites/venia-ui/lib/components/CartPage/cartPage.js +0 -7
  31. package/src/overwrites/venia-ui/lib/components/CheckoutPage/ItemsReview/item.js +43 -2
  32. package/src/overwrites/venia-ui/lib/components/CheckoutPage/ItemsReview/item.module.css +36 -0
  33. package/src/overwrites/venia-ui/lib/components/CheckoutPage/ItemsReview/itemsReview.js +8 -2
  34. package/src/overwrites/venia-ui/lib/components/CheckoutPage/OrderSummary/orderSummary.js +4 -0
  35. package/src/overwrites/venia-ui/lib/components/CheckoutPage/PriceAdjustments/priceAdjustments.js +65 -57
  36. package/src/overwrites/venia-ui/lib/components/CheckoutPage/PriceAdjustments/priceAdjustments.module.css +29 -0
  37. package/src/overwrites/venia-ui/lib/components/CheckoutPage/checkoutPage.js +9 -4
  38. package/src/overwrites/venia-ui/lib/components/CheckoutPage/checkoutPage.module.css +2 -0
  39. package/src/overwrites/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilters.js +1 -1
  40. package/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js +1 -1
  41. package/src/overwrites/venia-ui/lib/components/Gallery/item.js +2 -10
  42. package/src/overwrites/venia-ui/lib/components/Gallery/item.module.css +5 -4
  43. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/Reviews/index.js +1 -0
  44. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/Reviews/reviewModal.js +316 -0
  45. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/Reviews/starInput.js +33 -0
  46. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/orderHistoryPage.js +29 -2
  47. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/orderRow.js +70 -34
  48. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/CustomAttributes/customAttributes.js +10 -2
  49. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/modalFormReview.js +8 -8
  50. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/preOrderDetail.js +195 -37
  51. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/productReview.js +4 -27
  52. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +194 -63
  53. package/src/talons/ProductContent/productContent.gql.js +11 -1
  54. package/src/talons/ProductContent/useProductContent.js +14 -2
  55. package/src/talons/ReviewModal/reviewModal.gql.js +45 -0
  56. package/src/talons/ReviewModal/useReviewModal.js +87 -0
  57. package/src/components/RMAPage/components/productItem.css +0 -15
  58. package/src/components/RMAPage/components/productItem.module.css +0 -15
@@ -0,0 +1,33 @@
1
+ import React, { useState } from 'react';
2
+
3
+ const StarRating = ({ totalStars = 5, rating, onRatingChange }) => {
4
+ const [hover, setHover] = useState(0);
5
+
6
+ const handleClick = (newRating) => {
7
+ onRatingChange(newRating);
8
+ };
9
+
10
+ return (
11
+ <div className="star-rating flex flex-row gap-x-1">
12
+ {[...Array(totalStars)].map((star, index) => {
13
+ const ratingClass = index < (hover || rating) ? 'filled text-yellow-400' : 'empty text-gray-400';
14
+ return (
15
+ <span
16
+ key={index}
17
+ className={`star ${ratingClass}`}
18
+ onMouseEnter={() => setHover(index + 1)}
19
+ onMouseLeave={() => setHover(0)}
20
+ onClick={() => handleClick(index + 1)}
21
+ >
22
+ <svg className={ratingClass} width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
23
+ <path d="M8.00887 2.04754L9.03554 4.10087C9.17554 4.38671 9.54887 4.66087 9.86387 4.71337L11.7247 5.02254C12.9147 5.22087 13.1947 6.08421 12.3372 6.93587L10.8905 8.38254C10.6455 8.62754 10.5114 9.10004 10.5872 9.43837L11.0014 11.2292C11.328 12.6467 10.5755 13.195 9.32137 12.4542L7.5772 11.4217C7.2622 11.235 6.74304 11.235 6.4222 11.4217L4.67804 12.4542C3.4297 13.195 2.67137 12.6409 2.99804 11.2292L3.4122 9.43837C3.48804 9.10004 3.35387 8.62754 3.10887 8.38254L1.6622 6.93587C0.810536 6.08421 1.0847 5.22087 2.2747 5.02254L4.13554 4.71337C4.4447 4.66087 4.81804 4.38671 4.95804 4.10087L5.9847 2.04754C6.5447 0.933372 7.4547 0.933372 8.00887 2.04754Z" fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" />
24
+ </svg>
25
+
26
+ </span>
27
+ );
28
+ })}
29
+ </div>
30
+ );
31
+ };
32
+
33
+ export default StarRating;
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useEffect } from 'react';
1
+ import React, { useMemo, useEffect, useState } from 'react';
2
2
  import { useIntl, FormattedMessage } from 'react-intl';
3
3
  import {
4
4
  Search as SearchIcon,
@@ -25,6 +25,7 @@ import ResetButton from './resetButton';
25
25
  import { SearchNormal } from 'iconsax-react';
26
26
  import cn from 'classnames';
27
27
  import Pagination from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/Pagination';
28
+ import { ReviewModal } from './Reviews'
28
29
 
29
30
  const errorIcon = (
30
31
  <Icon
@@ -52,6 +53,8 @@ const OrderHistoryPage = props => {
52
53
  } = talonProps;
53
54
  const [, { addToast }] = useToasts();
54
55
  const { formatMessage } = useIntl();
56
+ const [reviewOrder, setReviewOrder] = useState(null);
57
+
55
58
  const PAGE_TITLE = formatMessage({
56
59
  id: 'orderHistoryPage.pageTitleText',
57
60
  defaultMessage: 'My Orders'
@@ -73,7 +76,7 @@ const OrderHistoryPage = props => {
73
76
 
74
77
  const orderRows = useMemo(() => {
75
78
  return orders.map(order => {
76
- return <OrderRow key={order.id} order={order} />;
79
+ return <OrderRow setReviewOrder={setReviewOrder} key={order.id} order={order} />;
77
80
  });
78
81
  }, [orders]);
79
82
 
@@ -163,8 +166,32 @@ const OrderHistoryPage = props => {
163
166
  }
164
167
  }, [addToast, errorMessage]);
165
168
 
169
+ console.log('reviewOrder',reviewOrder)
170
+
166
171
  return (
167
172
  <OrderHistoryContextProvider>
173
+ <ReviewModal
174
+ // open={open}
175
+ setReviewOrder={setReviewOrder}
176
+ reviewOrder={reviewOrder}
177
+ // defaultNickname={defaultNickname}
178
+ // ratingsMetadata={ratingsMetadataData?.productReviewRatingsMetadata?.items || []}
179
+ // loadingRatingsMetadata={loadingRatingsMetadata}
180
+ // onSubmit={async (formValues) => {
181
+ // setSubmitting(true);
182
+ // const input = {
183
+ // nickname: formValues.nickname,
184
+ // summary: formValues.summary,
185
+ // text: formValues.review,
186
+ // sku: product?.sku,
187
+ // ratings: formValues.ratings
188
+ // };
189
+ // const result = await handleSubmitReview(input);
190
+ // setSubmitting(false);
191
+ // if (result.success) setOpen(false);
192
+ // }}
193
+ // submitting={submitting}
194
+ />
168
195
  <div className={classes.root}>
169
196
  <StoreTitle>{PAGE_TITLE}</StoreTitle>
170
197
  <div aria-live="polite" className="text-xl font-medium text-left">
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useCallback, useState } from 'react';
2
2
  import { arrayOf, number, shape, string } from 'prop-types';
3
3
  import { ChevronDown, ChevronUp } from 'react-feather';
4
4
  import { FormattedMessage, useIntl } from 'react-intl';
@@ -17,7 +17,7 @@ import PlaceholderImage from '@magento/venia-ui/lib/components/Image/placeholder
17
17
  import { Link, useHistory } from 'react-router-dom';
18
18
 
19
19
  const OrderRow = props => {
20
- const { order } = props;
20
+ const { order, setReviewOrder } = props;
21
21
  const { formatMessage } = useIntl();
22
22
  const history = useHistory();
23
23
  const {
@@ -179,9 +179,22 @@ const OrderRow = props => {
179
179
  });
180
180
  };
181
181
 
182
+ const handleWriteReview = useCallback(( order ) => {
183
+ console.log('order',order)
184
+ if (order) {
185
+ setReviewOrder(order);
186
+ }
187
+ }, [setReviewOrder]);
188
+
189
+
190
+ // ReviewModal
191
+
182
192
  return (
183
193
  <li className={classes.root}>
184
- <div className='flex flex-col md_flex-row md_items-start justify-between mb-2.5'>
194
+ <div className='flex flex-col md_flex-row md_items-start justify-between mb-2.5' style={{
195
+ "borderBottom": "1px solid #e6e9ea",
196
+ "paddingBottom": "10px"
197
+ }}>
185
198
  <div className='flex flex-col ml-[14px] mt-[5px]'>
186
199
  <div className="flex gap-x-[15px] flex-col">
187
200
  <div className='flex gap-x-[5px] items-center'>
@@ -202,13 +215,16 @@ const OrderRow = props => {
202
215
  </div>
203
216
  </div>
204
217
  </div>
205
- <div className='flex flex-col md_flex-row justify-between mb-2.5'>
206
- <div className='flex flex-col ml-[5px] gap-y-[20px]'>
218
+ <div className='flex flex-col md_flex-column justify-between mb-0'>
219
+ <div className='flex flex-col ml-[5px] gap-y-[10px]'>
207
220
  {/* Per-item horizontal flex: image + detail */}
208
221
  {items && items.length > 0 && items.map((it, idx) => {
209
222
  const { url, label } = getThumbnailForSku(it.product_sku, it.product_name, idx);
210
223
  return (
211
- <div key={it.id || idx} className='flex flex-row gap-4 mb-2'>
224
+ <div key={it.id || idx} className='flex flex-row gap-4 mb-1 justify-between' style={{
225
+ "borderBottom": "1px solid #e6e9ea",
226
+ "paddingBottom": "10px"
227
+ }}>
212
228
  <div className={classes.productImage}>
213
229
  {url ? (
214
230
  <img
@@ -222,15 +238,16 @@ const OrderRow = props => {
222
238
  )}
223
239
  </div>
224
240
  <div className='flex flex-col gap-1 pb-2 last_pb-0 max-w-[375px]'>
225
- <div className={cn(classes.productName, 'text-[14] font-medium')}>
241
+ <div className={cn(classes.productName, 'text-[14px] font-medium')}>
226
242
  <span>{it.product_name}</span>
227
243
  </div>
228
- <div className="text-[14] text-gray-300">
229
- <span>Qty : {`${it.quantity_ordered}`}</span>
230
- <br />
231
- <span>
232
- Price : <Price currencyCode={it?.product_sale_price?.currency} value={it?.product_sale_price?.value} />
233
- </span>
244
+ <div className="text-[12px]">
245
+ <span>x{`${it.quantity_ordered}`}</span>
246
+ </div>
247
+ </div>
248
+ <div class="flex flex-col gap-1 pb-2 last_pb-0 justify-center">
249
+ <div class="text-[14px] text-blue-700">
250
+ <span><Price currencyCode={it?.product_sale_price?.currency} value={it?.product_sale_price?.value} /></span>
234
251
  </div>
235
252
  </div>
236
253
  </div>
@@ -238,45 +255,64 @@ const OrderRow = props => {
238
255
  })}
239
256
  </div>
240
257
  {/* Right column: bottom-aligned Order Total + CTA */}
241
- <div className='flex flex-col items-end gap-2 md_pl-10 md_self-end mr-4 mb-1'>
242
- <div className="md_text-right">
243
- <span className="text-[14] text-gray-200 block mb-1">
258
+ <div className='flex flex-col items-end gap-4 md_pl-10 md_self-end mr-4 mb-1 pt-2'>
259
+ <div className="md_text-right flex flex-row gap-5 items-center">
260
+ <span className="text-[14px] text-gray-200 block mb-1">
244
261
  <FormattedMessage
245
262
  id={'orderRow.orderTotalText'}
246
263
  defaultMessage={'Order Total'}
247
- />
264
+ />:
248
265
  </span>
249
- <div className="text-lg font-medium">{orderTotalPrice}</div>
266
+ <div className="text-lg font-medium text-blue-700">{orderTotalPrice}</div>
250
267
  </div>
251
268
  <div className="flex flex-row gap-2 w-full justify-end items-center">
269
+ {showNewReturnButton && (
270
+ <>
271
+ {/* <ReviewModal order={reviewOrder} /> */}
272
+ <span className="bg-blue-700 hover:bg-white hover:text-blue-700 hover:border hover:border-blue-700 rounded-full px-[30px] py-[8px] text-[13px] font-medium text-white transition-all duration-300 ease-in-out" onClick={() => handleWriteReview(order)}>
273
+ <FormattedMessage
274
+ id={'orderRow.ViewTransactionDetail'}
275
+ defaultMessage={'Write a Review'}
276
+ />
277
+ </span>
278
+ </>
279
+ )}
252
280
  <Link
253
281
  to={{
254
282
  pathname: `/order-history/view/${orderNumber}`,
255
283
  state: { order }
256
284
  }}
257
285
  >
258
- <span className="bg-blue-700 hover:bg-white hover:text-blue-700 hover:border hover:border-blue-700 rounded-full px-[30px] py-[5px] text-[13px] font-medium text-white transition-all duration-300 ease-in-out">
286
+ <span className="bg-blue-700 hover:bg-white hover:text-blue-700 hover:border hover:border-blue-700 rounded-full px-[30px] py-[8px] text-[13px] font-medium text-white transition-all duration-300 ease-in-out">
259
287
  <FormattedMessage
260
288
  id={'orderRow.ViewTransactionDetail'}
261
- defaultMessage={'View Order Detail'}
289
+ defaultMessage={'View Order'}
262
290
  />
263
291
  </span>
264
292
  </Link>
265
293
  {showNewReturnButton && (
266
- <div
267
- className={cn(
268
- "cursor-pointer border border-blue-700 bg-white text-blue-700 hover:bg-blue-50 hover:text-blue-700 hover:border-blue-700",
269
- "rounded-full px-[10px] py-[3px] text-[13px] font-medium transition-all duration-300 ease-in-out flex items-center gap-2"
270
- )}
271
- onClick={handleNewReturn}
272
- title={formatMessage({ id: 'orderRow.returnProduct', defaultMessage: 'Return Product' })}
273
- >
274
- <ConvertCard size={20} color="#f26313" />
275
- <FormattedMessage
276
- id={'orderRow.ReturnItems'}
277
- defaultMessage={'Return Items'}
278
- />
279
- </div>
294
+ <>
295
+ <span className="bg-white border border-blue-700 bg-white text-blue-700 hover:bg-blue-50 hover:text-blue-700 hover:border-blue-700 rounded-full px-[30px] py-[8px] text-[13px] font-medium text-white transition-all duration-300 ease-in-out">
296
+ <FormattedMessage
297
+ id={'orderRow.ReturnItems'}
298
+ defaultMessage={'Return Items'}
299
+ />
300
+ </span>
301
+ {/* <div
302
+ className={cn(
303
+ "cursor-pointer ",
304
+ "rounded-full px-[10px] py-[3px] text-[13px] font-medium transition-all duration-300 ease-in-out flex items-center gap-2"
305
+ )}
306
+ onClick={handleNewReturn}
307
+ title={formatMessage({ id: 'orderRow.returnProduct', defaultMessage: 'Return Product' })}
308
+ >
309
+ <ConvertCard size={20} color="#f26313" />
310
+ <FormattedMessage
311
+ id={'orderRow.ReturnItems'}
312
+ defaultMessage={'Return Items'}
313
+ />
314
+ </div> */}
315
+ </>
280
316
  )}
281
317
  </div>
282
318
  </div>
@@ -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) {
@@ -9,17 +9,17 @@ import StarRating from './starInput';
9
9
 
10
10
  import { primary900 } from '@riosst100/pwa-marketplace/src/theme/vars';
11
11
 
12
- const modalFormReview = (props) => {
13
- const { open, setOpen, ratingsMetadata = [], loadingRatingsMetadata, onSubmit, submitting, defaultNickname } = props;
12
+ const ModalFormReview = (props) => {
13
+ const { reviewOrder, setReviewOrder, ratingsMetadata = [], loadingRatingsMetadata, onSubmit, submitting, defaultNickname } = props;
14
14
 
15
15
  const [formState, setFormState] = useState({ nickname: defaultNickname || '' });
16
16
 
17
17
  useEffect(() => {
18
- if (open && defaultNickname && !formState.nickname) {
18
+ if (reviewOrder && defaultNickname && !formState.nickname) {
19
19
  setFormState(prev => ({ ...prev, nickname: defaultNickname }));
20
20
  }
21
21
  // eslint-disable-next-line react-hooks/exhaustive-deps
22
- }, [open, defaultNickname]);
22
+ }, [reviewOrder, defaultNickname]);
23
23
 
24
24
  // ratings: [{ id, value_id }]
25
25
  const [ratings, setRatings] = useState([]);
@@ -45,7 +45,7 @@ const modalFormReview = (props) => {
45
45
  return (
46
46
  <>
47
47
  <Modal
48
- open={open}
48
+ reviewOrder={reviewOrder}
49
49
  className="modal_form_review !p-[30px] md_min-w-[650px]"
50
50
  >
51
51
  <div className='form_review-container'>
@@ -53,7 +53,7 @@ const modalFormReview = (props) => {
53
53
  <div className='text-lg text-black font-medium'>
54
54
  Write Review
55
55
  </div>
56
- <button onClick={() => { setOpen(!open) }} >
56
+ <button onClick={() => { setReviewOrder(!reviewOrder) }} >
57
57
  <X size={24} color={primary900} />
58
58
  </button>
59
59
  </div>
@@ -145,7 +145,7 @@ const modalFormReview = (props) => {
145
145
  classes={{
146
146
  content: 'capitalize text-[16px] font-medium'
147
147
  }}
148
- onClick={() => setOpen(false)}
148
+ onClick={() => setReviewOrder(false)}
149
149
  type="button"
150
150
  >
151
151
  Cancel
@@ -168,4 +168,4 @@ const modalFormReview = (props) => {
168
168
  );
169
169
  };
170
170
 
171
- export default modalFormReview;
171
+ export default ModalFormReview;
@@ -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, productDetails } = props
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 = preorderData && preorderData.is_preorder ? true : false;
11
- const preOrderNotes = preorderData ? preorderData.preorder_notes : null;
12
- const preOrderAvailableDate = preorderData ? new Date(preorderData.preorder_availability) : null;
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
- const month = ["January","February","March","April","May","June","July","August","September","October","November","December"];
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-2.5',
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='flex flex-row justify-between gap-[20px]'>
25
- <div className='flex flex-col gap-2.5'>
26
- <div className='text-[14px] font-medium text-gray-500'>
27
- Release Date
28
- </div>
29
- <div className='text-[16px] font-medium text-colorDefault text-center'>
30
- {preOrderAvailableDate ? preOrderAvailableDate.getDate() + ', ' + month[preOrderAvailableDate.getMonth()] + ' ' + preOrderAvailableDate.getFullYear() : 'No Release Date.'}
31
- {/* 27, February 2024 */}
32
- </div>
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="w-[1px] h-[50px]" />
35
- <div className='flex flex-col gap-2.5'>
36
- <div className='text-[14px] font-medium text-gray-500'>
37
- Last Date
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="w-[1px] h-[50px]" />
44
- <div className='flex flex-col gap-2.5'>
45
- <div className='text-[14px] font-medium text-gray-500'>
46
- Deposite
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
  ) : ''