@riosst100/pwa-marketplace 2.9.6 → 2.9.7
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/LiveChat/chatContent.js +28 -13
- package/src/components/OrderDetail/orderDetail.js +86 -26
- package/src/components/RMAPage/RMACreate.js +107 -116
- package/src/components/RMAPage/RMADetail.js +150 -113
- package/src/components/RMAPage/components/productItem.js +32 -7
- package/src/components/RMAPage/orderRow.js +28 -17
- package/src/components/commons/Select/index.js +6 -2
- package/src/overwrites/peregrine/lib/talons/RMAPage/rmaPage.gql.js +33 -1
- package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/orderRow.js +39 -40
- package/src/talons/RMAPage/useRmaPage.js +40 -6
package/package.json
CHANGED
|
@@ -1,26 +1,42 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useRef, useEffect } from 'react';
|
|
2
2
|
import cn from 'classnames';
|
|
3
3
|
|
|
4
4
|
const chatContent = (props) => {
|
|
5
5
|
const { chatData } = props;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
|
|
8
|
+
const orderedChatData = [...chatData].reverse();
|
|
9
|
+
const messagesContainerRef = useRef(null);
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (messagesContainerRef.current) {
|
|
13
|
+
messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
|
|
14
|
+
}
|
|
15
|
+
}, [orderedChatData]);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<div
|
|
20
|
+
ref={messagesContainerRef}
|
|
21
|
+
className='flex flex-col gap-2 w-full overflow-y-auto max-h-[400px]'
|
|
22
|
+
>
|
|
23
|
+
{orderedChatData.map((chat, idx) => (
|
|
12
24
|
<div
|
|
25
|
+
key={idx}
|
|
13
26
|
className={cn(
|
|
14
27
|
'w-fit max-w-[70%] flex flex-col gap-1',
|
|
15
28
|
chat.type === 'seller' ? 'self-start ' : 'self-end items-end',
|
|
16
|
-
|
|
17
29
|
)}
|
|
18
30
|
>
|
|
31
|
+
{chat.senderName && (
|
|
32
|
+
<span className='text-[12px] text-gray-500 font-semibold mb-[-2px]'>
|
|
33
|
+
{chat.senderName}
|
|
34
|
+
</span>
|
|
35
|
+
)}
|
|
19
36
|
<div
|
|
20
37
|
className={cn(
|
|
21
38
|
'w-fit py-2 px-3 rounded-md flex flex-col',
|
|
22
39
|
chat.type === 'seller' ? 'bg-gray-150 rounded-tl-none' : ' bg-[#D9D9D9] rounded-tr-none',
|
|
23
|
-
|
|
24
40
|
)}
|
|
25
41
|
>
|
|
26
42
|
{chat.message}
|
|
@@ -29,11 +45,10 @@ const chatContent = (props) => {
|
|
|
29
45
|
{chat.timeStamp}
|
|
30
46
|
</span>
|
|
31
47
|
</div>
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
48
|
+
))}
|
|
49
|
+
</div>
|
|
50
|
+
</>
|
|
51
|
+
)
|
|
37
52
|
}
|
|
38
53
|
|
|
39
54
|
export default chatContent
|
|
@@ -3,11 +3,11 @@ import { useIntl } from 'react-intl';
|
|
|
3
3
|
import { StoreTitle } from '@magento/venia-ui/lib/components/Head';
|
|
4
4
|
import Button from '@magento/venia-ui/lib/components/Button';
|
|
5
5
|
import cn from 'classnames';
|
|
6
|
-
import { Printer } from 'iconsax-react';
|
|
6
|
+
import { Printer, ConvertCard } from 'iconsax-react';
|
|
7
7
|
import ItemsOrdered from './components/itemsOrdered';
|
|
8
8
|
import RMAList from './components/rmaList';
|
|
9
9
|
import useRmaPage from '@riosst100/pwa-marketplace/src/talons/RMAPage/useRmaPage';
|
|
10
|
-
import { useLocation, useParams } from 'react-router-dom';
|
|
10
|
+
import { useLocation, useParams, useHistory } from 'react-router-dom';
|
|
11
11
|
import useOrderHistoryPage from '@riosst100/pwa-marketplace/src/talons/OrderHistoryPage/useOrderHistoryPage';
|
|
12
12
|
|
|
13
13
|
const OrderDetail = (props) => {
|
|
@@ -27,6 +27,45 @@ const OrderDetail = (props) => {
|
|
|
27
27
|
|
|
28
28
|
// Keep a stable local state for order to avoid it turning undefined on re-renders
|
|
29
29
|
const [order, setOrder] = useState(() => props.order || location.state?.order || null);
|
|
30
|
+
const history = useHistory();
|
|
31
|
+
// Button Return Items: tampil jika status Complete/Delivered
|
|
32
|
+
const isReturnAllowed = useMemo(() => {
|
|
33
|
+
if (!order) return false;
|
|
34
|
+
const status = order.status;
|
|
35
|
+
if (!status) return false;
|
|
36
|
+
return (
|
|
37
|
+
status === 'Complete' ||
|
|
38
|
+
status.toLowerCase() === 'delivered' // antisipasi backend bisa lowercase
|
|
39
|
+
);
|
|
40
|
+
}, [order]);
|
|
41
|
+
|
|
42
|
+
// Handler sama seperti orderRow
|
|
43
|
+
const handleNewReturn = useCallback(() => {
|
|
44
|
+
if (!order) return;
|
|
45
|
+
let orderWithImages = { ...order };
|
|
46
|
+
if (orderWithImages.items && Array.isArray(orderWithImages.items)) {
|
|
47
|
+
orderWithImages = {
|
|
48
|
+
...orderWithImages,
|
|
49
|
+
items: orderWithImages.items.map(it => {
|
|
50
|
+
let product_image = '';
|
|
51
|
+
if (typeof it.product_image_url === 'string' && it.product_image_url.trim() !== '') {
|
|
52
|
+
product_image = it.product_image_url;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
...it,
|
|
56
|
+
product_image
|
|
57
|
+
};
|
|
58
|
+
})
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
localStorage.setItem('rma_order', JSON.stringify(orderWithImages));
|
|
63
|
+
} catch (e) {}
|
|
64
|
+
history.push({
|
|
65
|
+
pathname: `/return/create/${order.number}`,
|
|
66
|
+
state: { order: orderWithImages }
|
|
67
|
+
});
|
|
68
|
+
}, [order, history]);
|
|
30
69
|
|
|
31
70
|
// Fallback: fetch order by orderNumber from order history if not found
|
|
32
71
|
const { orders } = useOrderHistoryPage();
|
|
@@ -149,31 +188,52 @@ const OrderDetail = (props) => {
|
|
|
149
188
|
</div>
|
|
150
189
|
</div>
|
|
151
190
|
<div className='flex flex-col md_flex-row gap-x-4 justify-end no-print'>
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
191
|
+
<div className='flex flex-row gap-x-3'>
|
|
192
|
+
<Button
|
|
193
|
+
priority='high'
|
|
194
|
+
classes={{
|
|
195
|
+
content: "flex justify-center gap-x-2.5 items-center text-[14px] font-medium capitalize"
|
|
196
|
+
}}
|
|
197
|
+
className={cn(
|
|
198
|
+
"bg-white px-6 py-2 rounded-md text-gray-900 hover_!text-white border border-solid",
|
|
199
|
+
"border-gray-900 focus-visible_outline-none hover_bg-gray-900 min-h-[40px]"
|
|
200
|
+
)}
|
|
201
|
+
onClick={handlePrint}
|
|
202
|
+
aria-label={formatMessage({ id: 'order.printInvoices', defaultMessage: 'Print Invoices' })}
|
|
203
|
+
>
|
|
204
|
+
<span className='flex gap-x-3 justify-center group-hover_!text-white'>
|
|
205
|
+
<Printer
|
|
206
|
+
size="24"
|
|
207
|
+
className=''
|
|
208
|
+
/>
|
|
209
|
+
{
|
|
210
|
+
formatMessage({
|
|
211
|
+
id: 'order.PrintInvoices',
|
|
212
|
+
defaultMessage: 'Print Invoices'
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
</span>
|
|
216
|
+
</Button>
|
|
217
|
+
{isReturnAllowed && (
|
|
218
|
+
<Button
|
|
219
|
+
priority='high'
|
|
220
|
+
classes={{
|
|
221
|
+
content: "flex justify-center gap-x-2.5 items-center text-[14px] font-medium capitalize"
|
|
222
|
+
}}
|
|
223
|
+
className={cn(
|
|
224
|
+
"bg-blue-700 px-6 py-2 rounded-md text-white border border-blue-700 min-h-[40px]",
|
|
225
|
+
"transition-all duration-300 ease-in-out flex items-center gap-2"
|
|
226
|
+
)}
|
|
227
|
+
onClick={handleNewReturn}
|
|
228
|
+
aria-label={formatMessage({ id: 'orderRow.returnProduct', defaultMessage: 'Return Product' })}
|
|
229
|
+
>
|
|
230
|
+
<span className='flex gap-x-3 justify-center'>
|
|
231
|
+
<ConvertCard size={22} color="#fff" />
|
|
232
|
+
{formatMessage({ id: 'orderRow.ReturnItems', defaultMessage: 'Return Items' })}
|
|
233
|
+
</span>
|
|
234
|
+
</Button>
|
|
160
235
|
)}
|
|
161
|
-
|
|
162
|
-
aria-label={formatMessage({ id: 'order.printInvoices', defaultMessage: 'Print Invoices' })}
|
|
163
|
-
>
|
|
164
|
-
<span className='flex gap-x-3 justify-center group-hover_!text-white'>
|
|
165
|
-
<Printer
|
|
166
|
-
size="24"
|
|
167
|
-
className=''
|
|
168
|
-
/>
|
|
169
|
-
{
|
|
170
|
-
formatMessage({
|
|
171
|
-
id: 'order.PrintInvoices',
|
|
172
|
-
defaultMessage: 'Print Invoices'
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
</span>
|
|
176
|
-
</Button>
|
|
236
|
+
</div>
|
|
177
237
|
</div>
|
|
178
238
|
</div>
|
|
179
239
|
<div className="block block-order-details-view">
|
|
@@ -60,15 +60,11 @@ const RMACreate = () => {
|
|
|
60
60
|
// Set default values when checked true
|
|
61
61
|
let defaults = {};
|
|
62
62
|
if (checked) {
|
|
63
|
-
const cfg = rmaConfigData && rmaConfigData.lofmpRmaConfigurations;
|
|
64
|
-
const firstReason = cfg && cfg.reasons && cfg.reasons.length > 0 ? cfg.reasons[0].id : null;
|
|
65
|
-
const firstCondition = cfg && cfg.conditions && cfg.conditions.length > 0 ? cfg.conditions[0].id : null;
|
|
66
|
-
const firstResolution = cfg && cfg.resolutions && cfg.resolutions.length > 0 ? cfg.resolutions[0].id : null;
|
|
67
63
|
defaults = {
|
|
68
64
|
qty_requested: current.qty_requested != null ? current.qty_requested : 1,
|
|
69
|
-
reason_id:
|
|
70
|
-
condition_id:
|
|
71
|
-
resolution_id:
|
|
65
|
+
reason_id: '',
|
|
66
|
+
condition_id: '',
|
|
67
|
+
resolution_id: ''
|
|
72
68
|
};
|
|
73
69
|
}
|
|
74
70
|
return {
|
|
@@ -82,6 +78,20 @@ const RMACreate = () => {
|
|
|
82
78
|
});
|
|
83
79
|
};
|
|
84
80
|
|
|
81
|
+
|
|
82
|
+
// Check if at least one item is checked and all checked items have required fields
|
|
83
|
+
const checkedItems = Object.values(itemReturnState).filter(state => state && state.checked);
|
|
84
|
+
const isAllCheckedItemsValid =
|
|
85
|
+
checkedItems.length > 0 &&
|
|
86
|
+
checkedItems.every(state => {
|
|
87
|
+
return (
|
|
88
|
+
state.qty_requested &&
|
|
89
|
+
state.reason_id !== undefined && state.reason_id !== null && state.reason_id !== '' && state.reason_id !== 0 && state.reason_id !== "" &&
|
|
90
|
+
state.condition_id !== undefined && state.condition_id !== null && state.condition_id !== '' && state.condition_id !== 0 && state.condition_id !== "" &&
|
|
91
|
+
state.resolution_id !== undefined && state.resolution_id !== null && state.resolution_id !== '' && state.resolution_id !== 0 && state.resolution_id !== ""
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
85
95
|
// Build input mutation sesuai struktur backend
|
|
86
96
|
const buildRmaInput = () => {
|
|
87
97
|
if (!order || !order.items) return null;
|
|
@@ -151,16 +161,19 @@ const RMACreate = () => {
|
|
|
151
161
|
const message = 'RMA request created successfully!';
|
|
152
162
|
setSubmitSuccess(message);
|
|
153
163
|
addToast({
|
|
154
|
-
type: '
|
|
155
|
-
icon: <CheckCircleIcon size={18} />,
|
|
164
|
+
type: 'success',
|
|
165
|
+
icon: <CheckCircleIcon size={18} />,
|
|
156
166
|
message,
|
|
157
167
|
dismissable: true,
|
|
158
168
|
timeout: 4000
|
|
159
169
|
});
|
|
160
|
-
// Redirect to RMA
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
170
|
+
// Redirect to RMA detail page after brief delay
|
|
171
|
+
const rmaId = payload.rma_id || (payload.rma && payload.rma.id);
|
|
172
|
+
if (rmaId) {
|
|
173
|
+
setTimeout(() => {
|
|
174
|
+
history.push(`/return/view/${rmaId}`);
|
|
175
|
+
}, 400);
|
|
176
|
+
}
|
|
164
177
|
} else {
|
|
165
178
|
const message = (payload && payload.message) || 'Failed to create RMA.';
|
|
166
179
|
setSubmitError(message);
|
|
@@ -176,104 +189,79 @@ const RMACreate = () => {
|
|
|
176
189
|
const { formatMessage } = useIntl();
|
|
177
190
|
const PAGE_TITLE = formatMessage({
|
|
178
191
|
id: 'Quotes.pageTitleTextRMACreate',
|
|
179
|
-
defaultMessage: 'New Return'
|
|
192
|
+
defaultMessage: 'New Return For Order'
|
|
180
193
|
});
|
|
181
194
|
|
|
182
|
-
const dummyChat = [
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
]
|
|
195
|
+
// const dummyChat = [
|
|
196
|
+
// {
|
|
197
|
+
// "message": "May I help you",
|
|
198
|
+
// "type": "seller",
|
|
199
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
200
|
+
// },
|
|
201
|
+
// {
|
|
202
|
+
// "message": "Yes",
|
|
203
|
+
// "type": "buyer",
|
|
204
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
205
|
+
// },
|
|
206
|
+
// {
|
|
207
|
+
// "message": "can i get a discount",
|
|
208
|
+
// "type": "buyer",
|
|
209
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
210
|
+
// },
|
|
211
|
+
// {
|
|
212
|
+
// "message": "you can redeem this code 'JunePlay'",
|
|
213
|
+
// "type": "seller",
|
|
214
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
215
|
+
// },
|
|
216
|
+
// {
|
|
217
|
+
// "message": "or this code 'collectfest'",
|
|
218
|
+
// "type": "seller",
|
|
219
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
220
|
+
// },
|
|
221
|
+
// {
|
|
222
|
+
// "message": "May I help you",
|
|
223
|
+
// "type": "seller",
|
|
224
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
225
|
+
// },
|
|
226
|
+
// {
|
|
227
|
+
// "message": "Yes",
|
|
228
|
+
// "type": "buyer",
|
|
229
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
230
|
+
// },
|
|
231
|
+
// {
|
|
232
|
+
// "message": "can i get a discount",
|
|
233
|
+
// "type": "buyer",
|
|
234
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
235
|
+
// },
|
|
236
|
+
// {
|
|
237
|
+
// "message": "you can redeem this code 'JunePlay'",
|
|
238
|
+
// "type": "seller",
|
|
239
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
240
|
+
// },
|
|
241
|
+
// {
|
|
242
|
+
// "message": "or this code 'collectfest'",
|
|
243
|
+
// "type": "seller",
|
|
244
|
+
// "timeStamp": "03-06-2024 2:59 pm"
|
|
245
|
+
// },
|
|
246
|
+
// ]
|
|
234
247
|
|
|
235
248
|
return (
|
|
236
249
|
<div className='relative grid gap-y-md'>
|
|
237
250
|
<StoreTitle>{PAGE_TITLE}</StoreTitle>
|
|
238
251
|
<div aria-live="polite" className="text-xl font-medium text-left">
|
|
239
|
-
{PAGE_TITLE} {order ? `-
|
|
252
|
+
{PAGE_TITLE} {order ? `- #${order.number || order.increment_id}` : ''}
|
|
240
253
|
</div>
|
|
241
254
|
<div className='block relative'>
|
|
242
255
|
<div aria-live="polite" className="text-lg font-medium text-left mb-4">
|
|
243
256
|
{
|
|
244
257
|
formatMessage({
|
|
245
258
|
id: 'RMA.requestReturnInformation',
|
|
246
|
-
defaultMessage: '
|
|
259
|
+
defaultMessage: 'Order Information'
|
|
247
260
|
})
|
|
248
261
|
}
|
|
249
262
|
</div>
|
|
250
263
|
<div className='rounded-md border border-gray-100 px-4 py-6 flex flex-col md_flex-row justify-between gap-x-10'>
|
|
251
264
|
<div className='flex flex-col gap-y-4 w-full mb-4 md_0 md_w-6/12'>
|
|
252
|
-
<div className='block p-2 -ml-2 -mr-2 bg-[#F1EFF6] rounded-md'>
|
|
253
|
-
<p className='text-[13px] text-blue-700 flex justify-between'>
|
|
254
|
-
<span className='font-medium block'>
|
|
255
|
-
{
|
|
256
|
-
formatMessage({
|
|
257
|
-
id: 'RMA.Status',
|
|
258
|
-
defaultMessage: 'Status Order'
|
|
259
|
-
})
|
|
260
|
-
}
|
|
261
|
-
</span>
|
|
262
|
-
<span className='font-medium block text-blue-700'>
|
|
263
|
-
{order?.status}
|
|
264
|
-
</span>
|
|
265
|
-
</p>
|
|
266
|
-
</div>
|
|
267
|
-
<div className='block'>
|
|
268
|
-
<p className='text-[13px] text-colorDefault flex justify-between'>
|
|
269
|
-
<span className='font-medium block'>
|
|
270
|
-
{formatMessage({ id: 'RMA.RMANumber', defaultMessage: 'RMA' })}
|
|
271
|
-
</span>
|
|
272
|
-
<span className='font-normal block'>
|
|
273
|
-
{order ? order.increment_id || order.number : '-'}
|
|
274
|
-
</span>
|
|
275
|
-
</p>
|
|
276
|
-
</div>
|
|
277
265
|
<div className='block'>
|
|
278
266
|
<p className='text-[13px] text-colorDefault flex justify-between'>
|
|
279
267
|
<span className='font-medium block'>
|
|
@@ -329,8 +317,11 @@ const RMACreate = () => {
|
|
|
329
317
|
</div>
|
|
330
318
|
</div>
|
|
331
319
|
<div className=' flex flex-col'>
|
|
332
|
-
<div aria-live="polite" className="text-lg font-medium text-left mb-
|
|
333
|
-
Items
|
|
320
|
+
<div aria-live="polite" className="text-lg font-medium text-left mb-1">
|
|
321
|
+
Select Items to Return
|
|
322
|
+
</div>
|
|
323
|
+
<div className="text-[13px] text-gray-500 mb-3">
|
|
324
|
+
Please check the items you want to return.
|
|
334
325
|
</div>
|
|
335
326
|
<div className='border-t border-gray-100 py-4 flex flex-col gap-y-4'>
|
|
336
327
|
{order && order.items && order.items.length > 0 ? (
|
|
@@ -351,7 +342,26 @@ const RMACreate = () => {
|
|
|
351
342
|
)}
|
|
352
343
|
</div>
|
|
353
344
|
</div>
|
|
354
|
-
<div className='
|
|
345
|
+
<div className='flex justify-end flex-row mb-4'>
|
|
346
|
+
<Button
|
|
347
|
+
priority='high'
|
|
348
|
+
onClick={handleSubmit}
|
|
349
|
+
disabled={createRmaLoading || !isAllCheckedItemsValid}
|
|
350
|
+
className={cn(
|
|
351
|
+
'px-6 py-2 rounded-full text-white border flex items-center gap-2',
|
|
352
|
+
createRmaLoading || !isAllCheckedItemsValid
|
|
353
|
+
? 'bg-gray-400 border-gray-400 cursor-not-allowed'
|
|
354
|
+
: 'bg-blue-700 border-blue-700 hover_bg-blue-700 hover_border-blue-700',
|
|
355
|
+
'focus-visible_outline-none'
|
|
356
|
+
)}
|
|
357
|
+
>
|
|
358
|
+
{createRmaLoading && (
|
|
359
|
+
<span className="inline-block h-4 w-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
360
|
+
)}
|
|
361
|
+
{createRmaLoading ? 'Submitting...' : 'Submit Return Request'}
|
|
362
|
+
</Button>
|
|
363
|
+
</div>
|
|
364
|
+
{/* <div className=' flex flex-col mb-10'>
|
|
355
365
|
<div aria-live="polite" className="text-lg font-medium text-left mb-4">
|
|
356
366
|
Messages
|
|
357
367
|
</div>
|
|
@@ -381,26 +391,7 @@ const RMACreate = () => {
|
|
|
381
391
|
</Button>
|
|
382
392
|
</div>
|
|
383
393
|
</div>
|
|
384
|
-
|
|
385
|
-
<Button
|
|
386
|
-
priority='high'
|
|
387
|
-
onClick={handleSubmit}
|
|
388
|
-
disabled={createRmaLoading}
|
|
389
|
-
className={cn(
|
|
390
|
-
'px-6 py-2 rounded-full text-white border flex items-center gap-2',
|
|
391
|
-
createRmaLoading
|
|
392
|
-
? 'bg-gray-400 border-gray-400 cursor-not-allowed'
|
|
393
|
-
: 'bg-blue-700 border-blue-700 hover_bg-blue-700 hover_border-blue-700',
|
|
394
|
-
'focus-visible_outline-none'
|
|
395
|
-
)}
|
|
396
|
-
>
|
|
397
|
-
{createRmaLoading && (
|
|
398
|
-
<span className="inline-block h-4 w-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
399
|
-
)}
|
|
400
|
-
{createRmaLoading ? 'Submitting...' : 'Submit RMA Request'}
|
|
401
|
-
</Button>
|
|
402
|
-
</div>
|
|
403
|
-
</div>
|
|
394
|
+
</div> */}
|
|
404
395
|
<style jsx="true">
|
|
405
396
|
{`
|
|
406
397
|
.chat-container::-webkit-scrollbar {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useMemo, useRef, useCallback } from 'react';
|
|
1
|
+
import React, { useMemo, useRef, useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
3
|
import { StoreTitle } from '@magento/venia-ui/lib/components/Head';
|
|
4
4
|
import Image from '@magento/venia-ui/lib/components/Image';
|
|
@@ -8,70 +8,57 @@ import ChatContent from '../LiveChat/chatContent';
|
|
|
8
8
|
import cn from 'classnames';
|
|
9
9
|
import { Send, Printer } from 'iconsax-react';
|
|
10
10
|
import useRmaPage from '@riosst100/pwa-marketplace/src/talons/RMAPage/useRmaPage';
|
|
11
|
+
import { useToasts } from '@magento/peregrine/lib/Toasts';
|
|
12
|
+
import { CheckCircle as CheckCircleIcon } from 'react-feather';
|
|
11
13
|
|
|
12
14
|
const RMADetail = () => {
|
|
13
15
|
const { formatMessage } = useIntl();
|
|
14
16
|
const { urlKey } = useParams();
|
|
15
17
|
const rmaId = urlKey;
|
|
16
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
rmaDetail,
|
|
20
|
+
rmaDetailLoading,
|
|
21
|
+
rmaDetailError,
|
|
22
|
+
confirmShipping,
|
|
23
|
+
confirmShippingLoading,
|
|
24
|
+
confirmShippingError,
|
|
25
|
+
confirmShippingData,
|
|
26
|
+
refetchRmaDetail,
|
|
27
|
+
sendRmaMessage,
|
|
28
|
+
sendRmaMessageLoading,
|
|
29
|
+
sendRmaMessageError,
|
|
30
|
+
sendRmaMessageData
|
|
31
|
+
} = useRmaPage({ rmaId });
|
|
32
|
+
const [, { addToast }] = useToasts();
|
|
33
|
+
// State untuk input chat
|
|
34
|
+
const [chatInput, setChatInput] = useState('');
|
|
35
|
+
const [chatSending, setChatSending] = useState(false);
|
|
36
|
+
// Handler kirim pesan chat
|
|
37
|
+
const handleSendChat = useCallback(async () => {
|
|
38
|
+
if (!chatInput.trim()) return;
|
|
39
|
+
setChatSending(true);
|
|
40
|
+
const input = {
|
|
41
|
+
rma_id: rmaId,
|
|
42
|
+
text: chatInput.trim()
|
|
43
|
+
};
|
|
44
|
+
const { data, error } = await sendRmaMessage(input);
|
|
45
|
+
setChatSending(false);
|
|
46
|
+
if (error || !data?.lofmpSendRmaMessage?.success) {
|
|
47
|
+
addToast({
|
|
48
|
+
type: 'error',
|
|
49
|
+
message: (data?.lofmpSendRmaMessage?.message || error?.message || 'Failed to send message'),
|
|
50
|
+
dismissable: true,
|
|
51
|
+
timeout: 4000
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
setChatInput('');
|
|
56
|
+
if (typeof refetchRmaDetail === 'function') refetchRmaDetail();
|
|
57
|
+
}, [chatInput, rmaId, sendRmaMessage, addToast, refetchRmaDetail]);
|
|
17
58
|
const PAGE_TITLE = formatMessage({
|
|
18
59
|
id: 'Quotes.pageTitleTextRMADetail',
|
|
19
60
|
defaultMessage: 'Return Detail'
|
|
20
61
|
});
|
|
21
|
-
|
|
22
|
-
// Dummy chat data retained for development/demo and as a fallback
|
|
23
|
-
const dummyChat = [
|
|
24
|
-
{
|
|
25
|
-
message: 'May I help you',
|
|
26
|
-
type: 'seller',
|
|
27
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
message: 'Yes',
|
|
31
|
-
type: 'buyer',
|
|
32
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
message: 'can i get a discount',
|
|
36
|
-
type: 'buyer',
|
|
37
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
message: "you can redeem this code 'JunePlay'",
|
|
41
|
-
type: 'seller',
|
|
42
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
message: "or this code 'collectfest'",
|
|
46
|
-
type: 'seller',
|
|
47
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
message: 'May I help you',
|
|
51
|
-
type: 'seller',
|
|
52
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
message: 'Yes',
|
|
56
|
-
type: 'buyer',
|
|
57
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
message: 'can i get a discount',
|
|
61
|
-
type: 'buyer',
|
|
62
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
message: "you can redeem this code 'JunePlay'",
|
|
66
|
-
type: 'seller',
|
|
67
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
message: "or this code 'collectfest'",
|
|
71
|
-
type: 'seller',
|
|
72
|
-
timeStamp: '03-06-2024 2:59 pm'
|
|
73
|
-
}
|
|
74
|
-
];
|
|
75
62
|
|
|
76
63
|
const chatData = useMemo(() => {
|
|
77
64
|
const messages = (rmaDetail && rmaDetail.messages) ? rmaDetail.messages : [];
|
|
@@ -79,7 +66,8 @@ const RMADetail = () => {
|
|
|
79
66
|
const mapped = messages.map(m => ({
|
|
80
67
|
message: m.text,
|
|
81
68
|
type: email && m.sender_email === email ? 'buyer' : 'seller',
|
|
82
|
-
timeStamp: m.created_at
|
|
69
|
+
timeStamp: m.created_at,
|
|
70
|
+
senderName: m.sender_name || ''
|
|
83
71
|
}));
|
|
84
72
|
return mapped.length > 0 ? mapped : dummyChat;
|
|
85
73
|
}, [rmaDetail]);
|
|
@@ -101,7 +89,8 @@ const RMADetail = () => {
|
|
|
101
89
|
orderDate: rmaDetail.order && rmaDetail.order.created_at ? toDate(rmaDetail.order.created_at) : '',
|
|
102
90
|
dateRequested: rmaDetail.rma_date ? toDate(rmaDetail.rma_date) : '',
|
|
103
91
|
shipping: rmaDetail.shipping_address,
|
|
104
|
-
items: rmaDetail.items || []
|
|
92
|
+
items: rmaDetail.items || [],
|
|
93
|
+
rawStatus: rmaDetail.status_label,
|
|
105
94
|
};
|
|
106
95
|
}, [rmaDetail]);
|
|
107
96
|
// Use same in-page print approach as OrderDetail: CSS hides everything except .print-area
|
|
@@ -129,6 +118,25 @@ const RMADetail = () => {
|
|
|
129
118
|
window.print();
|
|
130
119
|
}, []);
|
|
131
120
|
|
|
121
|
+
// Show toast and refetch on confirm shipping success
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (
|
|
124
|
+
confirmShippingData &&
|
|
125
|
+
confirmShippingData.lofmpRmaShippingConfirm &&
|
|
126
|
+
confirmShippingData.lofmpRmaShippingConfirm.success
|
|
127
|
+
) {
|
|
128
|
+
const message = confirmShippingData.lofmpRmaShippingConfirm.message || 'Shipping confirmed!';
|
|
129
|
+
addToast({
|
|
130
|
+
type: 'success',
|
|
131
|
+
icon: <CheckCircleIcon size={18} />,
|
|
132
|
+
message,
|
|
133
|
+
dismissable: true,
|
|
134
|
+
timeout: 4000
|
|
135
|
+
});
|
|
136
|
+
if (typeof refetchRmaDetail === 'function') refetchRmaDetail();
|
|
137
|
+
}
|
|
138
|
+
}, [confirmShippingData, refetchRmaDetail, addToast]);
|
|
139
|
+
|
|
132
140
|
return (
|
|
133
141
|
<div className='print-area relative grid gap-y-md' ref={printRef}>
|
|
134
142
|
<StoreTitle>{PAGE_TITLE}</StoreTitle>
|
|
@@ -136,52 +144,68 @@ const RMADetail = () => {
|
|
|
136
144
|
{PAGE_TITLE} {formatted.increment_id ? `#${formatted.increment_id}` : ''}
|
|
137
145
|
</div>
|
|
138
146
|
<div className='block relative'>
|
|
139
|
-
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
147
|
+
{(formatted.rawStatus === 'Approved' || formatted.rawStatus === 'approved') && (
|
|
148
|
+
<div className='flex flex-col gap-y-2 mb-2 no-print'>
|
|
149
|
+
<div className='flex gap-x-4 justify-end'>
|
|
150
|
+
<Button
|
|
151
|
+
priority='high'
|
|
152
|
+
classes={{
|
|
153
|
+
content: "flex justify-center gap-x-2.5 items-center text-[14px] font-medium capitalize"
|
|
154
|
+
}}
|
|
155
|
+
className={cn(
|
|
156
|
+
"bg-white px-6 py-2 rounded-md text-gray-900 hover_!text-white border border-solid",
|
|
157
|
+
"border-gray-900 focus-visible_outline-none hover_bg-gray-900"
|
|
158
|
+
)}
|
|
159
|
+
onClick={handlePrint}
|
|
160
|
+
>
|
|
161
|
+
<span className='flex gap-x-3 justify-center group-hover_!text-white'>
|
|
162
|
+
<Printer
|
|
163
|
+
size="24"
|
|
164
|
+
className=''
|
|
165
|
+
/>
|
|
166
|
+
{
|
|
167
|
+
formatMessage({
|
|
168
|
+
id: 'RMA.printRMAPackingSlip',
|
|
169
|
+
defaultMessage: 'Print RMA Packing Slip'
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
</span>
|
|
173
|
+
</Button>
|
|
164
174
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
<Button
|
|
176
|
+
priority='high'
|
|
177
|
+
classes={{
|
|
178
|
+
content: "flex justify-center gap-x-2.5 items-center text-[14px] font-medium capitalize"
|
|
179
|
+
}}
|
|
180
|
+
className={cn(
|
|
181
|
+
"bg-blue-600 px-6 py-2 rounded-md text-white border ",
|
|
182
|
+
"border-blue-600 hover_bg-blue-700 hover_border-blue-700 focus-visible_outline-none"
|
|
183
|
+
)}
|
|
184
|
+
onClick={async () => {
|
|
185
|
+
if (!confirmShippingLoading) await confirmShipping(Number(rmaId));
|
|
186
|
+
}}
|
|
187
|
+
disabled={confirmShippingLoading}
|
|
188
|
+
>
|
|
189
|
+
<span className='flex gap-x-3 justify-center'>
|
|
190
|
+
{confirmShippingLoading
|
|
191
|
+
? formatMessage({ id: 'RMA.confirmShippingLoading', defaultMessage: 'Processing...' })
|
|
192
|
+
: formatMessage({ id: 'RMA.confirmShipping', defaultMessage: 'Confirm Shipping' })
|
|
193
|
+
}
|
|
194
|
+
</span>
|
|
195
|
+
</Button>
|
|
196
|
+
</div>
|
|
197
|
+
{confirmShippingError && (
|
|
198
|
+
<div className='text-[13px] text-red-600 text-right'>
|
|
199
|
+
{confirmShippingError.message}
|
|
200
|
+
</div>
|
|
173
201
|
)}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
</span>
|
|
183
|
-
</Button>
|
|
184
|
-
</div>
|
|
202
|
+
{confirmShippingData && confirmShippingData.lofmpRmaShippingConfirm && !confirmShippingData.lofmpRmaShippingConfirm.success && (
|
|
203
|
+
<div className='text-[13px] text-red-600 text-right'>
|
|
204
|
+
{confirmShippingData.lofmpRmaShippingConfirm.message}
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
)}
|
|
185
209
|
<div aria-live="polite" className="text-lg font-medium text-left mb-4">
|
|
186
210
|
{
|
|
187
211
|
formatMessage({
|
|
@@ -267,13 +291,15 @@ const RMADetail = () => {
|
|
|
267
291
|
</p>
|
|
268
292
|
<p className='text-[13px] text-colorDefault whitespace-pre-wrap'>
|
|
269
293
|
{
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
294
|
+
formatted.shipping
|
|
295
|
+
? `${formatted.shipping.firstname || ''} ${formatted.shipping.lastname || ''}\n` +
|
|
296
|
+
`${formatted.shipping.email || ''}\n` +
|
|
297
|
+
`${formatted.shipping.telephone || ''}\n` +
|
|
298
|
+
`${formatted.shipping.street || ''}\n` +
|
|
299
|
+
`${formatted.shipping.city || ''}\n` +
|
|
300
|
+
`${formatted.shipping.region || ''}\n` +
|
|
301
|
+
`${formatted.shipping.country_id || ''}\n`
|
|
302
|
+
: ''
|
|
277
303
|
}
|
|
278
304
|
</p>
|
|
279
305
|
</div>
|
|
@@ -325,6 +351,10 @@ const RMADetail = () => {
|
|
|
325
351
|
<textarea
|
|
326
352
|
className='w-full focus-visible_outline-none border border-gray-100 p-1 rounded-md'
|
|
327
353
|
cols={5}
|
|
354
|
+
value={chatInput}
|
|
355
|
+
onChange={e => setChatInput(e.target.value)}
|
|
356
|
+
placeholder={formatMessage({ id: 'RMA.chatPlaceholder', defaultMessage: 'Type your message...' })}
|
|
357
|
+
disabled={chatSending || sendRmaMessageLoading}
|
|
328
358
|
/>
|
|
329
359
|
<Button
|
|
330
360
|
priority='high'
|
|
@@ -335,11 +365,18 @@ const RMADetail = () => {
|
|
|
335
365
|
"bg-blue-600 px-6 py-2 rounded-full text-white border ",
|
|
336
366
|
"border-blue-600 hover_bg-blue-700 hover_border-blue-700 focus-visible_outline-none"
|
|
337
367
|
)}
|
|
368
|
+
onClick={handleSendChat}
|
|
369
|
+
disabled={chatSending || sendRmaMessageLoading || !chatInput.trim()}
|
|
370
|
+
aria-label={formatMessage({ id: 'RMA.sendMessage', defaultMessage: 'Send Message' })}
|
|
338
371
|
>
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
372
|
+
{chatSending || sendRmaMessageLoading ? (
|
|
373
|
+
<span className="inline-block h-4 w-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
374
|
+
) : (
|
|
375
|
+
<Send
|
|
376
|
+
size="24"
|
|
377
|
+
className='text-white'
|
|
378
|
+
/>
|
|
379
|
+
)}
|
|
343
380
|
</Button>
|
|
344
381
|
</div>
|
|
345
382
|
</div>
|
|
@@ -37,8 +37,17 @@ const productItem = (props) => {
|
|
|
37
37
|
if (onItemCheck) onItemCheck(e.target.checked);
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
// Input change handler with qty_requested validation
|
|
41
42
|
const handleFieldChange = (field, value) => {
|
|
43
|
+
if (field === 'qty_requested' && item && item.quantity_ordered) {
|
|
44
|
+
if (value > item.quantity_ordered) {
|
|
45
|
+
value = item.quantity_ordered;
|
|
46
|
+
}
|
|
47
|
+
if (value < 1) {
|
|
48
|
+
value = 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
42
51
|
if (onItemChange) onItemChange(field, value);
|
|
43
52
|
};
|
|
44
53
|
|
|
@@ -89,43 +98,59 @@ const productItem = (props) => {
|
|
|
89
98
|
<div className='block w-full lg_w-2/5'>
|
|
90
99
|
{!!itemState.checked ? (
|
|
91
100
|
<>
|
|
92
|
-
<div className='flex flex-col mb-2.5'
|
|
101
|
+
<div className='flex flex-col mb-2.5'>
|
|
93
102
|
<p className='mb-1'>
|
|
94
103
|
Qty Return
|
|
95
104
|
</p>
|
|
96
105
|
<TextField
|
|
97
|
-
value={
|
|
98
|
-
|
|
106
|
+
value={
|
|
107
|
+
itemState.qty_requested > item.quantity_ordered
|
|
108
|
+
? item.quantity_ordered
|
|
109
|
+
: itemState.qty_requested || ''
|
|
110
|
+
}
|
|
111
|
+
onChange={e => {
|
|
112
|
+
let val = parseFloat(e.target.value);
|
|
113
|
+
if (isNaN(val)) val = '';
|
|
114
|
+
if (val > item.quantity_ordered) val = item.quantity_ordered;
|
|
115
|
+
if (val < 1 && val !== '') val = 1;
|
|
116
|
+
handleFieldChange('qty_requested', val);
|
|
117
|
+
}}
|
|
99
118
|
type="number"
|
|
100
119
|
min={1}
|
|
101
120
|
max={item.quantity_ordered}
|
|
102
121
|
/>
|
|
103
122
|
</div>
|
|
104
|
-
<div className='flex flex-col mb-2.5'
|
|
123
|
+
<div className='flex flex-col mb-2.5'>
|
|
105
124
|
<p className='mb-1'>
|
|
106
125
|
Return Reason
|
|
107
126
|
</p>
|
|
108
127
|
<Select
|
|
109
128
|
options={reasonOptions}
|
|
110
129
|
className='w-full'
|
|
130
|
+
value={itemState.reason_id !== undefined && itemState.reason_id !== null ? itemState.reason_id : ''}
|
|
131
|
+
onChange={e => handleFieldChange('reason_id', e.target.value)}
|
|
111
132
|
/>
|
|
112
133
|
</div>
|
|
113
|
-
<div className='flex flex-col mb-2.5'
|
|
134
|
+
<div className='flex flex-col mb-2.5'>
|
|
114
135
|
<p className='mb-1'>
|
|
115
136
|
Item Condition
|
|
116
137
|
</p>
|
|
117
138
|
<Select
|
|
118
139
|
options={conditionOptions}
|
|
119
140
|
className='w-full'
|
|
141
|
+
value={itemState.condition_id !== undefined && itemState.condition_id !== null ? itemState.condition_id : ''}
|
|
142
|
+
onChange={e => handleFieldChange('condition_id', e.target.value)}
|
|
120
143
|
/>
|
|
121
144
|
</div>
|
|
122
|
-
<div className='flex flex-col mb-2.5'
|
|
145
|
+
<div className='flex flex-col mb-2.5'>
|
|
123
146
|
<p className='mb-1'>
|
|
124
147
|
Resolution
|
|
125
148
|
</p>
|
|
126
149
|
<Select
|
|
127
150
|
options={resolutionOptions}
|
|
128
151
|
className='w-full'
|
|
152
|
+
value={itemState.resolution_id !== undefined && itemState.resolution_id !== null ? itemState.resolution_id : ''}
|
|
153
|
+
onChange={e => handleFieldChange('resolution_id', e.target.value)}
|
|
129
154
|
/>
|
|
130
155
|
</div>
|
|
131
156
|
</>
|
|
@@ -28,7 +28,6 @@ const RmaRow = props => {
|
|
|
28
28
|
rmaDetailError,
|
|
29
29
|
rmaDetailData
|
|
30
30
|
} = rma;
|
|
31
|
-
|
|
32
31
|
const classes = useStyle(defaultClasses, props.classes);
|
|
33
32
|
// Format date
|
|
34
33
|
const isoFormattedDate = created_at ? created_at.replace(' ', 'T') : '';
|
|
@@ -54,6 +53,14 @@ const RmaRow = props => {
|
|
|
54
53
|
return 'text-blue-700 text-[14px] font-medium py-[5px] border-none';
|
|
55
54
|
}
|
|
56
55
|
|
|
56
|
+
// Inject parent rma_id (atau increment_id) ke setiap item agar key unik
|
|
57
|
+
const itemsWithParentId = Array.isArray(items)
|
|
58
|
+
? items.map(it => ({
|
|
59
|
+
...it,
|
|
60
|
+
__rmaParentId: rma_id || increment_id || ''
|
|
61
|
+
}))
|
|
62
|
+
: [];
|
|
63
|
+
|
|
57
64
|
return (
|
|
58
65
|
<li className={classes.root}>
|
|
59
66
|
<div className='flex flex-col md_flex-row md_items-start justify-between mb-2.5'>
|
|
@@ -65,7 +72,7 @@ const RmaRow = props => {
|
|
|
65
72
|
{sellerName}
|
|
66
73
|
</span>
|
|
67
74
|
<span>|</span>
|
|
68
|
-
<span className="text-blue-700 text-[14px]">#{
|
|
75
|
+
<span className="text-blue-700 text-[14px]">#{increment_id}</span>
|
|
69
76
|
</div>
|
|
70
77
|
</div>
|
|
71
78
|
</div>
|
|
@@ -81,14 +88,15 @@ const RmaRow = props => {
|
|
|
81
88
|
<div className='flex flex-col ml-[14px]'>
|
|
82
89
|
<div className='flex flex-row'>
|
|
83
90
|
<div className={cn('mr-4 flex flex-col gap-8')}>
|
|
84
|
-
{
|
|
91
|
+
{itemsWithParentId.map((it, idx) => {
|
|
85
92
|
const thumbnailProps = {
|
|
86
93
|
alt: it.name,
|
|
87
94
|
classes: { root: classes.thumbnail },
|
|
88
95
|
width: 50
|
|
89
96
|
};
|
|
97
|
+
// Use unique key: parent id + item.id
|
|
90
98
|
return (
|
|
91
|
-
<div key={it.id || idx} className={classes.productImage}>
|
|
99
|
+
<div key={`${it.__rmaParentId}-${it.id || idx}`} className={classes.productImage}>
|
|
92
100
|
{it.image_url ? (
|
|
93
101
|
<img src={it.image_url} alt={it.name} width={70} className={classes.thumbnail} />
|
|
94
102
|
) : (
|
|
@@ -99,20 +107,23 @@ const RmaRow = props => {
|
|
|
99
107
|
})}
|
|
100
108
|
</div>
|
|
101
109
|
<div className='flex flex-col max-w-[375px] gap-8'>
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
{itemsWithParentId.map((it, idx) => {
|
|
111
|
+
const qty = Number.isFinite(it.qty_requested) ? it.qty_requested : '-';
|
|
112
|
+
return (
|
|
113
|
+
<div key={`${it.__rmaParentId}-${it.id || idx}`} className='flex flex-col gap-1 pb-2 last_pb-0'>
|
|
114
|
+
<div className={cn(classes.productName, 'text-[14] font-medium')}>
|
|
115
|
+
<span>{it.name}</span>
|
|
116
|
+
</div>
|
|
117
|
+
<div className="text-[14] text-gray-300">
|
|
118
|
+
<span>Qty Requested : {qty}</span>
|
|
119
|
+
<br />
|
|
120
|
+
<span>
|
|
121
|
+
Price : {it.price ? <Price currencyCode={it.price.currency} value={it.price.value} /> : '-'}
|
|
122
|
+
</span>
|
|
123
|
+
</div>
|
|
113
124
|
</div>
|
|
114
|
-
|
|
115
|
-
)
|
|
125
|
+
);
|
|
126
|
+
})}
|
|
116
127
|
</div>
|
|
117
128
|
</div>
|
|
118
129
|
</div>
|
|
@@ -5,19 +5,23 @@ const Select = (props) => {
|
|
|
5
5
|
const {
|
|
6
6
|
wrapperClassname,
|
|
7
7
|
className,
|
|
8
|
-
options = []
|
|
8
|
+
options = [],
|
|
9
|
+
value,
|
|
10
|
+
onChange
|
|
9
11
|
} = props;
|
|
10
12
|
|
|
11
13
|
return (
|
|
12
14
|
<div className={cx('flex', wrapperClassname)}>
|
|
13
15
|
<select
|
|
14
16
|
className={cx('border rounded-md border-gray-100 p-2 focus-visible_outline-none', className)}
|
|
17
|
+
value={value}
|
|
18
|
+
onChange={onChange}
|
|
15
19
|
>
|
|
16
20
|
<option value=''>
|
|
17
21
|
Choose Option
|
|
18
22
|
</option>
|
|
19
23
|
{options.map((item) => (
|
|
20
|
-
<option value={item.value}>
|
|
24
|
+
<option key={item.value} value={item.value}>
|
|
21
25
|
{item.label}
|
|
22
26
|
</option>
|
|
23
27
|
))}
|
|
@@ -42,6 +42,36 @@ const LofmpRmasFragment = gql`
|
|
|
42
42
|
}
|
|
43
43
|
`;
|
|
44
44
|
|
|
45
|
+
export const LOFMP_SEND_RMA_MESSAGE = gql`
|
|
46
|
+
mutation lofmpSendRmaMessage($input: SendMessageInput!) {
|
|
47
|
+
lofmpSendRmaMessage(input: $input) {
|
|
48
|
+
created_message {
|
|
49
|
+
attachments {
|
|
50
|
+
id
|
|
51
|
+
name
|
|
52
|
+
url
|
|
53
|
+
}
|
|
54
|
+
created_at
|
|
55
|
+
id
|
|
56
|
+
sender_email
|
|
57
|
+
sender_name
|
|
58
|
+
text
|
|
59
|
+
}
|
|
60
|
+
message
|
|
61
|
+
success
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
export const LOFMP_RMA_SHIPPING_CONFIRM = gql`
|
|
67
|
+
mutation lofmpRmaShippingConfirm($rma_id: Int!) {
|
|
68
|
+
lofmpRmaShippingConfirm(rma_id: $rma_id) {
|
|
69
|
+
message
|
|
70
|
+
success
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
`;
|
|
74
|
+
|
|
45
75
|
export const GET_LOFMP_RMA_CONFIGURATIONS = gql`
|
|
46
76
|
query GetLofmpRmaConfigurations {
|
|
47
77
|
lofmpRmaConfigurations {
|
|
@@ -166,5 +196,7 @@ export const GET_LOFMP_RMA_DETAIL = gql`
|
|
|
166
196
|
export default {
|
|
167
197
|
getLofmpRmasQuery: GET_LOFMP_RMAS,
|
|
168
198
|
lofmpCreateRmaMutation: LOFMP_CREATE_RMA,
|
|
169
|
-
getLofmpRmaDetailQuery: GET_LOFMP_RMA_DETAIL
|
|
199
|
+
getLofmpRmaDetailQuery: GET_LOFMP_RMA_DETAIL,
|
|
200
|
+
lofmpRmaShippingConfirmMutation: LOFMP_RMA_SHIPPING_CONFIRM,
|
|
201
|
+
lofmpSendRmaMessageMutation: LOFMP_SEND_RMA_MESSAGE
|
|
170
202
|
};
|
|
@@ -12,7 +12,7 @@ import OrderProgressBar from './orderProgressBar';
|
|
|
12
12
|
import OrderDetails from './OrderDetails';
|
|
13
13
|
import defaultClasses from './orderRow.module.css';
|
|
14
14
|
import cn from 'classnames';
|
|
15
|
-
import { Verify, Shop } from 'iconsax-react';
|
|
15
|
+
import { Verify, Shop, ConvertCard } from 'iconsax-react';
|
|
16
16
|
import PlaceholderImage from '@magento/venia-ui/lib/components/Image/placeholderImage';
|
|
17
17
|
import { Link, useHistory } from 'react-router-dom';
|
|
18
18
|
|
|
@@ -203,30 +203,25 @@ const OrderRow = props => {
|
|
|
203
203
|
</div>
|
|
204
204
|
</div>
|
|
205
205
|
<div className='flex flex-col md_flex-row justify-between mb-2.5'>
|
|
206
|
-
<div className='flex flex-col ml-[5px]'>
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
})}
|
|
226
|
-
</div>
|
|
227
|
-
<div className='flex flex-col max-w-[375px] gap-8'>
|
|
228
|
-
{items && items.length > 0 && items.map((it, idx) => (
|
|
229
|
-
<div key={it.id || idx} className='flex flex-col gap-1 pb-2 last_pb-0'>
|
|
206
|
+
<div className='flex flex-col ml-[5px] gap-y-[20px]'>
|
|
207
|
+
{/* Per-item horizontal flex: image + detail */}
|
|
208
|
+
{items && items.length > 0 && items.map((it, idx) => {
|
|
209
|
+
const { url, label } = getThumbnailForSku(it.product_sku, it.product_name, idx);
|
|
210
|
+
return (
|
|
211
|
+
<div key={it.id || idx} className='flex flex-row gap-4 mb-2'>
|
|
212
|
+
<div className={classes.productImage}>
|
|
213
|
+
{url ? (
|
|
214
|
+
<img
|
|
215
|
+
src={url}
|
|
216
|
+
alt={label}
|
|
217
|
+
width={80}
|
|
218
|
+
className={classes.thumbnail}
|
|
219
|
+
/>
|
|
220
|
+
) : (
|
|
221
|
+
<PlaceholderImage alt={it.product_name} classes={{ root: classes.thumbnail }} width={60} />
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
<div className='flex flex-col gap-1 pb-2 last_pb-0 max-w-[375px]'>
|
|
230
225
|
<div className={cn(classes.productName, 'text-[14] font-medium')}>
|
|
231
226
|
<span>{it.product_name}</span>
|
|
232
227
|
</div>
|
|
@@ -238,9 +233,9 @@ const OrderRow = props => {
|
|
|
238
233
|
</span>
|
|
239
234
|
</div>
|
|
240
235
|
</div>
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
})}
|
|
244
239
|
</div>
|
|
245
240
|
{/* Right column: bottom-aligned Order Total + CTA */}
|
|
246
241
|
<div className='flex flex-col items-end gap-2 md_pl-10 md_self-end mr-4 mb-1'>
|
|
@@ -254,18 +249,6 @@ const OrderRow = props => {
|
|
|
254
249
|
<div className="text-lg font-medium">{orderTotalPrice}</div>
|
|
255
250
|
</div>
|
|
256
251
|
<div className="flex flex-row gap-2 w-full justify-end items-center">
|
|
257
|
-
{showNewReturnButton && (
|
|
258
|
-
<button
|
|
259
|
-
type="button"
|
|
260
|
-
className="bg-blue-700 hover:bg-white hover:text-blue-700 hover:border hover:border-blue-700 rounded-full px-[30px] py-[5px] text-[13px] font-medium text-white transition-all duration-300 ease-in-out"
|
|
261
|
-
onClick={handleNewReturn}
|
|
262
|
-
>
|
|
263
|
-
<FormattedMessage
|
|
264
|
-
id={'orderRow.newReturn'}
|
|
265
|
-
defaultMessage={'New Return'}
|
|
266
|
-
/>
|
|
267
|
-
</button>
|
|
268
|
-
)}
|
|
269
252
|
<Link
|
|
270
253
|
to={{
|
|
271
254
|
pathname: `/order-history/view/${orderNumber}`,
|
|
@@ -279,6 +262,22 @@ const OrderRow = props => {
|
|
|
279
262
|
/>
|
|
280
263
|
</span>
|
|
281
264
|
</Link>
|
|
265
|
+
{showNewReturnButton && (
|
|
266
|
+
<div
|
|
267
|
+
className={cn(
|
|
268
|
+
"cursor-pointer border border-blue-700 bg-white text-blue-700 hover:bg-blue-50 hover:text-blue-700 hover:border-blue-700",
|
|
269
|
+
"rounded-full px-[10px] py-[3px] text-[13px] font-medium transition-all duration-300 ease-in-out flex items-center gap-2"
|
|
270
|
+
)}
|
|
271
|
+
onClick={handleNewReturn}
|
|
272
|
+
title={formatMessage({ id: 'orderRow.returnProduct', defaultMessage: 'Return Product' })}
|
|
273
|
+
>
|
|
274
|
+
<ConvertCard size={20} color="#f26313" />
|
|
275
|
+
<FormattedMessage
|
|
276
|
+
id={'orderRow.ReturnItems'}
|
|
277
|
+
defaultMessage={'Return Items'}
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
)}
|
|
282
281
|
</div>
|
|
283
282
|
</div>
|
|
284
283
|
</div>
|
|
@@ -12,10 +12,27 @@ const PAGE_SIZE = 5;
|
|
|
12
12
|
// Custom RMA Page talon with page-based pagination
|
|
13
13
|
export const useRmaPage = (props = {}) => {
|
|
14
14
|
const { rmaId, orderNumber } = props;
|
|
15
|
-
const { getLofmpRmasQuery, getLofmpRmaDetailQuery, lofmpCreateRmaMutation } = operations;
|
|
15
|
+
const { getLofmpRmasQuery, getLofmpRmaDetailQuery, lofmpCreateRmaMutation, lofmpRmaShippingConfirmMutation, lofmpSendRmaMessageMutation } = operations;
|
|
16
|
+
|
|
17
|
+
// Mutation for sending RMA message
|
|
18
|
+
const [sendRmaMessageMutation, { data: sendRmaMessageData, error: sendRmaMessageError, loading: sendRmaMessageLoading }] = useMutation(lofmpSendRmaMessageMutation);
|
|
19
|
+
|
|
20
|
+
// Handler for sending RMA message
|
|
21
|
+
const sendRmaMessage = useCallback(async (input) => {
|
|
22
|
+
try {
|
|
23
|
+
const { data } = await sendRmaMessageMutation({ variables: { input } });
|
|
24
|
+
return { data, error: null };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return { data: null, error };
|
|
27
|
+
}
|
|
28
|
+
}, [sendRmaMessageMutation]);
|
|
29
|
+
|
|
16
30
|
// Mutation for creating RMA
|
|
17
31
|
const [createRmaMutation, { data: createRmaData, error: createRmaError, loading: createRmaLoading }] = useMutation(lofmpCreateRmaMutation);
|
|
18
32
|
|
|
33
|
+
// Mutation for confirming shipping
|
|
34
|
+
const [confirmShippingMutation, { data: confirmShippingData, error: confirmShippingError, loading: confirmShippingLoading }] = useMutation(lofmpRmaShippingConfirmMutation);
|
|
35
|
+
|
|
19
36
|
// Handler for creating RMA
|
|
20
37
|
const createRma = useCallback(async (input) => {
|
|
21
38
|
try {
|
|
@@ -26,6 +43,16 @@ export const useRmaPage = (props = {}) => {
|
|
|
26
43
|
}
|
|
27
44
|
}, [createRmaMutation]);
|
|
28
45
|
|
|
46
|
+
// Handler for confirming shipping
|
|
47
|
+
const confirmShipping = useCallback(async (rma_id) => {
|
|
48
|
+
try {
|
|
49
|
+
const { data } = await confirmShippingMutation({ variables: { rma_id } });
|
|
50
|
+
return { data, error: null };
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return { data: null, error };
|
|
53
|
+
}
|
|
54
|
+
}, [confirmShippingMutation]);
|
|
55
|
+
|
|
29
56
|
const [
|
|
30
57
|
,
|
|
31
58
|
{
|
|
@@ -47,7 +74,7 @@ export const useRmaPage = (props = {}) => {
|
|
|
47
74
|
variables: {
|
|
48
75
|
filter: orderNumber
|
|
49
76
|
? { order_number: orderNumber }
|
|
50
|
-
: (searchText ? {
|
|
77
|
+
: (searchText ? { order_number: searchText } : undefined),
|
|
51
78
|
pageSize,
|
|
52
79
|
currentPage
|
|
53
80
|
},
|
|
@@ -58,11 +85,13 @@ export const useRmaPage = (props = {}) => {
|
|
|
58
85
|
const {
|
|
59
86
|
data: rmaDetailData,
|
|
60
87
|
error: rmaDetailError,
|
|
61
|
-
loading: rmaDetailLoading
|
|
88
|
+
loading: rmaDetailLoading,
|
|
89
|
+
refetch: refetchRmaDetail
|
|
62
90
|
} = useQuery(getLofmpRmaDetailQuery, {
|
|
63
91
|
fetchPolicy: 'cache-and-network',
|
|
64
92
|
variables: { rmaId },
|
|
65
|
-
skip: !rmaId
|
|
93
|
+
skip: !rmaId,
|
|
94
|
+
pollInterval: 3000 // 1 detik auto-refresh
|
|
66
95
|
});
|
|
67
96
|
|
|
68
97
|
const rmasData = data && data.lofmpRmas ? data.lofmpRmas : undefined;
|
|
@@ -135,10 +164,15 @@ export const useRmaPage = (props = {}) => {
|
|
|
135
164
|
createRmaLoading,
|
|
136
165
|
createRmaError,
|
|
137
166
|
createRmaData,
|
|
138
|
-
|
|
167
|
+
confirmShipping,
|
|
168
|
+
confirmShippingLoading,
|
|
169
|
+
confirmShippingError,
|
|
170
|
+
confirmShippingData,
|
|
139
171
|
rmaDetail: rmaDetailData ? rmaDetailData.lofmpRmaDetail : undefined,
|
|
140
172
|
rmaDetailLoading,
|
|
141
|
-
rmaDetailError
|
|
173
|
+
rmaDetailError,
|
|
174
|
+
refetchRmaDetail
|
|
175
|
+
,sendRmaMessage, sendRmaMessageData, sendRmaMessageError, sendRmaMessageLoading
|
|
142
176
|
};
|
|
143
177
|
};
|
|
144
178
|
|