@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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@riosst100/pwa-marketplace",
3
3
  "author": "riosst100@gmail.com",
4
- "version": "3.2.9",
4
+ "version": "3.3.1",
5
5
  "main": "src/index.js",
6
6
  "pwa-studio": {
7
7
  "targets": {
@@ -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 './ageVerificationModal';
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;
@@ -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,.4);
15
- backdrop-filter:blur(2px)
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,370px);
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:16px 20px;
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;
@@ -0,0 +1,2 @@
1
+ export { default } from './ageVerificationModal';
2
+ // export { default as AgeVerificationModalShimmer } from './ageVerificationModal.shimmer';
@@ -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'>{formatMessage({ id: 'messages.titleWithSeller', defaultMessage: 'Start Conversation With ' })} {seller?.name}</h3>
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>{formatMessage({ id: 'messages.compose.header', defaultMessage: 'Create New Message' })}</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: 'Send Message...' })
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(a.created_at) - new Date(b.created_at));
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
- if (!isComposing && sortedThreads?.length) {
69
- const latest = sortedThreads[sortedThreads.length - 1];
70
- if (!selectedThread) {
71
- setSelectedThread(latest);
72
- } else {
73
- const stillThere = sortedThreads.find(t => t.message_id === selectedThread.message_id);
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, isComposing, selectedThread, selectLatestAfterSend]);
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-2 border-b font-medium flex items-center justify-between'>
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
- {(!isComposing && !hasLoadedOnce && loading && sortedThreads.length === 0) ? (
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
- <button
198
- key={t.message_id}
199
- onClick={() => setSelectedThread(t)}
200
- className={cn('w-full text-left px-3 py-2 border-b hover:bg-gray-50',
201
- selectedThread?.message_id === t.message_id && 'bg-gray-100')}
202
- >
203
- <div className='flex items-center justify-between gap-2'>
204
- <div className='text-[13px] font-semibold line-clamp-1'>{t.subject || formatMessage({ id: 'messages.noSubject', defaultMessage: 'No Subject' })}</div>
205
- {/* <div className='text-[12px] flex-shrink-0'>
206
- {renderStatusBadge(t.status)}
207
- </div> */}
208
- </div>
209
- <div className='text-[11px] text-gray-500 mt-1'>{formatMessage({ id: 'messages.createdAt', defaultMessage: 'Created:' })} {new Date(t.created_at).toLocaleString()}</div>
210
- </button>
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
- {(isComposing || sortedThreads.length === 0) && (
221
- <form onSubmit={handleSubmitNewMessage} className='mb-3 rounded-[6px] p-1 flex flex-col gap-2'>
222
- <div className='text-[13px] font-semibold flex items-center justify-between'>
223
- <span>{formatMessage({ id: 'messages.compose.header', defaultMessage: 'Create New Message' })}</span>
224
- {(sortedThreads.length > 0) && (
225
- <button
226
- type='button'
227
- onClick={() => setIsComposing(false)}
228
- className='text-[12px] px-2 py-1 !border-gray-100 rounded border text-gray-600 hover:bg-gray-50'
229
- >
230
- {formatMessage({ id: 'messages.compose.cancel', defaultMessage: 'Cancel' })}
231
- </button>
232
- )}
233
- </div>
234
- <input
235
- type='text'
236
- className='border border-gray-100 rounded px-3 py-2 outline-none focus:ring-2 focus:ring-[#FF6E26]'
237
- placeholder={formatMessage({ id: 'messages.compose.subjectPlaceholder', defaultMessage: 'Subject' })}
238
- value={newSubject}
239
- onChange={(e) => setNewSubject(e.target.value)}
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
- type='submit'
251
- className={cn('px-4 py-2 rounded-[6px] text-white disabled:opacity-50 disabled:cursor-not-allowed', isSendingNew ? 'bg-gray-500 text-gray-700' : 'bg-[#FF6E26]')}
252
- disabled={isSendingNew}
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
- {isSendingNew
256
- ? formatMessage({ id: 'messages.compose.sending', defaultMessage: 'Send Message...' })
257
- : formatMessage({ id: 'messages.compose.send', defaultMessage: 'Send Message' })}
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
- </div>
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
- {!isComposing && sortedThreads.length > 0 && (
288
- <div className='!border-gray-100 border rounded-[6px] p-3 flex-1 min-h-[320px]'>
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
- <div className='text-sm text-gray-500'>{formatMessage({ id: 'messages.detail.selectPrompt', defaultMessage: 'Select a message to view the conversation.' })}</div>
293
- )}
294
- </div>
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
- {!isComposing && sortedThreads.length > 0 && selectedThread && (
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;