@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
@@ -5,15 +5,17 @@ import { useMutation, useQuery } from '@apollo/client';
5
5
  import { useCartContext } from '@magento/peregrine/lib/context/cart';
6
6
 
7
7
  import { useStyle } from '@magento/venia-ui/lib/classify';
8
- import LoadingIndicator from '@magento/venia-ui/lib/components/LoadingIndicator';
9
8
  import { Accordion, Section } from '@magento/venia-ui/lib/components/Accordion';
10
9
  import SellerCouponCheckout from '@riosst100/pwa-marketplace/src/components/SellerCoupon/sellerCouponCheckout';
11
10
  import couponCodeOperations from '@magento/peregrine/lib/talons/CartPage/PriceAdjustments/CouponCode/couponCode.gql';
12
11
  import GiftCardSection from '@magento/venia-ui/lib/components/CartPage/PriceAdjustments/giftCardSection';
13
12
  import GiftOptionsSection from '@magento/venia-ui/lib/components/CartPage/PriceAdjustments/giftOptionsSection';
14
-
13
+ import CouponCode from '@magento/venia-ui/lib/components/CartPage/PriceAdjustments/CouponCode';
14
+ import Shimmer from '@magento/venia-ui/lib/components/Shimmer';
15
15
  import defaultClasses from './priceAdjustments.module.css';
16
16
  import cn from 'classnames';
17
+ import LoadingIndicator from '@magento/venia-ui/lib/components/LoadingIndicator';
18
+ import { FormattedMessage } from 'react-intl';
17
19
 
