@riosst100/pwa-marketplace 3.0.1 → 3.0.4

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 (29) 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 +2 -0
  5. package/src/components/SellerCoupon/index.js +1 -0
  6. package/src/components/SellerCoupon/sellerCoupon.js +41 -6
  7. package/src/components/SellerCoupon/sellerCoupon.module.css +22 -0
  8. package/src/components/SellerCoupon/sellerCouponCheckout.js +36 -19
  9. package/src/components/SellerCoupon/sellerCouponCheckout.shimmer.js +21 -0
  10. package/src/overwrites/peregrine/lib/talons/CartPage/PriceSummary/usePriceSummary.js +2 -2
  11. package/src/overwrites/peregrine/lib/talons/CartPage/useCartPage.js +121 -0
  12. package/src/overwrites/venia-ui/lib/components/CartPage/PriceAdjustments/CouponCode/couponCode.js +24 -27
  13. package/src/overwrites/venia-ui/lib/components/CartPage/PriceAdjustments/CouponCode/couponCode.module.css +2 -0
  14. package/src/overwrites/venia-ui/lib/components/CartPage/PriceSummary/priceSummary.js +14 -5
  15. package/src/overwrites/venia-ui/lib/components/CartPage/cartPage.js +0 -7
  16. package/src/overwrites/venia-ui/lib/components/CheckoutPage/OrderSummary/orderSummary.js +4 -0
  17. package/src/overwrites/venia-ui/lib/components/CheckoutPage/PriceAdjustments/priceAdjustments.js +65 -57
  18. package/src/overwrites/venia-ui/lib/components/CheckoutPage/PriceAdjustments/priceAdjustments.module.css +29 -0
  19. package/src/overwrites/venia-ui/lib/components/CheckoutPage/checkoutPage.js +2 -2
  20. package/src/overwrites/venia-ui/lib/components/CheckoutPage/checkoutPage.module.css +2 -0
  21. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/Reviews/index.js +1 -0
  22. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/Reviews/reviewModal.js +316 -0
  23. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/Reviews/starInput.js +33 -0
  24. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/orderHistoryPage.js +29 -2
  25. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/orderRow.js +70 -34
  26. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/modalFormReview.js +8 -8
  27. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/productReview.js +4 -27
  28. package/src/talons/ReviewModal/reviewModal.gql.js +45 -0
  29. package/src/talons/ReviewModal/useReviewModal.js +87 -0
@@ -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>
@@ -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;
@@ -2,9 +2,6 @@ import React, { useState } from 'react'
2
2
  import Review from '@riosst100/pwa-marketplace/src/components/ProductReviewItem';
3
3
  import { Star1 } from 'iconsax-react';
4
4
  import Button from '../../Button';
5
- import ModalFormReview from './modalFormReview';
6
-
7
-
8
5
 
