@riosst100/pwa-marketplace 3.2.9 → 3.3.1

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/package.json +1 -1
  2. package/src/components/AgeVerification/ageVerification.js +121 -0
  3. package/src/components/AgeVerification/index.js +1 -2
  4. package/src/components/AgeVerificationModal/ageVerificationModal.js +83 -0
  5. package/src/components/{AgeVerification → AgeVerificationModal}/ageVerificationModal.module.css +77 -10
  6. package/src/components/AgeVerificationModal/index.js +2 -0
  7. package/src/components/LiveChat/MessagesModal.js +3 -3
  8. package/src/components/Messages/messages.js +90 -117
  9. package/src/components/Messages/messages.module.css +15 -0
  10. package/src/components/RFQ/modalRfq.js +90 -18
  11. package/src/components/RFQPage/quoteDetail.js +47 -30
  12. package/src/components/ShowMore/showMore.js +8 -5
  13. package/src/intercept.js +9 -0
  14. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategory.js +0 -4
  15. package/src/overwrites/venia-ui/lib/RootComponents/Category/category.js +2 -0
  16. package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +1 -1
  17. package/src/overwrites/venia-ui/lib/RootComponents/Product/product.js +2 -0
  18. package/src/overwrites/venia-ui/lib/components/Adapter/adapter.js +0 -23
  19. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +9 -4
  20. package/src/talons/AgeVerification/ageVerification.gql.js +31 -0
  21. package/src/talons/AgeVerification/useAgeVerification.js +126 -0
  22. package/src/talons/MessagesPage/messagesPage.gql.js +4 -0
  23. package/src/talons/RFQ/rfq.gql.js +8 -0
  24. package/src/talons/RFQ/useRFQ.js +31 -6
  25. package/src/talons/Seller/useSeller.js +4 -1
  26. package/src/components/AgeVerification/ageVerificationModal.js +0 -163
  27. package/src/components/AgeVerification/sellerCoupon.js +0 -119
  28. package/src/components/AgeVerification/sellerCouponCheckout.js +0 -164
  29. /package/src/components/{AgeVerification → AgeVerificationModal}/ageVerificationModal.shimmer.js +0 -0