18
20
  /**
19
21
  * PriceAdjustments component for the Checkout page.
@@ -32,7 +34,8 @@ const PriceAdjustments = props => {
32
34
  couponCodeOperations.getAppliedCouponsQuery,
33
35
  { variables: { cartId }, skip: !cartId }
34
36
  );
35
- const [sectionOpen, setSectionOpen] = useState(false);
37
+ const [sectionOpen, setSectionOpen] = useState(true);
38
+ const [couponModalOpen, setCouponModalOpen] = useState(false);
36
39
 
37
40
  const handleApplyFromClaim = useCallback(async (item) => {
38
41
  if (!item?.code || !cartId) return;
@@ -48,6 +51,10 @@ const PriceAdjustments = props => {
48
51
  }
49
52
  }, [applyCoupon, cartId, setPageIsUpdating]);
50
53
 
54
+ const handleViewCoupons = useCallback(() => {
55
+ setCouponModalOpen(true);
56
+ }, [setCouponModalOpen]);
57
+
51
58
  const handleRemoveCoupon = useCallback(async () => {
52
59
  if (!cartId) return;
53
60
  try {
@@ -64,62 +71,63 @@ const PriceAdjustments = props => {
64
71
 
65
72
  return (
66
73
  <div className={classes.root}>
67
- <Accordion
68
- canOpenMultiple={true}
69
- classes={{
70
- root: 'w-full'
71
- }}
72
- >
73
- <Section
74
- data-cy="PriceAdjustments-couponCodeSection"
75
- id={'coupon_code'}
76
- title={formatMessage({
74
+ <div className={classes.coupon_container}>
75
+ <h5 className={classes.cardTitle}>
76
+ {formatMessage({
77
77
  id: 'checkoutPage.couponCode',
78
- defaultMessage: 'Apply Coupon Code'
78
+ defaultMessage: 'Apply Coupon'
79
79
  })}
80
- classes={{
81
- root: cn('border border-gray-100 rounded-md max-w-[400px] p-2.5'),
82
- title: 'text-[16px] font-medium cursor-pointer',
83
- title_wrapper: 'gap-sm grid grid-cols-1 grid-flow-col h-[38px] items-center justify-items-start',
84
- contents_container: 'p-0'
85
- }}
86
- onClick={() => setSectionOpen(prev => !prev)}
87
- >
88
- <Suspense fallback={<LoadingIndicator />}>
89
- {sectionOpen && (
90
- <SellerCouponCheckout
91
- couponData={couponData}
92
- couponLoading={couponLoading || applyingCoupon || appliedLoading || removingCoupon}
93
- couponError={couponError}
94
- autoOpen={true}
95
- closeOnClaim={true}
96
- onSelectCoupon={handleApplyFromClaim}
97
- onTriggerRender={() => null}
98
- />
99
- )}
100
- {appliedCoupons.length > 0 && (
101
- <div className="mt-3 flex items-center gap-2 flex-wrap">
102
- {appliedCoupons.map(c => (
103
- <div key={c.code} className="flex items-center gap-2 bg-[#fff8f1] border border-[#ffb891] rounded px-2 py-1 text-sm font-medium text-[#f76b1c]">
104
- <span>{c.code}</span>
105
- <button
106
- type="button"
107
- onClick={handleRemoveCoupon}
108
- disabled={removingCoupon}
109
- className="text-[#a94442] hover:text-[#f26313] disabled:opacity-50"
110
- aria-label={formatMessage({ id: 'checkoutPage.removeCoupon', defaultMessage: 'Remove coupon' })}
111
- >
112
- ×
113
- </button>
114
- </div>
115
- ))}
116
- </div>
117
- )}
118
- </Suspense>
119
- </Section>
120
- <GiftCardSection setIsCartUpdating={setPageIsUpdating} />
121
- <GiftOptionsSection />
122
- </Accordion>
80
+ </h5>
81
+ {applyingCoupon || appliedLoading ? <LoadingIndicator classes={{ root: classes.loading }}>
82
+ <FormattedMessage
83
+ id={'checkoutPage.loadingApplyingCoupon'}
84
+ defaultMessage={'Applying Coupon'}
85
+ />
86
+ </LoadingIndicator> : removingCoupon ? <LoadingIndicator classes={{ root: classes.loading }}>
87
+ <FormattedMessage
88
+ id={'checkoutPage.loadingRemovingCoupon'}
89
+ defaultMessage={'Removing Coupon'}
90
+ />
91
+ </LoadingIndicator> :
92
+ <Suspense fallback={<LoadingIndicator />}>
93
+ <CouponCode setIsCartUpdating={setPageIsUpdating} />
94
+ {appliedCoupons.length > 0 && (
95
+ <div className="mt-3 flex items-center gap-2 flex-wrap">
96
+ {appliedCoupons.map(c => {
97
+ const coupon = couponData.sellerCoupons.items.find(item => item.code === c.code);
98
+ return <div key={c.code} className="flex items-center gap-2 bg-[#fff8f1] border border-[#ffb891] rounded px-2 py-1 text-sm font-medium text-[#f76b1c]">
99
+ <span>{coupon.name} ({coupon.code})</span>
100
+ <button
101
+ type="button"
102
+ onClick={handleRemoveCoupon}
103
+ disabled={removingCoupon}
104
+ className="text-[#a94442] hover:text-[#f26313] disabled:opacity-50"
105
+ aria-label={formatMessage({ id: 'checkoutPage.removeCoupon', defaultMessage: 'Remove coupon' })}
106
+ >
107
+ ×
108
+ </button>
109
+ </div>
110
+ })}
111
+ </div>
112
+ )}
113
+ {!appliedCoupons.length && couponData?.sellerCoupons?.items.length ? <button class="p-2 text-[13px] text-blue-700 border border-solid cursor-pointer rounded-full w-full mt-4" onClick={handleViewCoupons}>Select coupon to apply</button> : ''}
114
+ {couponModalOpen && (
115
+ <SellerCouponCheckout
116
+ couponData={couponData}
117
+ couponLoading={couponLoading || applyingCoupon || appliedLoading || removingCoupon}
118
+ couponError={couponError}
119
+ autoOpen={true}
120
+ closeOnClaim={true}
121
+ couponModalOpen={couponModalOpen}
122
+ setCouponModalOpen={setCouponModalOpen}
123
+ onSelectCoupon={handleApplyFromClaim}
124
+ onTriggerRender={() => null}
125
+ />
126
+ )}
127
+ </Suspense>}
128
+ </div>
129
+ <GiftCardSection setIsCartUpdating={setPageIsUpdating} />
130
+ <GiftOptionsSection />
123
131
  </div>
124
132
  );
125
133
  };
@@ -1,2 +1,31 @@
1
1
  .root {
2
+ composes: gap-xs from global;
3
+ composes: grid from global;
4
+ composes: grid-cols-1 from global;
2
5
  }
6
+
7
+ .coupon_container {
8
+ composes: border from global;
9
+ composes: p-4 from global;
10
+ composes: pb-6 from global;
11
+ composes: border-gray-100 from global;
12
+ composes: rounded-md from global;
13
+ }
14
+
15
+ .loading {
16
+ composes: root from '@magento/venia-ui/lib/components/LoadingIndicator/indicator.module.css';
17
+
18
+ composes: text-center from global;
19
+ }
20
+
21
+ .heading {
22
+ composes: border-0 from global;
23
+
24
+ composes: lg_border from global;
25
+ }
26
+
27
+ .cardTitle {
28
+ composes: font-medium from global;
29
+ composes: text-[16px] from global;
30
+ composes: mb-4 from global;
31
+ }
@@ -84,7 +84,7 @@ const CheckoutPage = props => {
84
84
  toggleAddressBookContent,
85
85
  toggleSignInContent
86
86
  } = talonProps;
87
-
87
+
88
88
  const [, { addToast }] = useToasts();
89
89
 
90
90
  const history = useHistory();
@@ -301,7 +301,12 @@ const CheckoutPage = props => {
301
301
  // Show ItemsReview from the beginning, not only at the REVIEW step
302
302
  const itemsReview = (
303
303
  <div className={classes.items_review_container}>
304
- <ItemsReview />
304
+ <ItemsReview
305
+ isPreorder={cartItems?.[0]?.is_preorder}
306
+ preOrderDeposit={cartItems?.[0]?.pre_order_deposit}
307
+ preOrderPaymentType={cartItems?.[0]?.pre_order_payment_type}
308
+ releaseDate={cartItems?.[0]?.product?.release_date}
309
+ />
305
310
  </div>
306
311
  );
307
312
 
@@ -345,7 +350,7 @@ const CheckoutPage = props => {
345
350
  : '')
346
351
  }
347
352
  >
348
- <OrderSummary isUpdating={isUpdating} />
353
+ <OrderSummary isUpdating={isUpdating} placeOrderButton={placeOrderButton} />
349
354
  {itemsReview}
350
355
  </div>
351
356
  );
@@ -430,7 +435,7 @@ const CheckoutPage = props => {
430
435
  {priceAdjustmentsSection}
431
436
  {reviewOrderButton}
432
437
  {orderSummary}
433
- {placeOrderButton}
438
+ {/* {placeOrderButton} */}
434
439
  <GoogleReCaptcha {...recaptchaWidgetProps} />
