@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
package/package.json
CHANGED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import AgeVerificationModal from '@riosst100/pwa-marketplace/src/components/AgeVerificationModal';
|
|
3
|
+
import { useHistory, useLocation } from 'react-router-dom';
|
|
4
|
+
import { useToasts } from '@magento/peregrine/lib/Toasts';
|
|
5
|
+
import { BrowserPersistence } from '@magento/peregrine/lib/util';
|
|
6
|
+
import { useAgeVerification } from '@riosst100/pwa-marketplace/src/talons/AgeVerification/useAgeVerification'
|
|
7
|
+
|
|
8
|
+
const storage = new BrowserPersistence();
|
|
9
|
+
|
|
10
|
+
const AgeVerification = (props) => {
|
|
11
|
+
const { pageType, product } = props;
|
|
12
|
+
|
|
13
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
14
|
+
|
|
15
|
+
const [month, setMonth] = useState("");
|
|
16
|
+
const [day, setDay] = useState("");
|
|
17
|
+
const [year, setYear] = useState("");
|
|
18
|
+
const [ageVerificationErrorMessage, setAgeVerificationErrorMessage] = useState("");
|
|
19
|
+
|
|
20
|
+
const history = useHistory();
|
|
21
|
+
const location = useLocation();
|
|
22
|
+
|
|
23
|
+
const [, { addToast }] = useToasts();
|
|
24
|
+
|
|
25
|
+
const handleClose = () => setModalOpen(false);
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
error,
|
|
29
|
+
loading,
|
|
30
|
+
ageVerificationConfig
|
|
31
|
+
} = useAgeVerification({ pageType, product });
|
|
32
|
+
|
|
33
|
+
const months = [
|
|
34
|
+
"January","February","March","April","May","June",
|
|
35
|
+
"July","August","September","October","November","December"
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const savedAge = storage.getItem('age');
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (ageVerificationConfig && ageVerificationConfig.status) {
|
|
42
|
+
if (savedAge && savedAge > 18) {
|
|
43
|
+
setModalOpen(false);
|
|
44
|
+
} else {
|
|
45
|
+
setModalOpen(true);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}, [loading, setModalOpen, savedAge, ageVerificationConfig]);
|
|
49
|
+
|
|
50
|
+
const redirectUser = () => {
|
|
51
|
+
const currentPathname = location.pathname;
|
|
52
|
+
let historyPathname = history.location.pathname;
|
|
53
|
+
if (historyPathname == currentPathname) {
|
|
54
|
+
historyPathname = '/';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
addToast({
|
|
58
|
+
type: 'error',
|
|
59
|
+
message: 'Access is restricted because you are under 18 years old.',
|
|
60
|
+
dismissable: true,
|
|
61
|
+
timeout: 5000
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
history.push(historyPathname)
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const handleUnderAge = useCallback(() => {
|
|
68
|
+
redirectUser();
|
|
69
|
+
}, [])
|
|
70
|
+
|
|
71
|
+
const handleOverAge = useCallback(() => {
|
|
72
|
+
if (!day || !month || !year) {
|
|
73
|
+
setAgeVerificationErrorMessage('Please enter your birthday!');
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const birthDate = new Date(
|
|
78
|
+
year,
|
|
79
|
+
months.indexOf(month),
|
|
80
|
+
day
|
|
81
|
+
);
|
|
82
|
+
const today = new Date();
|
|
83
|
+
const age = today.getFullYear() - birthDate.getFullYear();
|
|
84
|
+
const isBirthdayPassed =
|
|
85
|
+
today >= new Date(today.getFullYear(), birthDate.getMonth(), birthDate.getDate());
|
|
86
|
+
|
|
87
|
+
if (age > 18 || (age === 18 && isBirthdayPassed)) {
|
|
88
|
+
storage.setItem('age', age);
|
|
89
|
+
|
|
90
|
+
addToast({
|
|
91
|
+
type: 'success',
|
|
92
|
+
message: 'Thank you for confirming your age. Your birthday has been saved. You may now continue.',
|
|
93
|
+
dismissable: true,
|
|
94
|
+
timeout: 5000
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
handleClose();
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
redirectUser();
|
|
102
|
+
return;
|
|
103
|
+
}, [day, month, year])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
return modalOpen ? (
|
|
108
|
+
<AgeVerificationModal
|
|
109
|
+
ageVerificationErrorMessage={ageVerificationErrorMessage}
|
|
110
|
+
modalOpen={modalOpen}
|
|
111
|
+
months={months}
|
|
112
|
+
setMonth={setMonth}
|
|
113
|
+
setDay={setDay}
|
|
114
|
+
setYear={setYear}
|
|
115
|
+
handleOverAge={handleOverAge}
|
|
116
|
+
handleUnderAge={handleUnderAge}
|
|
117
|
+
/>
|
|
118
|
+
) : ''
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default AgeVerification;
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export { default } from './
|
|
2
|
-
// export { default as AgeVerificationModalShimmer } from './ageVerificationModal.shimmer';
|
|
1
|
+
export { default } from './ageVerification';
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import modalClasses from './ageVerificationModal.module.css';
|
|
4
|
+
import Icon from '@magento/venia-ui/lib/components/Icon';
|
|
5
|
+
import { AlertTriangle } from 'react-feather';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// Simple portal root fallback
|
|
9
|
+
const getPortalRoot = () => {
|
|
10
|
+
let root = document.getElementById('age-verification-portal');
|
|
11
|
+
if (!root) {
|
|
12
|
+
root = document.createElement('div');
|
|
13
|
+
root.id = 'age-verification-portal';
|
|
14
|
+
document.body.appendChild(root);
|
|
15
|
+
}
|
|
16
|
+
return root;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const AgeVerificationModal = props => {
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
setMonth,
|
|
23
|
+
setDay,
|
|
24
|
+
setYear,
|
|
25
|
+
handleOverAge,
|
|
26
|
+
handleUnderAge,
|
|
27
|
+
ageVerificationErrorMessage,
|
|
28
|
+
modalOpen,
|
|
29
|
+
months
|
|
30
|
+
} = props;
|
|
31
|
+
|
|
32
|
+
const currentYear = new Date().getFullYear();
|
|
33
|
+
const years = Array.from({ length: 100 }, (_, i) => currentYear - i);
|
|
34
|
+
const days = Array.from({ length: 31 }, (_, i) => i + 1);
|
|
35
|
+
|
|
36
|
+
const modal = modalOpen ? (
|
|
37
|
+
<div className={modalClasses.overlay} role="dialog" aria-modal="true" aria-label="Modal">
|
|
38
|
+
<div className={modalClasses.backdrop} />
|
|
39
|
+
<div className={modalClasses.dialog}>
|
|
40
|
+
<div className={modalClasses.body}>
|
|
41
|
+
<div><Icon src={AlertTriangle} attrs={{ width: 20 }} /></div>
|
|
42
|
+
<h2 className={modalClasses.title}>Are you over 18 years old?</h2>
|
|
43
|
+
<p>You must be at least 18 years old to access this page. Please verify your age.</p>
|
|
44
|
+
<label>Your Birthday</label>
|
|
45
|
+
<div className={modalClasses.birthday}>
|
|
46
|
+
{/* Month */}
|
|
47
|
+
<select onChange={(e) => setMonth(e.target.value)}>
|
|
48
|
+
<option value="">Month</option>
|
|
49
|
+
{months.map((m) => (
|
|
50
|
+
<option key={m} value={m}>{m}</option>
|
|
51
|
+
))}
|
|
52
|
+
</select>
|
|
53
|
+
|
|
54
|
+
{/* Day */}
|
|
55
|
+
<select onChange={(e) => setDay(e.target.value)}>
|
|
56
|
+
<option value="">Day</option>
|
|
57
|
+
{days.map((d) => (
|
|
58
|
+
<option key={d} value={d}>{d}</option>
|
|
59
|
+
))}
|
|
60
|
+
</select>
|
|
61
|
+
|
|
62
|
+
{/* Year */}
|
|
63
|
+
<select onChange={(e) => setYear(e.target.value)}>
|
|
64
|
+
<option value="">Year</option>
|
|
65
|
+
{years.map((y) => (
|
|
66
|
+
<option key={y} value={y}>{y}</option>
|
|
67
|
+
))}
|
|
68
|
+
</select>
|
|
69
|
+
</div>
|
|
70
|
+
{ageVerificationErrorMessage && <div>{ageVerificationErrorMessage}</div>}
|
|
71
|
+
<div className={modalClasses.actions}>
|
|
72
|
+
<button className={modalClasses.btnUnder} onClick={handleUnderAge}>I'm under 18 years old</button>
|
|
73
|
+
<button className={modalClasses.btnOver} onClick={handleOverAge}>I'm 18 years old</button>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
) : null;
|
|
79
|
+
|
|
80
|
+
return <>{modalOpen ? ReactDOM.createPortal(modal, getPortalRoot()) : null}</>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default AgeVerificationModal;
|
package/src/components/{AgeVerification → AgeVerificationModal}/ageVerificationModal.module.css
RENAMED
|
@@ -1,26 +1,89 @@
|
|
|
1
|
+
.modal label {
|
|
2
|
+
display: block;
|
|
3
|
+
font-size: 14px;
|
|
4
|
+
font-weight: bold;
|
|
5
|
+
margin-bottom: 8px;
|
|
6
|
+
text-align: left;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* Birthday selects */
|
|
10
|
+
.birthday {
|
|
11
|
+
display: flex;
|
|
12
|
+
gap: 10px;
|
|
13
|
+
margin-bottom: 20px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.birthday select {
|
|
17
|
+
flex: 1;
|
|
18
|
+
padding: 8px;
|
|
19
|
+
border-radius: 6px;
|
|
20
|
+
border: 1px solid #ccc;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Buttons */
|
|
24
|
+
.actions {
|
|
25
|
+
display: flex;
|
|
26
|
+
gap: 10px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.btnUnder {
|
|
30
|
+
flex: 1;
|
|
31
|
+
padding: 10px;
|
|
32
|
+
border: none;
|
|
33
|
+
border-radius: 6px;
|
|
34
|
+
color: white;
|
|
35
|
+
font-size: 14px;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.btnOver {
|
|
40
|
+
flex: 1;
|
|
41
|
+
padding: 10px;
|
|
42
|
+
border: none;
|
|
43
|
+
border-radius: 6px;
|
|
44
|
+
color: white;
|
|
45
|
+
font-size: 14px;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.btnUnder {
|
|
50
|
+
background: #ef4444;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.btnOver {
|
|
54
|
+
background: #22c55e;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.btnOver:hover {
|
|
58
|
+
opacity: 0.9;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.btnUnder:hover {
|
|
62
|
+
opacity: 0.9;
|
|
63
|
+
}
|
|
64
|
+
|
|
1
65
|
.overlay{
|
|
2
66
|
position:fixed;
|
|
3
67
|
inset:0;
|
|
4
|
-
z-index:1000;
|
|
5
68
|
display:flex;
|
|
6
|
-
align-items:flex-start;
|
|
7
|
-
justify-content:center;
|
|
8
69
|
padding:5vh 24px;
|
|
9
|
-
font-family:inherit
|
|
70
|
+
font-family:inherit;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
z-index: 9999;
|
|
10
74
|
}
|
|
11
75
|
.backdrop{
|
|
12
76
|
position:absolute;
|
|
13
77
|
inset:0;
|
|
14
|
-
background:rgba(0,0,0
|
|
15
|
-
backdrop-filter:blur(
|
|
78
|
+
background: rgba(0, 0, 0, 0.64);
|
|
79
|
+
backdrop-filter: blur(8px);
|
|
16
80
|
}
|
|
17
81
|
.dialog{
|
|
18
82
|
position:relative;
|
|
19
83
|
background:#fff;
|
|
20
84
|
border-radius:12px;
|
|
21
85
|
box-shadow:0 8px 32px rgba(0,0,0,.25);
|
|
22
|
-
width:clamp(320px,80vw,
|
|
23
|
-
max-height:90vh;
|
|
86
|
+
width:clamp(320px,80vw,500px);
|
|
24
87
|
display:flex;
|
|
25
88
|
flex-direction:column
|
|
26
89
|
}
|
|
@@ -52,8 +115,12 @@
|
|
|
52
115
|
color:#222
|
|
53
116
|
}
|
|
54
117
|
.body{
|
|
55
|
-
padding:
|
|
56
|
-
overflow:auto
|
|
118
|
+
padding: 20px 50px;
|
|
119
|
+
overflow: auto;
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
text-align: center;
|
|
123
|
+
flex-wrap: wrap;
|
|
57
124
|
}
|
|
58
125
|
.triggerBtn{
|
|
59
126
|
background:#f76b1c;
|
|
@@ -173,7 +173,7 @@ const MessagesModal = ({
|
|
|
173
173
|
onClick={(e) => e.stopPropagation()}
|
|
174
174
|
>
|
|
175
175
|
<div className='flex items-center justify-between mb-3'>
|
|
176
|
-
<h3 className='text-[18px] font-semibold'>
|
|
176
|
+
<h3 className='text-[18px] font-semibold'>Send Message</h3>
|
|
177
177
|
<button onClick={onClose} aria-label='Close' className='text-gray-600 text-[30px] hover:text-black'>×</button>
|
|
178
178
|
</div>
|
|
179
179
|
<div className='flex gap-4'>
|
|
@@ -184,7 +184,7 @@ const MessagesModal = ({
|
|
|
184
184
|
|
|
185
185
|
<form onSubmit={handleSubmitNewMessage} className='mb-3 rounded-[6px] p-1 flex flex-col gap-2'>
|
|
186
186
|
<div className='text-[13px] font-semibold flex items-center justify-between'>
|
|
187
|
-
<span>
|
|
187
|
+
<span>Send Message to {seller?.name}</span>
|
|
188
188
|
{/* {(sortedThreads.length > 0) && (
|
|
189
189
|
<button
|
|
190
190
|
type='button'
|
|
@@ -217,7 +217,7 @@ const MessagesModal = ({
|
|
|
217
217
|
aria-busy={isSendingNew}
|
|
218
218
|
>
|
|
219
219
|
{isSendingNew
|
|
220
|
-
? formatMessage({ id: 'messages.compose.sending', defaultMessage: '
|
|
220
|
+
? formatMessage({ id: 'messages.compose.sending', defaultMessage: 'Sending Message...' })
|
|
221
221
|
: formatMessage({ id: 'messages.compose.send', defaultMessage: 'Send Message' })}
|
|
222
222
|
</button>
|
|
223
223
|
</div>
|
|
@@ -8,6 +8,8 @@ import defaultClasses from './messages.module.css';
|
|
|
8
8
|
import cn from 'classnames';
|
|
9
9
|
import React, { useMemo, useState, useEffect } from 'react';
|
|
10
10
|
import ChatContent from '@riosst100/pwa-marketplace/src/components/LiveChat/chatContent';
|
|
11
|
+
import { Link } from "react-router-dom";
|
|
12
|
+
import { Star1, Verify, Sms, Message, Shop, ArrowUp2 } from 'iconsax-react';
|
|
11
13
|
|
|
12
14
|
const Messages = props => {
|
|
13
15
|
const {
|
|
@@ -18,7 +20,6 @@ const Messages = props => {
|
|
|
18
20
|
onSend,
|
|
19
21
|
loading } = props;
|
|
20
22
|
const classes = useStyle(defaultClasses, props.classes);
|
|
21
|
-
// const { becomeSellerProps } = useMessages(props);
|
|
22
23
|
const { formatMessage } = useIntl();
|
|
23
24
|
|
|
24
25
|
const [selectedThread, setSelectedThread] = useState(null);
|
|
@@ -61,29 +62,23 @@ const Messages = props => {
|
|
|
61
62
|
}, [messages, seller]);
|
|
62
63
|
|
|
63
64
|
const sortedThreads = useMemo(() => {
|
|
64
|
-
return [...threads].sort((a, b) => new Date(
|
|
65
|
+
return [...threads].sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
|
65
66
|
}, [threads]);
|
|
67
|
+
|
|
68
|
+
const { location } = globalThis;
|
|
69
|
+
|
|
70
|
+
const query = new URLSearchParams(location.search);
|
|
66
71
|
|
|
67
72
|
useEffect(() => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (!stillThere) {
|
|
75
|
-
setSelectedThread(latest);
|
|
76
|
-
} else {
|
|
77
|
-
// Replace with the fresh thread data so new details from polling appear
|
|
78
|
-
setSelectedThread(stillThere);
|
|
79
|
-
if (selectLatestAfterSend) {
|
|
80
|
-
setSelectedThread(latest);
|
|
81
|
-
setSelectLatestAfterSend(false);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
73
|
+
const messageId = query.get('id') || null;
|
|
74
|
+
if (messageId && sortedThreads?.length) {
|
|
75
|
+
const selectedThread = sortedThreads.find(t => t.message_id == messageId);
|
|
76
|
+
if (selectedThread) {
|
|
77
|
+
console.log('selectedThread',selectedThread)
|
|
78
|
+
setSelectedThread(selectedThread);
|
|
84
79
|
}
|
|
85
80
|
}
|
|
86
|
-
}, [sortedThreads,
|
|
81
|
+
}, [sortedThreads, query]);
|
|
87
82
|
|
|
88
83
|
// Tandai sudah pernah load minimal sekali untuk mencegah flicker
|
|
89
84
|
useEffect(() => {
|
|
@@ -165,6 +160,9 @@ const Messages = props => {
|
|
|
165
160
|
setSelectLatestAfterSend(true);
|
|
166
161
|
};
|
|
167
162
|
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
168
166
|
return (
|
|
169
167
|
<>
|
|
170
168
|
<div className={cn(classes.leftContent, '')}>
|
|
@@ -176,124 +174,99 @@ const Messages = props => {
|
|
|
176
174
|
</StoreTitle>
|
|
177
175
|
<div className={cn(classes.leftContentContainer, 'border !border-gray-100 lg_rounded-md !rounded-lg')}>
|
|
178
176
|
<div class="auth-left">
|
|
179
|
-
<div className='px-3 py-
|
|
177
|
+
<div className='text-[16px] px-3 py-3 border-gray-100 border-b font-medium flex items-center justify-between'>
|
|
180
178
|
<span>{formatMessage({ id: 'messages.sidebar.title', defaultMessage: 'Messages' })}</span>
|
|
181
|
-
<button
|
|
182
|
-
type='button'
|
|
183
|
-
onClick={() => {
|
|
184
|
-
setIsComposing(true);
|
|
185
|
-
setSelectedThread(null);
|
|
186
|
-
}}
|
|
187
|
-
className='text-[12px] px-2 py-1 rounded border border-[#FF6E26] text-[#FF6E26] hover:bg-[#FF6E26] hover:text-white'
|
|
188
|
-
>
|
|
189
|
-
{formatMessage({ id: 'messages.compose.newMessage', defaultMessage: 'Send New Message' })}
|
|
190
|
-
</button>
|
|
191
179
|
</div>
|
|
192
180
|
<div className='max-h-[420px] overflow-y-auto'>
|
|
193
|
-
{(!
|
|
181
|
+
{(!hasLoadedOnce && loading) ? (
|
|
194
182
|
<div className='p-3 text-sm text-gray-500'>{formatMessage({ id: 'messages.loading', defaultMessage: 'Loading…' })}</div>
|
|
183
|
+
) : !sortedThreads || sortedThreads.length === 0 ? (
|
|
184
|
+
<div className={cn(classes.noMessages, 'text-sm text-gray-500')}>{formatMessage({ id: 'messages.detail.noMessages', defaultMessage: 'No messages yet.' })}</div>
|
|
195
185
|
) : (
|
|
196
|
-
sortedThreads.map(t =>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
186
|
+
sortedThreads && sortedThreads.length && sortedThreads.map(t =>
|
|
187
|
+
// console.log('fffff',t)
|
|
188
|
+
<div>
|
|
189
|
+
<hr className="!border-gray-100" style={{"width": "100%"}} />
|
|
190
|
+
<button
|
|
191
|
+
key={t.message_id}
|
|
192
|
+
onClick={() => setSelectedThread(t)}
|
|
193
|
+
className={cn('w-full text-left px-3 py-2 border-b hover:bg-gray-50',
|
|
194
|
+
selectedThread?.message_id === t.message_id && 'bg-gray-100')}
|
|
195
|
+
>
|
|
196
|
+
<div className='flex items-center justify-between gap-2'>
|
|
197
|
+
<div style={{"color": "#f76b1c"}}className='text-[12px] font-semibold line-clamp-1'>{t.seller ? t.seller.name : 'Unknown Seller' }</div>
|
|
198
|
+
{/* <div className='text-[12px] flex-shrink-0'>
|
|
199
|
+
{renderStatusBadge(t.status)}
|
|
200
|
+
</div> */}
|
|
201
|
+
</div>
|
|
202
|
+
<div className='flex items-center justify-between gap-2'>
|
|
203
|
+
<div className='text-[12px] line-clamp-1'>Subject: {t.subject || formatMessage({ id: 'messages.noSubject', defaultMessage: 'No Subject' })}</div>
|
|
204
|
+
{/* <div className='text-[12px] flex-shrink-0'>
|
|
205
|
+
{renderStatusBadge(t.status)}
|
|
206
|
+
</div> */}
|
|
207
|
+
</div>
|
|
208
|
+
<div className='text-[11px] text-gray-500 mt-1'>
|
|
209
|
+
{t.details.total_count ? t.details.items[t.details.items.length-1].content : t.description}
|
|
210
|
+
</div>
|
|
211
|
+
<div className='text-[10px] text-gray-500 mt-2'>{new Date(t.created_at).toLocaleString()}</div>
|
|
212
|
+
</button>
|
|
213
|
+
</div>
|
|
214
|
+
)
|
|
212
215
|
)}
|
|
213
216
|
</div>
|
|
214
217
|
</div>
|
|
215
218
|
</div>
|
|
216
219
|
</div>
|
|
217
220
|
<div className={classes.root}>
|
|
218
|
-
<div className="lg_border-2 lg_border-solid lg_border-subtle lg_rounded-md !border !border-gray-100 p-6 !rounded-lg">
|
|
221
|
+
<div style={{"padding": "20px"}} className="lg_border-2 lg_border-solid lg_border-subtle lg_rounded-md !border !border-gray-100 p-6 !rounded-lg">
|
|
219
222
|
{/* Compose new message (shown only when composing OR when no thread exists) */}
|
|
220
|
-
{
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
{
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
/>
|
|
241
|
-
<textarea
|
|
242
|
-
rows={3}
|
|
243
|
-
className='border border-gray-100 rounded px-3 py-2 outline-none focus:ring-2 focus:ring-[#FF6E26]'
|
|
244
|
-
placeholder={formatMessage({ id: 'messages.compose.contentPlaceholder', defaultMessage: 'Write a message to the seller…' })}
|
|
245
|
-
value={newContent}
|
|
246
|
-
onChange={(e) => setNewContent(e.target.value)}
|
|
247
|
-
/>
|
|
248
|
-
<div className='flex justify-end'>
|
|
223
|
+
{sortedThreads.length > 0 && (
|
|
224
|
+
<div className='flex items-center justify-between mb-2'>
|
|
225
|
+
{selectedThread && (
|
|
226
|
+
<>
|
|
227
|
+
<div style={{"textAlign": "left"}}>
|
|
228
|
+
{selectedThread.seller && <Link to={"/seller/"+selectedThread.seller.url_key}>
|
|
229
|
+
{/* <div class="flex items-center justify-center gap-[10px] relative">
|
|
230
|
+
<Shop color="#f76b1c" size={14} variant="Outline" className='stroke-[#f76b1c]' />
|
|
231
|
+
<div class="relative xs_hidden lg_flex w-fit text-[12px] text-[#f76b1c] text-[14px] tracking-[0] leading-[15px] whitespace-nowrap">
|
|
232
|
+
Visit Store
|
|
233
|
+
</div>
|
|
234
|
+
</div> */}
|
|
235
|
+
<div style={{"color": "#f76b1c"}}className='text-[14px] font-semibold line-clamp-1'>{selectedThread.seller ? selectedThread.seller.name : 'Unknown Seller' }</div>
|
|
236
|
+
</Link>}
|
|
237
|
+
<div className='mt-3 mb-1 text-[12px]'>Subject: {selectedThread?.subject || formatMessage({ id: 'messages.noSubject', defaultMessage: 'No Subject' })}</div>
|
|
238
|
+
{/* <div className='text-[12px] text-gray-600 flex items-center gap-2'>
|
|
239
|
+
<span>{formatMessage({ id: 'messages.detail.status', defaultMessage: 'Status:' })}</span>
|
|
240
|
+
{selectedThread && renderStatusBadge(selectedThread.status)}
|
|
241
|
+
</div> */}
|
|
242
|
+
</div>
|
|
249
243
|
<button
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
disabled
|
|
253
|
-
aria-busy={isSendingNew}
|
|
244
|
+
onClick={handleDeleteThread}
|
|
245
|
+
disabled={isDeleting}
|
|
246
|
+
className='text-[12px] px-3 py-1 rounded border text-red-600 border-red-300 hover:bg-red-50 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2'
|
|
254
247
|
>
|
|
255
|
-
{
|
|
256
|
-
|
|
257
|
-
|
|
248
|
+
{isDeleting && (
|
|
249
|
+
<span className='w-3 h-3 border-2 border-red-200 border-t-red-500 rounded-full animate-spin' aria-hidden='true'></span>
|
|
250
|
+
)}
|
|
251
|
+
{isDeleting
|
|
252
|
+
? formatMessage({ id: 'messages.detail.deleting', defaultMessage: 'Deleting...' })
|
|
253
|
+
: formatMessage({ id: 'messages.detail.delete', defaultMessage: 'Delete Message' })}
|
|
258
254
|
</button>
|
|
259
|
-
|
|
260
|
-
</form>
|
|
261
|
-
)}
|
|
262
|
-
{!isComposing && sortedThreads.length > 0 && (
|
|
263
|
-
<div className='flex items-center justify-between mb-2'>
|
|
264
|
-
<div>
|
|
265
|
-
<div className='text-[14px] font-semibold'>{selectedThread?.subject || formatMessage({ id: 'messages.noSubject', defaultMessage: 'No Subject' })}</div>
|
|
266
|
-
{/* <div className='text-[12px] text-gray-600 flex items-center gap-2'>
|
|
267
|
-
<span>{formatMessage({ id: 'messages.detail.status', defaultMessage: 'Status:' })}</span>
|
|
268
|
-
{selectedThread && renderStatusBadge(selectedThread.status)}
|
|
269
|
-
</div> */}
|
|
270
|
-
</div>
|
|
271
|
-
{selectedThread && (
|
|
272
|
-
<button
|
|
273
|
-
onClick={handleDeleteThread}
|
|
274
|
-
disabled={isDeleting}
|
|
275
|
-
className='text-[12px] px-3 py-1 rounded border text-red-600 border-red-300 hover:bg-red-50 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2'
|
|
276
|
-
>
|
|
277
|
-
{isDeleting && (
|
|
278
|
-
<span className='w-3 h-3 border-2 border-red-200 border-t-red-500 rounded-full animate-spin' aria-hidden='true'></span>
|
|
279
|
-
)}
|
|
280
|
-
{isDeleting
|
|
281
|
-
? formatMessage({ id: 'messages.detail.deleting', defaultMessage: 'Deleting...' })
|
|
282
|
-
: formatMessage({ id: 'messages.detail.delete', defaultMessage: 'Delete Message' })}
|
|
283
|
-
</button>
|
|
255
|
+
</>
|
|
284
256
|
)}
|
|
285
257
|
</div>
|
|
286
258
|
)}
|
|
287
|
-
{
|
|
288
|
-
|
|
289
|
-
{selectedThread ? (
|
|
259
|
+
{sortedThreads.length > 0 && selectedThread ? (
|
|
260
|
+
<div className='!border-gray-100 border rounded-[6px] p-3 flex-1 min-h-[320px]'>
|
|
290
261
|
<ChatContent chatData={chatData} />
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
262
|
+
</div>
|
|
263
|
+
) : (
|
|
264
|
+
<div className={classes.noConversationContainer}>
|
|
265
|
+
<img src="https://cdni.iconscout.com/illustration/premium/thumb/empty-message-illustration-svg-download-png-8593289.png" style={{"width":"20%"}}/>
|
|
266
|
+
<div className='text-sm text-gray-500'>{formatMessage({ id: 'messages.detail.noConversation', defaultMessage: 'Select a message to view the conversation.' })}</div>
|
|
267
|
+
</div>
|
|
295
268
|
)}
|
|
296
|
-
{
|
|
269
|
+
{sortedThreads.length > 0 && selectedThread && (
|
|
297
270
|
<form onSubmit={handleSubmitReply} className='mt-3 flex gap-2'>
|
|
298
271
|
{(() => {
|
|
299
272
|
const statusNum = Number(selectedThread.status);
|
|
@@ -29,6 +29,21 @@
|
|
|
29
29
|
text-align: left;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
.noConversationContainer {
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-wrap: wrap;
|
|
36
|
+
align-content: center;
|
|
37
|
+
justify-content: flex-start;
|
|
38
|
+
align-items: center;
|
|
39
|
+
row-gap: 15px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.noMessages {
|
|
43
|
+
text-align: center;
|
|
44
|
+
padding: 60px;
|
|
45
|
+
}
|
|
46
|
+
|
|
32
47
|
.rootContainer {
|
|
33
48
|
justify-content: space-around;
|
|
34
49
|
display: flex;
|