@@ -0,0 +1,126 @@
1
+ import { useQuery, useLazyQuery } from '@apollo/client';
2
+ import { useMemo, useEffect } from 'react';
3
+
4
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
5
+ import DEFAULT_OPERATIONS from './ageVerification.gql';
6
+
7
+ export const useAgeVerification = props => {
8
+ const { pageType, product } = props;
9
+
10
+ const operations = mergeOperations(DEFAULT_OPERATIONS, null);
11
+ const { getAgeVerificationConfigQuery, getMenuTopFiltersAttributesConfigDataQuery } = operations;
12
+
13
+ const { location } = globalThis;
14
+
15
+ const { data: storeConfigData } = useQuery(getMenuTopFiltersAttributesConfigDataQuery, {
16
+ fetchPolicy: 'cache-and-network',
17
+ nextFetchPolicy: 'cache-first'
18
+ });
19
+
20
+ const activeMenuTopFilterAttribute = useMemo(() => {
21
+ if (storeConfigData) {
22
+
23
+ const menuTopFiltersAttributes = storeConfigData.storeConfig.marketplace_menu_topfilters_attributes;
24
+ if (!menuTopFiltersAttributes) {
25
+ return null;
26
+ }
27
+
28
+ const menuTopFiltersAttributeCodes = Object.values(JSON.parse(menuTopFiltersAttributes)).map(
29
+ item => item.attribute_code
30
+ );
31
+
32
+ if (!menuTopFiltersAttributeCodes) {
33
+ return null;
34
+ }
35
+
36
+ let result = null;
37
+
38
+ if (pageType == "CATEGORY") {
39
+ const params = new URLSearchParams(location.search);
40
+ if (!params.size) {
41
+ return null;
42
+ }
43
+
44
+ params.forEach((value, param) => {
45
+ const attrCode = param.replace("[filter]", "");
46
+ if (attrCode && menuTopFiltersAttributeCodes.includes(attrCode)) {
47
+ const attrOptionLabel = value.split(",")[0];
48
+ result = {
49
+ attributeCode: attrCode,
50
+ attributeOption: attrOptionLabel
51
+ };
52
+
53
+ return true;
54
+ }
55
+ });
56
+ }
57
+
58
+ if (pageType == "PRODUCT" && product) {
59
+ product.custom_attributes.map(attr => {
60
+ const attrCode = attr.attribute_metadata?.code;
61
+ if (attrCode && menuTopFiltersAttributeCodes.includes(attrCode)) {
62
+ let label = null;
63
+
64
+ // SELECT / MULTISELECT
65
+ if (attr?.selected_attribute_options?.attribute_option?.length) {
66
+ label = attr.selected_attribute_options.attribute_option.length ?
67
+ attr.selected_attribute_options.attribute_option.map(o => o.label)[0] : '';
68
+ // label = labelArr?.attributeOption?.[0] ?? "";
69
+ }
70
+
71
+ // TEXT / NUMBER
72
+ if (attr?.entered_attribute_value?.value) {
73
+ label = attr.entered_attribute_value.value;
74
+ }
75
+
76
+ result = {
77
+ attributeCode: attrCode,
78
+ attributeOption: label
79
+ };
80
+ }
81
+ });
82
+ }
83
+
84
+ return result;
85
+ }
86
+ }, [storeConfigData, pageType, product]);
87
+
88
+ const [getAgeVerificationConfig, { data, error, loading }] = useLazyQuery(
89
+ getAgeVerificationConfigQuery,
90
+ {
91
+ fetchPolicy: 'cache-and-network',
92
+ nextFetchPolicy: 'cache-first'
93
+ }
94
+ );
95
+
96
+
97
+ useEffect(() => {
98
+ if (activeMenuTopFilterAttribute) {
99
+ getAgeVerificationConfig({
100
+ variables: {
101
+ attributeCode: activeMenuTopFilterAttribute.attributeCode,
102
+ attributeOption: activeMenuTopFilterAttribute.attributeOption
103
+ }
104
+ });
105
+ }
106
+ }, [activeMenuTopFilterAttribute, getAgeVerificationConfig]);
107
+
108
+ const ageVerificationConfig = useMemo(() => {
109
+ if (!data) {
110
+ return null;
111
+ }
112
+
113
+ const getAgeVerificationConfig = data.getAgeVerificationConfig;
114
+ if (!getAgeVerificationConfig) {
115
+ return null;
116
+ }
117
+
118
+ return getAgeVerificationConfig;
119
+ }, [data]);
120
+
121
+ return {
122
+ error,
123
+ loading,
124
+ ageVerificationConfig
125
+ };
126
+ };
@@ -223,6 +223,10 @@ export const GET_CUSTOMER_SELLER_MESSAGES = gql`
223
223
  total_pages
224
224
  }
225
225
  items {
226
+ seller {
227
+ name
228
+ url_key
229
+ }
226
230
  message_id
227
231
  subject
228
232
  description
@@ -94,6 +94,7 @@ export const GET_QUICK_RFQ_DETAIL_QUERY = gql`
94
94
  date_need_quote
95
95
  email
96
96
  expiry
97
+ image_url
97
98
  info_buy_request