9
6
  const productReview = (props) => {
10
7
  const {
@@ -56,29 +53,9 @@ const productReview = (props) => {
56
53
  if (!reviewsData || !reviewsData.items.length) {
57
54
  return (
58
55
  <>
59
- <ModalFormReview
60
- open={open}
61
- setOpen={setOpen}
62
- defaultNickname={defaultNickname}
63
- ratingsMetadata={ratingsMetadataData?.productReviewRatingsMetadata?.items || []}
64
- loadingRatingsMetadata={loadingRatingsMetadata}
65
- onSubmit={async (formValues) => {
66
- setSubmitting(true);
67
- const input = {
68
- nickname: formValues.nickname,
69
- summary: formValues.summary,
70
- text: formValues.review,
71
- sku: product?.sku,
72
- ratings: formValues.ratings
73
- };
74
- const result = await handleSubmitReview(input);
75
- setSubmitting(false);
76
- if (result.success) setOpen(false);
77
- }}
78
- submitting={submitting}
79
- />
56
+
80
57
  <div className={className}>
81
- <div className="flex items-center justify-between mb-6">
58
+ {/* <div className="flex items-center justify-between mb-6">
82
59
  <div />
83
60
  <Button
84
61
  priority='low'
@@ -89,8 +66,8 @@ const productReview = (props) => {
89
66
  >
90
67
  Write a review
91
68
  </Button>
92
- </div>
93
- <div className="text-center py-8 text-gray-500">No reviews yet.</div>
69
+ </div> */}
70
+ <div className="text-center py-8 text-gray-600 text-[14px]">No reviews yet.</div>
94
71
  </div>
95
72
  </>
96
73
  );
@@ -0,0 +1,45 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ const GET_PRODUCT_REVIEW_RATINGS_METADATA = gql`
4
+ query getProductReviewRatingsMetadata {
5
+ productReviewRatingsMetadata {
6
+ items {
7
+ id
8
+ name
9
+ values {
10
+ value
11
+ value_id
12
+ }
13
+ }
14
+ }
15
+ }
16
+ `;
17
+
18
+ const CREATE_PRODUCT_REVIEW = gql`
19
+ mutation CreateProductReview($input: CreateProductReviewInput!) {
20
+ createProductReview(input: $input) {
21
+ review {
22
+ average_rating
23
+ created_at
24
+ nickname
25
+ product {
26
+ uuid: uid
27
+ uid
28
+ name
29
+ sku
30
+ }
31
+ ratings_breakdown {
32
+ name
33
+ value
34
+ }
35
+ summary
36
+ text
37
+ }
38
+ }
39
+ }
40
+ `;
41
+
42
+ export default {
43
+ createProductReview: CREATE_PRODUCT_REVIEW,
44
+ getProductReviewRatingsMetadata: GET_PRODUCT_REVIEW_RATINGS_METADATA
45
+ };
@@ -0,0 +1,87 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { useToasts } from '@magento/peregrine/lib';
3
+ import { useMutation, useQuery } from '@apollo/client';
4
+ import { useUserContext } from '@magento/peregrine/lib/context/user';
5
+ import { useHistory } from 'react-router-dom';
6
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
7
+ import defaultOperations from '@magento/peregrine/lib/talons/ProductFullDetail/productFullDetail.gql';
8
+ import productReviewOperations from './reviewModal.gql';
9
+
10
+ export const useReviewModal = props => {
11
+ const { reviewOrder, setSubmitting } = props;
12
+
13
+ const history = useHistory();
14
+
15
+ const handleToastAction = useCallback(
16
+ (removeToast) => {
17
+ history.push('/cart');
18
+ removeToast();
19
+ },
20
+ [history]
21
+ );
22
+
23
+ const [, { addToast }] = useToasts();
24
+
25
+ const operations = mergeOperations(defaultOperations, props.operations);
26
+
27
+ // Query for ratings metadata
28
+ const { data: ratingsMetadata, loading: loadingRatingsMetadata } = useQuery(
29
+ productReviewOperations.getProductReviewRatingsMetadata,
30
+ { fetchPolicy: 'network-only' }
31
+ );
32
+
33
+ // Mutation for creating review (allow partial data with errors)
34
+ const [createProductReview, { loading: loadingCreateReview, error: errorCreateReview }] = useMutation(
35
+ productReviewOperations.createProductReview,
36
+ { errorPolicy: 'all' }
37
+ );
38
+
39
+ // Handler for submitting review with robust toast handling
40
+ const handleSubmitReview = async (formValues) => {
41
+ try {
42
+ setSubmitting(true)
43
+
44
+ const result = await createProductReview({
45
+ variables: { input: formValues },
46
+ errorPolicy: 'all'
47
+ });
48
+
49
+ const gqlErrors = result?.errors || [];
50
+ const review = result?.data?.createProductReview?.review;
51
+
52
+ if (gqlErrors.length > 0 || !review) {
53
+ const message = gqlErrors[0]?.message || 'Failed to submit review!';
54
+ addToast({ type: 'error', message });
55
+ return { success: false, error: gqlErrors };
56
+ }
57
+
58
+ addToast({ type: 'success', message: 'Review submitted successfully!' });
59
+ return { success: true };
60
+ } catch (e) {
61
+ addToast({ type: 'error', message: e?.message || 'Failed to submit review!' });
62
+ return { success: false, error: e };
63
+ }
64
+ };
65
+
66
+ const [{ currentUser }] = useUserContext();
67
+
68
+ const defaultNickname = useMemo(() => {
69
+ if (!currentUser) return '';
70
+ const first = currentUser.firstname || '';
71
+ const last = currentUser.lastname || '';
72
+ const full = `${first} ${last}`.trim();
73
+ if (full) return full;
74
+ const email = currentUser.email || '';
75
+ return email ? email.split('@')[0] : '';
76
+ }, [currentUser]);
77
+
78
+ return {
79
+ ratingsMetadata: ratingsMetadata?.productReviewRatingsMetadata?.items[0] || null,
80
+ loadingRatingsMetadata,
81
+ handleSubmitReview,
82
+ loadingCreateReview,
83
+ errorCreateReview,
84
+ defaultNickname,
85
+ handleToastAction
86
+ };
87
+ };