@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.
- package/package.json +1 -1
- package/src/components/AgeVerification/ageVerification.js +121 -0
- package/src/components/AgeVerification/index.js +1 -2
- package/src/components/AgeVerificationModal/ageVerificationModal.js +83 -0
- package/src/components/{AgeVerification → AgeVerificationModal}/ageVerificationModal.module.css +77 -10
- package/src/components/AgeVerificationModal/index.js +2 -0
- package/src/components/LiveChat/MessagesModal.js +3 -3
- package/src/components/Messages/messages.js +90 -117
- package/src/components/Messages/messages.module.css +15 -0
- package/src/components/RFQ/modalRfq.js +90 -18
- package/src/components/RFQPage/quoteDetail.js +47 -30
- package/src/components/ShowMore/showMore.js +8 -5
- package/src/intercept.js +9 -0
- package/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategory.js +0 -4
- package/src/overwrites/venia-ui/lib/RootComponents/Category/category.js +2 -0
- package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +1 -1
- package/src/overwrites/venia-ui/lib/RootComponents/Product/product.js +2 -0
- package/src/overwrites/venia-ui/lib/components/Adapter/adapter.js +0 -23
- package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +9 -4
- package/src/talons/AgeVerification/ageVerification.gql.js +31 -0
- package/src/talons/AgeVerification/useAgeVerification.js +126 -0
- package/src/talons/MessagesPage/messagesPage.gql.js +4 -0
- package/src/talons/RFQ/rfq.gql.js +8 -0
- package/src/talons/RFQ/useRFQ.js +31 -6
- package/src/talons/Seller/useSeller.js +4 -1
- package/src/components/AgeVerification/ageVerificationModal.js +0 -163
- package/src/components/AgeVerification/sellerCoupon.js +0 -119
- package/src/components/AgeVerification/sellerCouponCheckout.js +0 -164
- /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
|
+
};
|
|
@@ -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,
|
package/src/talons/RFQ/useRFQ.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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;
|