98
99
  messages {
99
100
  created_at
@@ -166,7 +167,14 @@ export const CONVERT_QUICK_RFQ_TO_CART_MUTATION = gql`
166
167
  }
167
168
  `;
168
169
 
170
+ export const CREATE_CART = gql`
171
+ mutation createCart {
172
+ cartId: createEmptyCart
173
+ }
174
+ `;
175
+
169
176
  export default {
177
+ createCartMutation: CREATE_CART,
170
178
  convertQuickrfqToCartMutation: CONVERT_QUICK_RFQ_TO_CART_MUTATION,
171
179
  sendRfqMessageMutation: SEND_RFQ_MESSAGE_MUTATION,
172
180
  getQuickRfqDetailQuery: GET_QUICK_RFQ_DETAIL_QUERY,
@@ -4,14 +4,23 @@ import { useCallback } from 'react';
4
4
  import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
5
5
  import DEFAULT_OPERATIONS from './rfq.gql';
6
6
 
7
+ import { useLocation } from 'react-router-dom';
8
+
7
9
  export const useRFQ = props => {
10
+ const { pathname } = useLocation();
11
+ const rfqQuoteId = pathname.split('/')[2];
12
+
13
+ console.log('rfqQuoteId',rfqQuoteId)
14
+
8
15
  const operations = mergeOperations(DEFAULT_OPERATIONS, props?.operations);
16
+
9
17
  const {
10
18
  getQuickRfqListQuery,
11
19
  getQuickRfqDetailQuery,
12
20
  createQuickRfqMutation,
13
21
  sendRfqMessageMutation,
14
- convertQuickrfqToCartMutation
22
+ convertQuickrfqToCartMutation,
23
+ createCartMutation
15
24
  } = operations;
16
25
 
17
26
  const [fetchRfqList, rfqListState] = useLazyQuery(getQuickRfqListQuery, {
@@ -28,7 +37,20 @@ export const useRFQ = props => {
28
37
 
29
38
  const [createQuickRfq, createState] = useMutation(createQuickRfqMutation);
30
39
  const [sendRfqMessage, sendMessageState] = useMutation(sendRfqMessageMutation);
31
- const [convertQuickrfqToCart, convertState] = useMutation(convertQuickrfqToCartMutation);
40
+
41
+ const [fetchCartId] = useMutation(createCartMutation);
42
+ // const [convertQuickrfqToCart, convertState] = useMutation(convertQuickrfqToCartMutation);
43
+
44
+ const convertState = null;
45
+
46
+ const [
47
+ convertQuickrfqToCart,
48
+ {
49
+ data: addToCartResponseData,
50
+ error: errorAddingProductToCart,
51
+ loading: isAddProductLoading
52
+ }
53
+ ] = useMutation(convertQuickrfqToCartMutation);
32
54
 
33
55
  const loadRfqList = useCallback(
34
56
  variables => fetchRfqList({ variables }),
@@ -57,12 +79,15 @@ export const useRFQ = props => {
57
79
  [sendRfqMessage]
58
80
  );
59
81
 
60
- const handleConvertQuickrfqToCart = useCallback(
61
- quickrfqId => convertQuickrfqToCart({ variables: { quickrfqId } }),
62
- [convertQuickrfqToCart]
63
- );
82
+ const handleConvertQuickrfqToCart = useCallback(() => {
83
+ convertQuickrfqToCart({ variables: { quickrfqId: rfqQuoteId } })
84
+ }, [convertQuickrfqToCart, rfqQuoteId]);
64
85
 
65
86
  return {
87
+ fetchCartId,
88
+ addToCartResponseData,
89
+ isAddProductLoading,
90
+ errorAddingProductToCart,
66
91
  loadRfqList,
67
92
  rfqListState,
68
93
  loadRfqDetail,
@@ -154,7 +154,10 @@ export const useSeller = props => {
154
154
  }),
155
155
  timeout: 3000
156
156
  });
157
- history.push('/messages');
157
+ if (result) {
158
+ var messageId = result?.data?.customerSendMessage.message_id;
159
+ history.push('/messages?id=' + messageId);
160
+ }
158
161
  return result?.data?.customerSendMessage;
159
162
  }, [sendMessage, refetchMessages, addToast, formatMessage]);
160
163
 
@@ -1,163 +0,0 @@
1
- import React, { useEffect, useState } from 'react';
2
- import ReactDOM from 'react-dom';
3
- import modalClasses from '@riosst100/pwa-marketplace/src/components/AgeVerification/ageVerificationModal.module.css';
4
- // import { AgeVerificationModalShimmer } from '@riosst100/pwa-marketplace/src/components/SellerCoupon';
5
-
6
- // Reuse day diff logic
7
- const dayDiff = (toDate) => {
8
- if (!toDate) return null;
9
- const end = new Date(toDate);
10
- if (Number.isNaN(end.getTime())) return null;
11
- const now = new Date();
12
- const diffMs = end.setHours(23, 59, 59, 999) - now.getTime();
13
- const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
14
- return diffDays;
15
- };
16
-
17
- // Simple portal root fallback
18
- const getPortalRoot = () => {
19
- let root = document.getElementById('seller-coupon-portal');
20
- if (!root) {
21
- root = document.createElement('div');
22
- root.id = 'seller-coupon-portal';
23
- document.body.appendChild(root);
24
- }
25
- return root;
26
- };
27
-
28
- const AgeVerificationModal = ({
29
- couponData,
30
- couponError,
31
- setCouponModalOpen,
32
- couponModalOpen,
33
- couponLoading,
34
- isCopyAction,
35
- triggerLabel = 'View Seller Coupons',
36
- onSelectCoupon,
37
- onTriggerRender, // optional custom trigger renderer
38
- autoOpen = false, // auto open modal when mounted
39
- closeOnClaim = true // close after claiming
40
- }) => {
41
- // const [open, setOpen] = useState(false);
42
- const [copied, setCopied] = useState(null);
43
-
44
- const items = couponData?.sellerCoupons?.items || [];
45
-
46
- const handleOpen = () => setCouponModalOpen(true);
47
- const handleClose = () => setCouponModalOpen(false);
48
-
49
- useEffect(() => {
50
- if (!couponModalOpen) return;
51
- const onKey = (e) => {
52
- if (e.key === 'Escape') {
53
- handleClose();
54
- }
55
- };
56
- document.addEventListener('keydown', onKey);
57
- return () => document.removeEventListener('keydown', onKey);
58
- }, [couponModalOpen]);
59
-
60
- const handleClaim = async (item) => {
61
- try {
62
- if (navigator?.clipboard?.writeText) {
63
- await navigator.clipboard.writeText(item.code);
64
- }
65
- } catch (_) {
66
- /* ignore */
67
- }
68
- console.log('item.code',item.code)
69
- setCopied(item.code);
70
- setTimeout(() => setCopied(null), 2000);
71
- if (!isCopyAction) {
72
- if (onSelectCoupon) onSelectCoupon(item);
73
- if (closeOnClaim) handleClose();
74
- }
75
- };
76
-
77
- // Auto open behavior
78
- useEffect(() => {
79
- if (autoOpen && !couponModalOpen) {
80
- setCouponModalOpen(true);
81
- }
82
- }, [autoOpen, couponModalOpen]);
83
-
84
- console.log('copied',copied)
85
-
86
- const modal = couponModalOpen ? (
87
- <div className={modalClasses.overlay} role="dialog" aria-modal="true" aria-label="Seller coupons list">
88
- <div className={modalClasses.backdrop} onClick={handleClose} />
89
- <div className={modalClasses.dialog}>
90
- <div className={modalClasses.header}>
91
- <h2 className={modalClasses.title}>{isCopyAction ? 'Store Coupons' : 'Apply Coupon'}</h2>
92
- <button type="button" className={modalClasses.closeBtn} onClick={handleClose} aria-label="Close coupon modal">×</button>
93
- </div>
94
- <div className={modalClasses.body}>
95
- {/* {couponLoading && <AgeVerificationModalShimmer />} */}
96
- {!couponLoading && couponError && <p className={modalClasses.metaText}>Failed to load coupons.</p>}
97
- {!couponLoading && !couponError && !items.length && <p className={modalClasses.metaText}>No coupons available.</p>}
98
- {!couponLoading && !couponError && items.length > 0 && (
99
- <div className={modalClasses.stack}>
100
- {items.map(item => {
101
- const key = item.couponcode_id || item.coupon_id || item.code;
102
- const daysLeft = dayDiff(item.to_date);
103
- const expiryLabel = daysLeft === null
104
- ? 'No expiry'
105
- : daysLeft <= 0
106
- ? 'Ends today'
107
- : `Ends in ${daysLeft} day${daysLeft > 1 ? 's' : ''}`;
108
- const title = item.description || item.name || `Discount ${item.discount_amount || ''}`;
109
- return (
110
- <div key={key} className={modalClasses.card} role="group" aria-label={`Coupon ${item.code}`}>
111
- <div className={modalClasses.perf} aria-hidden="true" />
112
- <div className={modalClasses.content}>
113
- <div className={modalClasses.title}>{title}</div>
114
- <div className={modalClasses.subtext}>Code: <span className={modalClasses.code}>{item.code}</span></div>
115
- <div className={modalClasses.expiry}>{expiryLabel}</div>
116
- </div>
117
- <div className={modalClasses.divider} aria-hidden="true" />
118
- <div className={modalClasses.actions}>
119
- {isCopyAction ? <button
120
- type="button"
121
- className={copied === item.code ? modalClasses.claimedBtn : modalClasses.claimBtn}
122
- onClick={() => handleClaim(item)}
123
- aria-label={`Claim coupon ${item.code}`}
124
- >
125
- {copied === item.code ? 'Copied' : 'Claim'}
126
- </button> :
127
- <button
128
- type="button"
129
- className={copied === item.code ? modalClasses.claimedBtn : modalClasses.claimBtn}
130
- onClick={() => handleClaim(item)}
131
- aria-label={`Apply coupon ${item.code}`}
132
- >
133
- {copied === item.code ? 'Applied' : 'Apply'}
134
- </button>}
135
- </div>
136
- </div>
137
- );
138
- })}
139
- </div>
140
- )}
141
- </div>
142
- </div>
143
- </div>
144
- ) : null;
145
-
146
- const trigger = onTriggerRender
147
- ? onTriggerRender({ couponModalOpen, setCouponModalOpen, handleOpen })
148
- : (
149
- <button
150
- type="button"
151
- className={modalClasses.triggerBtn}
152
- onClick={handleOpen}
153
- aria-haspopup="dialog"
154
- aria-expanded={couponModalOpen}
155
- >
156
- {triggerLabel}
157
- </button>
158
- );
159
-
160
- return <>{onTriggerRender ? trigger : trigger}{couponModalOpen ? ReactDOM.createPortal(modal, getPortalRoot()) : null}</>;
161
- };
162
-
163
- export default AgeVerificationModal;
@@ -1,119 +0,0 @@
1
- import React, { useMemo, useState, useCallback } from 'react';
2
- import classes from '@riosst100/pwa-marketplace/src/components/AgeVerification/sellerCoupon.module.css';
3
- import SellerCouponCheckout from '@riosst100/pwa-marketplace/src/components/SellerCoupon/sellerCouponCheckout';
4
-
5
- const dayDiff = (toDate) => {
6
- if (!toDate) return null;
7
- const end = new Date(toDate);
8
- if (Number.isNaN(end.getTime())) return null;
9
- const now = new Date();
10
- const diffMs = end.setHours(23, 59, 59, 999) - now.getTime();
11
- const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
12
- return diffDays;
13
- };
14
-
15
- const SellerCoupon = ({ couponData, couponError, couponLoading }) => {
16
- if (couponLoading) {
17
- return '';
18
- // return <div className={classes.container}><p className={classes.metaText}>Loading coupons...</p></div>;
19
- }
20
-
21
- if (couponError) {
22
- return '';
23
- // return <div className={classes.container}><p className={classes.metaText}>Failed to load coupons.</p></div>;
24
- }
25
-
26
- const items = couponData?.sellerCoupons?.items || [];
27
-
28
- if (!items.length) {
29
- return '';
30
- // return <div className={classes.container}><p className={classes.metaText}>No coupons available.</p></div>;
31
- }
32
-
33
- const [copied, setCopied] = useState(null);
34
- const [couponModalOpen, setCouponModalOpen] = useState(false);
35
-
36
- const handleClaim = async (code) => {
37
- try {
38
- if (navigator?.clipboard?.writeText) {
39
- await navigator.clipboard.writeText(code);
40
- }
41
- } catch (_) {
42
- // ignore clipboard issues
43
- }
44
- setCopied(code);
45
- setTimeout(() => setCopied(null), 2000);
46
- };
47
-
48
- const handleViewCoupons = useCallback(() => {
49
- setCouponModalOpen(true);
50
- }, [setCouponModalOpen]);
51
-
52
- return (
53
- <section className={classes.container} aria-label="Seller coupons">
54
- <div className={classes.scroller}>
55
- {items.slice(0, 3).map(item => {
56
- const key = item.couponcode_id || item.coupon_id || item.code;
57
- const daysLeft = dayDiff(item.to_date);
58
- const expiryLabel = daysLeft === null
59
- ? 'No expiry'
60
- : daysLeft <= 0
61
- ? 'Ends today'
62
- : `Ends in ${daysLeft} day${daysLeft > 1 ? 's' : ''}`;
63
-
64
- const title = item.description || item.name || `Discount ${item.discount_amount || ''}`;
65
-
66
- return (
67
- <div className={classes.card} key={key} role="group" aria-label={`Coupon ${item.code}`}>
68
- <div className={classes.perf} aria-hidden="true" />
69
- <div className={classes.content}>
70
- <div className={classes.title}>{title}</div>
71
- <div className={classes.subtext}>Code: <span className={classes.code}>{item.code}</span></div>
72
- <div className={classes.expiry}>{expiryLabel}</div>
73
- </div>
74
- <div className={classes.divider} aria-hidden="true" />
75
- <div className={classes.actions}>
76
- <button
77
- type="button"
78
- className={copied === item.code ? classes.claimedBtn : classes.claimBtn}
79
- onClick={() => handleClaim(item.code)}
80
- aria-label={`Claim coupon ${item.code}`}
81
- >
82
- {copied === item.code ? 'Copied' : 'Claim'}
83
- </button>
84
- </div>
85
- </div>
86
- );
87
- })}
88
- {couponModalOpen && (
89
- <SellerCouponCheckout
90
- couponData={couponData}
91
- couponLoading={couponLoading}
92
- couponError={couponError}
93
- autoOpen={true}
94
- closeOnClaim={true}
95
- couponModalOpen={couponModalOpen}
96
- setCouponModalOpen={setCouponModalOpen}
97
- onSelectCoupon={handleClaim}
98
- onTriggerRender={() => null}
99
- isCopyAction={true}
100
- />
101
- )}
102
- <div className={classes.viewAllCard} role="group" aria-label="View All Coupons">
103
- <div className={classes.actions}>
104
- <button
105
- type="button"
106
- className={classes.viewAllBtn}
107
- onClick={handleViewCoupons}
108
- aria-label="View All Coupons"
109
- >
110
- View All Coupons
111
- </button>
112
- </div>
113
- </div>
114
- </div>
115
- </section>
116
- );
117
- };
118
-
119
- export default SellerCoupon;