435
440
  </div>
436
441
  );
@@ -190,6 +190,8 @@
190
190
  grid-column: 1 / span 1;
191
191
  composes: m-auto from global;
192
192
  text-transform: capitalize;
193
+ composes: w-full from global;
194
+ composes: mt-4 from global;
193
195
  }
194
196
  .place_order_button:hover,
195
197
  .place_order_button:focus,
@@ -20,7 +20,7 @@ const CurrentFilters = props => {
20
20
  const { title, value } = item || {};
21
21
  const key = `${group}::${title}_${value}`;
22
22
 
23
- const customAttributeLandingPage = ['bricks_categories','sc_sports_categories','card_game', 'sc_baseball_release','auction','special_price','lof_preorder','sc_baseball_inserts','sc_baseball_parallel','sc_set_type'];
23
+ const customAttributeLandingPage = ['bricks_categories','sc_sports_categories','card_game', 'sc_baseball_release','auction','special_price','lof_preorder','card_pre_orders','sc_baseball_inserts','sc_baseball_parallel','sc_set_type'];
24
24
 
25
25
  if (!customAttributeLandingPage.includes(group)) {
26
26
  elements.push(
@@ -125,7 +125,7 @@ const FilterSidebar = props => {
125
125
  const groupName = filterNames.get(group);
126
126
  const frontendInput = filterFrontendInput.get(group);
127
127
 
128
- const hideFilters = ['card_game','bricks_categories','sc_sports_categories','trains','trains_locomotives','trains_supplies_type','lof_preorder','auction','special_price','sc_baseball_inserts','sc_baseball_parallel','sale','sc_set_type','sc_brands'];
128
+ const hideFilters = ['card_game','bricks_categories','sc_sports_categories','trains','trains_locomotives','trains_supplies_type','lof_preorder','card_pre_orders','auction','special_price','sc_baseball_inserts','sc_baseball_parallel','sale','sc_set_type','sc_brands'];
129
129
  if (!allowedFiltersArr?.length || filterState.size && allowedFiltersArr.length || !filterState.size && allowedFiltersArr.length && allowedFiltersArr.includes(group)) {
130
130
  if (!hideFilters.includes(group) && groupName && !group.includes('card_release') && !group.includes('card_set') && !group.includes('sc_set_name')) {
131
131
 
@@ -140,7 +140,7 @@ const GalleryItem = props => {
140
140
  data-cy="GalleryItem-name">{name}</span>
141
141
  {/* </Link> */}
142
142
  <div data-cy="GalleryItem-price" className={cn(classes.price, 'mb-2.5 pt-2.5')}>
143
- <p className='font-medium !text-[#f76b1c] text-[1.1rem] !mb-1 !leading-[14px]'>
143
+ <p className='font-medium !text-[#f76b1c] !mb-1'>
144
144
  <Price
145
145
  value={priceSource.value}
146
146
  currencyCode={priceSource.currency}
@@ -154,16 +154,8 @@ const GalleryItem = props => {
154
154
  </p> */}
155
155
  </div>
156
156
 
157
- <div data-cy="GalleryItem-Rating" className={cn('flex gap-[5px] items-center mb-2')}>
158
- <Star />
159
- <span className='text-[12px] leading-normal'>
160
- {rating_summary}
161
- </span>
162
- </div>
163
-
164
157
  <div data-cy="GalleryItem-Seller" className={cn('flex gap-[5px] items-center')}>
165
- <Verify size={17} className='text-blue-700' variant="Bold" />
166
- <span className='text-[12px] text-gray-300 leading-normal'>
158
+ <span className='text-[12px] text-gray-400 leading-normal'>
167
159
  {seller}
168
160
  </span>
169
161
  </div>
@@ -74,11 +74,11 @@
74
74
  }
75
75
 
76
76
  .name {
77
- composes: font-medium from global;
77
+ /* composes: font-medium from global; */
78
78
  composes: text-base from global;
79
79
  composes: mb-1 from global;
80
80
  display: -webkit-box;
81
- -webkit-line-clamp: 2;
81
+ -webkit-line-clamp: 3;
82
82
  -webkit-box-orient: vertical;
83
83
  overflow: hidden;
84
84
  line-height: normal;
@@ -87,13 +87,14 @@
87
87
  .name,
88
88
  .price {
89
89
  composes: text-colorDefault from global;
90
- composes: text-[14px] from global;
91
- composes: font-medium from global;
90
+ /* composes: text-[14px] from global; */
91
+ /* composes: font-medium from global; */
92
92
  min-height: 1rem;
93
93
  }
94
94
 
95
95
  .price {
96
96
  padding-top: 2.5;
97
+ composes: font-medium from global;
97
98
  }
98
99
 
99
100
  .unavailableContainer {
@@ -0,0 +1 @@
1
+ export { default as ReviewModal } from './reviewModal';
@@ -0,0 +1,316 @@
1
+ import React, { useMemo, useState, useEffect } from 'react';
2
+ import Modal from '@riosst100/pwa-marketplace/src/components/Modal';
3
+ import { X } from 'react-feather';
4
+ import { Text } from 'informed';
5
+
6
+ import Field from '@magento/venia-ui/lib/components/Field';
7
+ // import TextInput from '@magento/venia-ui/lib/components/TextInput';
8
+ import Button from '@magento/venia-ui/lib/components/Button';
9
+ import { isRequired } from '@magento/venia-ui/lib/util/formValidators';
10
+ import StarRating from './starInput';
11
+
12
+ import { primary900 } from '@riosst100/pwa-marketplace/src/theme/vars';
13
+ import { useReviewModal } from '@riosst100/pwa-marketplace/src/talons/ReviewModal/useReviewModal';
14
+ import { useStyle } from '@magento/venia-ui/lib/classify';
15
+ import defaultClasses from '../orderRow.module.css';
16
+ import cn from 'classnames';
17
+
18
+ import { arrayOf, number, shape, string } from 'prop-types';
19
+ import { ChevronDown, ChevronUp } from 'react-feather';
20
+ import { FormattedMessage, useIntl } from 'react-intl';
21
+ import Price from '@magento/venia-ui/lib/components/Price';
22
+
23
+ import Icon from '@magento/venia-ui/lib/components/Icon';
24
+ import CollapsedImageGallery from '../collapsedImageGallery';
25
+ import OrderProgressBar from '../orderProgressBar';
26
+ import OrderDetails from '../OrderDetails';
27
+ import { Verify, Shop, ConvertCard } from 'iconsax-react';
28
+ import PlaceholderImage from '@magento/venia-ui/lib/components/Image/placeholderImage';
29
+ import { Link, useHistory } from 'react-router-dom';
30
+
31
+ const ReviewModal = (props) => {
32
+ const { reviewOrder, setReviewOrder } = props;
33
+
34
+ const [submitting, setSubmitting] = useState(false)
35
+
36
+ const classes = useStyle(defaultClasses, props.classes);
37
+
38
+ const talonProps = useReviewModal({ reviewOrder, setSubmitting });
39
+
40
+ const {
41
+ ratingsMetadata,
42
+ loadingRatingsMetadata,
43
+ handleSubmitReview,
44
+ loadingCreateReview,
45
+ errorCreateReview,
46
+ defaultNickname,
47
+ handleToastAction
48
+ } = talonProps;
49
+
50
+ const [formState, setFormState] = useState({ nickname: defaultNickname || '' });
51
+
52
+ useEffect(() => {
53
+ if (reviewOrder && defaultNickname && !formState.nickname) {
54
+ setFormState(prev => ({ ...prev, nickname: defaultNickname }));
55
+ }
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
57
+ }, [reviewOrder, defaultNickname]);
58
+
59
+ // ratings: [{ id, value_id }]
60
+ const [ratings, setRatings] = useState(null);
61
+
62
+ const handleRatingChange = (id, value_id) => {
63
+ // setRatings(prev => {
64
+ // const filtered = prev.filter(r => r.id !== id);
65
+ // return [...filtered, { id, value_id }];
66
+ // });
67
+ setRatings(value_id)
68
+ };
69
+
70
+ const handleChange = (field, value) => {
71
+ setFormState(prev => ({ ...prev, [field]: value }));
72
+ };
73
+
74
+ const handleSubmit = async (e) => {
75
+ e.preventDefault();
76
+ // if (onSubmit) {
77
+ // onSubmit({ ...formState, ratings });
78
+ // }
79
+ console.log('formState',formState)
80
+ console.log('e',e)
81
+ setSubmitting(true);
82
+ const input = {
83
+ nickname: formState.nickname,
84
+ summary: formState.summary,
85
+ text: formState.review,
86
+ sku: e.target.sku.value,
87
+ ratings: [{
88
+ id: ratingsMetadata.id,
89
+ value_id: ratings
90
+ }]
91
+ };
92
+ const result = await handleSubmitReview(input);
93
+ setSubmitting(false);
94
+ if (result.success) setReviewOrder(null);
95
+ };
96
+
97
+ // Helper to resolve a product image by SKU with graceful fallback, now prefers product_image_url from order.items
98
+ const getThumbnailForSku = (sku, fallbackLabel, idx) => {
99
+ let url = null;
100
+ let label = fallbackLabel;
101
+ // Prefer product_image_url from order.items if present and is a non-empty string
102
+ if (reviewOrder && reviewOrder.items && reviewOrder.items[idx] && typeof reviewOrder.items[idx].product_image_url === 'string' && reviewOrder.items[idx].product_image_url.trim() !== '') {
103
+ url = reviewOrder.items[idx].product_image_url;
104
+ } else if (imagesData && typeof imagesData === 'object') {
105
+ const byKey = sku && imagesData[sku];
106
+ const byValue = !byKey && sku
107
+ ? Object.values(imagesData).find(entry => entry?.sku === sku)
108
+ : null;
109
+ const chosen = byKey || byValue;
110
+ if (chosen?.thumbnail?.url && typeof chosen.thumbnail.url === 'string' && chosen.thumbnail.url.trim() !== '') {
111
+ url = chosen.thumbnail.url;
112
+ label = chosen.thumbnail.label || label;
113
+ }
114
+ }
115
+ // If url is not a non-empty string, fallback to null (will trigger placeholder)
116
+ if (typeof url !== 'string' || url.trim() === '') {
117
+ url = null;
118
+ }
119
+ return { url, label };
120
+ };
121
+
122
+ console.log('ratings',ratings)
123
+
124
+ const ratingStars = useMemo(() => {
125
+ console.log('ratingsMetadata22',ratingsMetadata)
126
+ if (!ratingsMetadata) {
127
+ return '';
128
+ }
129
+
130
+ // Find value (1-5) for selected value_id
131
+ const selectedValue = ratings && ratingsMetadata.values.find(v => v.value_id === ratings) || '';
132
+ console.log('selectedValue',selectedValue)
133
+
134
+ let starButtons = [];
135
+
136
+ [1,2,3,4,5].map(star => {
137
+ const valObj = ratingsMetadata.values.find(v => v.value === String(star));
138
+ console.log('String(star)',String(star))
139
+ console.log('valObj',valObj)
140
+ if (valObj) {
141
+ starButtons.push(
142
+ <button
143
+ key={valObj.value_id}
144
+ type="button"
145
+ aria-label={`${star} star${star > 1 ? 's' : ''}`}
146
+ className={`focus:outline-none ${selectedValue.value == star ? 'text-yellow-400' : 'text-gray-300'}`}
147
+ onClick={() => handleRatingChange(ratingsMetadata.id, valObj.value_id)}
148
+ >
149
+ <svg xmlns="http://www.w3.org/2000/svg" fill={selectedValue.value >= star ? '#F7C317' : '#D9D9D9'} viewBox="0 0 20 20" width="25" height="25">
150
+ <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.286 3.967a1 1 0 00.95.69h4.175c.969 0 1.371 1.24.588 1.81l-3.38 2.455a1 1 0 00-.364 1.118l1.287 3.966c.3.922-.755 1.688-1.54 1.118l-3.38-2.454a1 1 0 00-1.175 0l-3.38 2.454c-.784.57-1.838-.196-1.54-1.118l1.287-3.966a1 1 0 00-.364-1.118L2.05 9.394c-.783-.57-.38-1.81.588-1.81h4.175a1 1 0 00.95-.69l1.286-3.967z" />
151
+ </svg>
152
+ </button>
153
+ );
154
+ }
155
+ });
156
+
157
+ return (
158
+ <div className="mb-2">
159
+ <label className="block mb-1 font-bold text-gray-700">{ratingsMetadata.name}</label>
160
+ <div className="flex items-center gap-1">
161
+ {starButtons}
162
+ </div>
163
+ </div>
164
+ );
165
+ }, [ratingsMetadata, ratings]);
166
+
167
+ return (
168
+ <>
169
+ <Modal
170
+ open={reviewOrder}
171
+ className="modal_form_review !p-[30px] md_min-w-[650px]"
172
+ >
173
+ <div className='form_review-container'>
174
+ <div className='header_title-modal flex justify-between mb-5'>
175
+ <div className='text-lg text-black font-medium'>
176
+ Write Review
177
+ </div>
178
+ <button onClick={() => { setReviewOrder(!reviewOrder) }} >
179
+ <X size={24} color={primary900} />
180
+ </button>
181
+ </div>
182
+
183
+ {/* <Field id="nickname_field" label={'Nickname'}>
184
+ <input
185
+ id="nickname"
186
+ name="nickname"
187
+ type="text"
188
+ required
189
+ readOnly
190
+ value={formState.nickname || ''}
191
+ onChange={e => handleChange('nickname', e.target.value)}
192
+ data-cy="nickname"
193
+ aria-label={'nickname'}
194
+ placeholder={'e.g John Doe'}
195
+ className="border border-gray-100 rounded px-3 py-2 bg-gray-50 cursor-not-allowed"
196
+ aria-readonly="true"
197
+ />
198
+ </Field> */}
199
+ {/* Ratings breakdown from metadata */}
200
+ {reviewOrder && reviewOrder.items && reviewOrder.items.length > 0 && reviewOrder.items.map((it, idx) => {
201
+ const { url, label } = getThumbnailForSku(it.product_sku, it.product_name, idx);
202
+ return (
203
+ <>
204
+ <form className="flex flex-col gap-y-3" onSubmit={handleSubmit}>
205
+ <div key={it.id || idx} className='flex flex-row gap-4 mb-1 justify-between' style={{
206
+ "borderBottom": "1px solid #e6e9ea",
207
+ "paddingBottom": "10px"
208
+ }}>
209
+ <div className={classes.productImage}>
210
+ {url ? (
211
+ <img
212
+ src={url}
213
+ alt={label}
214
+ width={80}
215
+ className={classes.thumbnail}
216
+ />
217
+ ) : (
218
+ <PlaceholderImage alt={it.product_name} classes={{ root: classes.thumbnail }} width={60} />
219
+ )}
220
+ </div>
221
+ <div className='flex flex-col gap-1 pb-2 last_pb-0 max-w-[375px]'>
222
+ <div className={cn(classes.productName, 'text-[14px] font-medium')}>
223
+ <span>{it.product_name}</span>
224
+ </div>
225
+ <div className="text-[12px]">
226
+ <span>x{`${it.quantity_ordered}`}</span>
227
+ </div>
228
+ </div>
229
+ <div class="flex flex-col gap-1 pb-2 last_pb-0 justify-center">
230
+ <div class="text-[14px] text-blue-700">
231
+ <span><Price currencyCode={it?.product_sale_price?.currency} value={it?.product_sale_price?.value} /></span>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ <div className="flex flex-col gap-y-3">
236
+ {loadingRatingsMetadata ? (
237
+ <div>Loading ratings...</div>
238
+ ) : ratingStars}
239
+ </div>
240
+ <input type="hidden" id="sku" name="sku" value={it.product_sku} />
241
+ {/* <Field id="summary_field" label={'Summary'}>
242
+ <input
243
+ id="summary"
244
+ name="summary"
245
+ type="text"
246
+ required
247
+ value={formState.summary || ''}
248
+ onChange={e => handleChange('summary', e.target.value)}
249
+ data-cy="summary"
250
+ aria-label={'summary'}
251
+ placeholder={'Summary of your rating'}
252
+ className="border border-gray-100 rounded px-3 py-2"
253
+ />
254
+ </Field> */}
255
+ <Field id="summary_field" label={'Summary'}>
256
+ <input
257
+ id="summary"
258
+ name="summary"
259
+ type="text"
260
+ required
261
+ value={formState.summary || ''}
262
+ onChange={e => handleChange('summary', e.target.value)}
263
+ data-cy="summary"
264
+ aria-label={'summary'}
265
+ placeholder={'Summary of your rating'}
266
+ className="border border-gray-100 rounded px-3 py-2"
267
+ />
268
+ </Field>
269
+ <Field id="review_field" label={'Review'}>
270
+ <textarea
271
+ id="review"
272
+ name="review"
273
+ required
274
+ value={formState.review || ''}
275
+ onChange={e => handleChange('review', e.target.value)}
276
+ data-cy="review"
277
+ aria-label={'review'}
278
+ placeholder={'Let us know your thoughts'}
279
+ className="border border-gray-100 rounded px-3 py-2"
280
+ rows={3}
281
+ />
282
+ </Field>
283
+ <div className='actions flex justify-end gap-x-2.5 mt-4'>
284
+ {/* <Button
285
+ priority='low'
286
+ classes={{
287
+ content: 'capitalize text-[16px] font-medium'
288
+ }}
289
+ onClick={() => setReviewOrder(false)}
290
+ type="button"
291
+ >
292
+ Cancel
293
+ </Button> */}
294
+ <Button
295
+ priority='high'
296
+ classes={{
297
+ content: 'capitalize text-[16px] font-medium'
298
+ }}
299
+ type="submit"
300
+ disabled={submitting}
301
+ >
302
+ {submitting ? 'Submitting...' : 'Submit Review'}
303
+ </Button>
304
+ </div>
305
+ </form>
306
+ </>
307
+ );
308
+ })}
309
+
310
+ </div>
311
+ </Modal>
312
+ </>
313
+ );
314
+ };
315
+
316
+ export default ReviewModal;