strapi-plugin-payone-provider 1.6.6 → 4.6.9
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/.well-known/.gitkeep +3 -0
- package/.well-known/apple-developer-merchant-id-domain-association.txt +1 -0
- package/admin/src/pages/App/components/AppTabs.jsx +37 -16
- package/admin/src/pages/App/components/ApplePayBtn.jsx +63 -28
- package/admin/src/pages/App/components/ConfigurationPanel.jsx +163 -10
- package/admin/src/pages/App/components/CustomerInfoPopover.jsx +147 -0
- package/admin/src/pages/App/components/DocsPanel.jsx +864 -194
- package/admin/src/pages/App/components/HistoryPanel.jsx +24 -237
- package/admin/src/pages/App/components/PaymentActionsPanel.jsx +8 -1
- package/admin/src/pages/App/components/RawDataPopover.jsx +113 -0
- package/admin/src/pages/App/components/StatusBadge.jsx +72 -14
- package/admin/src/pages/App/components/TransactionHistoryTable/TransactionHistoryTableFilters.jsx +113 -0
- package/admin/src/pages/App/components/TransactionHistoryTable/TransactionHistoryTablePagination.jsx +180 -0
- package/admin/src/pages/App/components/TransactionHistoryTable/index.jsx +225 -0
- package/admin/src/pages/App/components/paymentActions/ApplePayPanel.jsx +45 -1
- package/admin/src/pages/App/index.jsx +4 -0
- package/admin/src/pages/hooks/useSettings.js +26 -0
- package/admin/src/pages/hooks/useTransactionHistory.js +75 -11
- package/package.json +3 -2
- package/server/bootstrap.js +9 -3
- package/server/controllers/payone.js +8 -0
- package/server/services/applePayService.js +103 -93
- package/server/services/transactionService.js +104 -19
- package/server/utils/paymentMethodParams.js +67 -18
|
@@ -8,15 +8,20 @@ import {
|
|
|
8
8
|
Stack,
|
|
9
9
|
Typography,
|
|
10
10
|
TextInput,
|
|
11
|
+
Select,
|
|
12
|
+
Option,
|
|
11
13
|
Divider,
|
|
12
14
|
} from "@strapi/design-system";
|
|
13
15
|
import { Search } from "@strapi/icons";
|
|
14
16
|
import TransactionHistoryItem from "./TransactionHistoryItem";
|
|
17
|
+
import TransactionHistoryTable from "./TransactionHistoryTable";
|
|
15
18
|
|
|
16
19
|
const HistoryPanel = ({
|
|
17
20
|
filters,
|
|
18
21
|
onFilterChange,
|
|
19
22
|
onFilterApply,
|
|
23
|
+
sorting,
|
|
24
|
+
onSort,
|
|
20
25
|
isLoadingHistory,
|
|
21
26
|
transactionHistory,
|
|
22
27
|
paginatedTransactions,
|
|
@@ -25,7 +30,10 @@ const HistoryPanel = ({
|
|
|
25
30
|
pageSize,
|
|
26
31
|
onRefresh,
|
|
27
32
|
onPageChange,
|
|
33
|
+
onPageSizeChange,
|
|
28
34
|
}) => {
|
|
35
|
+
console.log(transactionHistory);
|
|
36
|
+
|
|
29
37
|
return (
|
|
30
38
|
<Box
|
|
31
39
|
className="payment-container"
|
|
@@ -53,244 +61,23 @@ const HistoryPanel = ({
|
|
|
53
61
|
View and filter all payment transactions processed through Payone
|
|
54
62
|
</Typography>
|
|
55
63
|
</Box>
|
|
56
|
-
{/* Filters */}
|
|
57
|
-
<Box>
|
|
58
|
-
<Box marginBottom={4}>
|
|
59
|
-
<Typography variant="delta" as="h3" fontWeight="bold">
|
|
60
|
-
Transaction Filters
|
|
61
|
-
</Typography>
|
|
62
|
-
<Typography variant="pi" textColor="neutral600" marginTop={2}>
|
|
63
|
-
Filter transactions by status, type, date range, and more
|
|
64
|
-
</Typography>
|
|
65
|
-
</Box>
|
|
66
|
-
<Card className="payment-card">
|
|
67
|
-
<CardBody padding={6}>
|
|
68
|
-
<Stack spacing={4}>
|
|
69
|
-
<Flex gap={4} wrap="wrap" alignItems="center">
|
|
70
|
-
<TextInput
|
|
71
|
-
label="Status"
|
|
72
|
-
name="status"
|
|
73
|
-
value={filters.status}
|
|
74
|
-
onChange={(e) => onFilterChange("status", e.target.value)}
|
|
75
|
-
placeholder="APPROVED, ERROR, etc."
|
|
76
|
-
className="payment-input"
|
|
77
|
-
style={{ flex: 1, minWidth: "200px" }}
|
|
78
|
-
/>
|
|
79
|
-
<TextInput
|
|
80
|
-
label="Request Type"
|
|
81
|
-
name="request_type"
|
|
82
|
-
value={filters.request_type}
|
|
83
|
-
onChange={(e) =>
|
|
84
|
-
onFilterChange("request_type", e.target.value)
|
|
85
|
-
}
|
|
86
|
-
placeholder="preauthorization, authorization, etc."
|
|
87
|
-
className="payment-input"
|
|
88
|
-
style={{ flex: 1, minWidth: "200px" }}
|
|
89
|
-
/>
|
|
90
|
-
<TextInput
|
|
91
|
-
label="Transaction ID"
|
|
92
|
-
name="txid"
|
|
93
|
-
value={filters.txid}
|
|
94
|
-
onChange={(e) => onFilterChange("txid", e.target.value)}
|
|
95
|
-
placeholder="Enter TxId"
|
|
96
|
-
className="payment-input"
|
|
97
|
-
style={{ flex: 1, minWidth: "200px" }}
|
|
98
|
-
/>
|
|
99
|
-
<TextInput
|
|
100
|
-
label="Reference"
|
|
101
|
-
name="reference"
|
|
102
|
-
value={filters.reference}
|
|
103
|
-
onChange={(e) =>
|
|
104
|
-
onFilterChange("reference", e.target.value)
|
|
105
|
-
}
|
|
106
|
-
placeholder="Enter reference"
|
|
107
|
-
className="payment-input"
|
|
108
|
-
style={{ flex: 1, minWidth: "200px" }}
|
|
109
|
-
/>
|
|
110
|
-
<TextInput
|
|
111
|
-
label="Date From"
|
|
112
|
-
name="date_from"
|
|
113
|
-
value={filters.date_from}
|
|
114
|
-
onChange={(e) =>
|
|
115
|
-
onFilterChange("date_from", e.target.value)
|
|
116
|
-
}
|
|
117
|
-
placeholder="YYYY-MM-DD"
|
|
118
|
-
type="date"
|
|
119
|
-
className="payment-input"
|
|
120
|
-
style={{ flex: 1, minWidth: "200px" }}
|
|
121
|
-
/>
|
|
122
|
-
<TextInput
|
|
123
|
-
label="Date To"
|
|
124
|
-
name="date_to"
|
|
125
|
-
value={filters.date_to}
|
|
126
|
-
onChange={(e) => onFilterChange("date_to", e.target.value)}
|
|
127
|
-
placeholder="YYYY-MM-DD"
|
|
128
|
-
type="date"
|
|
129
|
-
className="payment-input"
|
|
130
|
-
style={{ flex: 1, minWidth: "200px" }}
|
|
131
|
-
/>
|
|
132
|
-
<Button
|
|
133
|
-
variant="default"
|
|
134
|
-
onClick={onFilterApply}
|
|
135
|
-
loading={isLoadingHistory}
|
|
136
|
-
startIcon={<Search />}
|
|
137
|
-
className="payment-button payment-button-primary"
|
|
138
|
-
>
|
|
139
|
-
Apply Filters
|
|
140
|
-
</Button>
|
|
141
|
-
</Flex>
|
|
142
|
-
</Stack>
|
|
143
|
-
</CardBody>
|
|
144
|
-
</Card>
|
|
145
|
-
</Box>
|
|
146
|
-
|
|
147
|
-
<Divider />
|
|
148
|
-
|
|
149
|
-
{/* Transaction History */}
|
|
150
|
-
<Box>
|
|
151
|
-
<Box marginBottom={6}>
|
|
152
|
-
<Flex
|
|
153
|
-
justifyContent="space-between"
|
|
154
|
-
alignItems="center"
|
|
155
|
-
marginBottom={4}
|
|
156
|
-
>
|
|
157
|
-
<Box>
|
|
158
|
-
<Typography variant="delta" as="h3" fontWeight="bold">
|
|
159
|
-
Transaction History
|
|
160
|
-
</Typography>
|
|
161
|
-
<Typography variant="pi" textColor="neutral600" marginTop={2}>
|
|
162
|
-
{transactionHistory.length} total transactions •{" "}
|
|
163
|
-
{paginatedTransactions.length} on page {currentPage} of{" "}
|
|
164
|
-
{totalPages}
|
|
165
|
-
</Typography>
|
|
166
|
-
</Box>
|
|
167
|
-
<Button
|
|
168
|
-
variant="default"
|
|
169
|
-
onClick={onRefresh}
|
|
170
|
-
loading={isLoadingHistory}
|
|
171
|
-
startIcon={<Search />}
|
|
172
|
-
size="S"
|
|
173
|
-
className="payment-button payment-button-success"
|
|
174
|
-
>
|
|
175
|
-
Refresh
|
|
176
|
-
</Button>
|
|
177
|
-
</Flex>
|
|
178
|
-
</Box>
|
|
179
|
-
|
|
180
|
-
{isLoadingHistory ? (
|
|
181
|
-
<Box padding={4} textAlign="center">
|
|
182
|
-
<Typography>Loading transactions...</Typography>
|
|
183
|
-
</Box>
|
|
184
|
-
) : transactionHistory.length === 0 ? (
|
|
185
|
-
<Box padding={4} textAlign="center">
|
|
186
|
-
<Typography textColor="neutral600">
|
|
187
|
-
No transactions found
|
|
188
|
-
</Typography>
|
|
189
|
-
</Box>
|
|
190
|
-
) : (
|
|
191
|
-
<Box>
|
|
192
|
-
{paginatedTransactions.map((transaction) => (
|
|
193
|
-
<TransactionHistoryItem
|
|
194
|
-
key={transaction.id}
|
|
195
|
-
transaction={transaction}
|
|
196
|
-
/>
|
|
197
|
-
))}
|
|
198
|
-
|
|
199
|
-
{/* Pagination */}
|
|
200
|
-
<Box paddingTop={6} paddingBottom={4}>
|
|
201
|
-
<Card className="payment-card">
|
|
202
|
-
<CardBody padding={4}>
|
|
203
|
-
<Flex justifyContent="space-between" alignItems="center">
|
|
204
|
-
{transactionHistory.length > pageSize &&
|
|
205
|
-
totalPages > 1 ? (
|
|
206
|
-
<Flex gap={3} alignItems="center">
|
|
207
|
-
<Button
|
|
208
|
-
variant="default"
|
|
209
|
-
size="S"
|
|
210
|
-
onClick={() =>
|
|
211
|
-
onPageChange(Math.max(1, currentPage - 1))
|
|
212
|
-
}
|
|
213
|
-
disabled={currentPage === 1}
|
|
214
|
-
className={`payment-button ${
|
|
215
|
-
currentPage === 1 ? "" : "payment-button-success"
|
|
216
|
-
}`}
|
|
217
|
-
style={{
|
|
218
|
-
background:
|
|
219
|
-
currentPage === 1 ? "#f6f6f9" : undefined,
|
|
220
|
-
color: currentPage === 1 ? "#666687" : undefined,
|
|
221
|
-
}}
|
|
222
|
-
>
|
|
223
|
-
← Previous
|
|
224
|
-
</Button>
|
|
225
64
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
size="S"
|
|
243
|
-
onClick={() =>
|
|
244
|
-
onPageChange(
|
|
245
|
-
Math.min(totalPages, currentPage + 1)
|
|
246
|
-
)
|
|
247
|
-
}
|
|
248
|
-
disabled={currentPage === totalPages}
|
|
249
|
-
className={`payment-button ${
|
|
250
|
-
currentPage === totalPages
|
|
251
|
-
? ""
|
|
252
|
-
: "payment-button-success"
|
|
253
|
-
}`}
|
|
254
|
-
style={{
|
|
255
|
-
background:
|
|
256
|
-
currentPage === totalPages
|
|
257
|
-
? "#f6f6f9"
|
|
258
|
-
: undefined,
|
|
259
|
-
color:
|
|
260
|
-
currentPage === totalPages
|
|
261
|
-
? "#666687"
|
|
262
|
-
: undefined,
|
|
263
|
-
}}
|
|
264
|
-
>
|
|
265
|
-
Next →
|
|
266
|
-
</Button>
|
|
267
|
-
</Flex>
|
|
268
|
-
) : (
|
|
269
|
-
<Typography
|
|
270
|
-
variant="pi"
|
|
271
|
-
textColor="neutral600"
|
|
272
|
-
fontWeight="medium"
|
|
273
|
-
>
|
|
274
|
-
{transactionHistory.length <= pageSize
|
|
275
|
-
? "All transactions shown"
|
|
276
|
-
: "No pagination needed"}
|
|
277
|
-
</Typography>
|
|
278
|
-
)}
|
|
279
|
-
</Flex>
|
|
280
|
-
</CardBody>
|
|
281
|
-
</Card>
|
|
282
|
-
<Typography
|
|
283
|
-
variant="pi"
|
|
284
|
-
textColor="neutral600"
|
|
285
|
-
fontWeight="medium"
|
|
286
|
-
>
|
|
287
|
-
Showing {paginatedTransactions.length} of{" "}
|
|
288
|
-
{transactionHistory.length} transactions
|
|
289
|
-
</Typography>
|
|
290
|
-
</Box>
|
|
291
|
-
</Box>
|
|
292
|
-
)}
|
|
293
|
-
</Box>
|
|
65
|
+
{/* Transaction Table */}
|
|
66
|
+
<TransactionHistoryTable
|
|
67
|
+
transactions={paginatedTransactions}
|
|
68
|
+
isLoading={isLoadingHistory}
|
|
69
|
+
filters={filters}
|
|
70
|
+
onFilterChange={onFilterChange}
|
|
71
|
+
onFilterApply={onFilterApply}
|
|
72
|
+
sorting={sorting}
|
|
73
|
+
onSort={onSort}
|
|
74
|
+
currentPage={currentPage}
|
|
75
|
+
totalPages={totalPages}
|
|
76
|
+
pageSize={pageSize}
|
|
77
|
+
totalItems={transactionHistory.length}
|
|
78
|
+
onPageChange={onPageChange}
|
|
79
|
+
onPageSizeChange={onPageSizeChange}
|
|
80
|
+
/>
|
|
294
81
|
|
|
295
82
|
<Box paddingTop={4}>
|
|
296
83
|
<Typography variant="sigma" textColor="neutral600">
|
|
@@ -51,7 +51,13 @@ const PaymentActionsPanel = ({
|
|
|
51
51
|
const mode = (settings?.mode || "test").toLowerCase();
|
|
52
52
|
const isLiveMode = mode === "live";
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
React.useEffect(() => {
|
|
55
|
+
if (isLiveMode && paymentMethod !== "apl") {
|
|
56
|
+
setPaymentMethod("apl");
|
|
57
|
+
}
|
|
58
|
+
}, [isLiveMode, paymentMethod]);
|
|
59
|
+
|
|
60
|
+
if (isLiveMode && paymentMethod !== "apl") {
|
|
55
61
|
return (
|
|
56
62
|
<Box
|
|
57
63
|
style={{
|
|
@@ -99,6 +105,7 @@ const PaymentActionsPanel = ({
|
|
|
99
105
|
cardcvc2={cardcvc2}
|
|
100
106
|
setCardcvc2={setCardcvc2}
|
|
101
107
|
onNavigateToConfig={onNavigateToConfig}
|
|
108
|
+
isLiveMode={isLiveMode}
|
|
102
109
|
/>
|
|
103
110
|
);
|
|
104
111
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
2
|
+
import { Box, Typography } from "@strapi/design-system";
|
|
3
|
+
import JsonView from "@uiw/react-json-view";
|
|
4
|
+
|
|
5
|
+
const RawDataPopover = ({ transaction, children }) => {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
const [position, setPosition] = useState("top"); // 'top' or 'bottom'
|
|
8
|
+
const popoverRef = useRef(null);
|
|
9
|
+
const buttonRef = useRef(null);
|
|
10
|
+
|
|
11
|
+
const calculatePosition = (buttonElement) => {
|
|
12
|
+
if (!buttonElement) return "top";
|
|
13
|
+
|
|
14
|
+
const buttonRect = buttonElement.getBoundingClientRect();
|
|
15
|
+
// Find the scrollable parent container
|
|
16
|
+
let scrollableParent = buttonElement.parentElement;
|
|
17
|
+
while (scrollableParent && scrollableParent !== document.body) {
|
|
18
|
+
const style = window.getComputedStyle(scrollableParent);
|
|
19
|
+
if (style.overflow === 'auto' || style.overflowY === 'auto' || style.overflow === 'scroll' || style.overflowY === 'scroll') {
|
|
20
|
+
const parentRect = scrollableParent.getBoundingClientRect();
|
|
21
|
+
const spaceAbove = buttonRect.top - parentRect.top;
|
|
22
|
+
const spaceBelow = parentRect.bottom - parentRect.bottom;
|
|
23
|
+
// RawDataPopover can be up to 500px tall, so we need more space
|
|
24
|
+
// If there's less space above than below (or less than 520px), show popover below
|
|
25
|
+
return spaceAbove < 520 || spaceAbove < spaceBelow ? "bottom" : "top";
|
|
26
|
+
}
|
|
27
|
+
scrollableParent = scrollableParent.parentElement;
|
|
28
|
+
}
|
|
29
|
+
// Fallback: use viewport space
|
|
30
|
+
const spaceAbove = buttonRect.top;
|
|
31
|
+
return spaceAbove < 520 ? "bottom" : "top";
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleButtonClick = () => {
|
|
35
|
+
if (!isOpen && buttonRef.current) {
|
|
36
|
+
// Calculate position before opening
|
|
37
|
+
const newPosition = calculatePosition(buttonRef.current);
|
|
38
|
+
setPosition(newPosition);
|
|
39
|
+
}
|
|
40
|
+
setIsOpen(!isOpen);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const handleClickOutside = (event) => {
|
|
45
|
+
if (
|
|
46
|
+
popoverRef.current &&
|
|
47
|
+
buttonRef.current &&
|
|
48
|
+
!popoverRef.current.contains(event.target) &&
|
|
49
|
+
!buttonRef.current.contains(event.target)
|
|
50
|
+
) {
|
|
51
|
+
setIsOpen(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (isOpen) {
|
|
56
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
57
|
+
return () => {
|
|
58
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}, [isOpen]);
|
|
62
|
+
|
|
63
|
+
if (!transaction) {
|
|
64
|
+
return children;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Box position="relative" style={{ display: "inline-block" }}>
|
|
69
|
+
<Box
|
|
70
|
+
ref={buttonRef}
|
|
71
|
+
onClick={handleButtonClick}
|
|
72
|
+
style={{ display: "inline-block" }}
|
|
73
|
+
>
|
|
74
|
+
{children}
|
|
75
|
+
</Box>
|
|
76
|
+
{isOpen && (
|
|
77
|
+
<Box
|
|
78
|
+
ref={popoverRef}
|
|
79
|
+
position="absolute"
|
|
80
|
+
zIndex={1000}
|
|
81
|
+
left={0}
|
|
82
|
+
{...(position === "top"
|
|
83
|
+
? { bottom: "100%", marginBottom: 2 }
|
|
84
|
+
: { top: "100%", marginTop: 2 }
|
|
85
|
+
)}
|
|
86
|
+
padding={3}
|
|
87
|
+
background="neutral0"
|
|
88
|
+
hasRadius
|
|
89
|
+
style={{
|
|
90
|
+
minWidth: "400px",
|
|
91
|
+
maxWidth: "600px",
|
|
92
|
+
maxHeight: "500px",
|
|
93
|
+
overflow: "auto",
|
|
94
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
95
|
+
border: "1px solid var(--strapi-colors-neutral200)",
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
<Box paddingBottom={2}>
|
|
99
|
+
<Typography variant="pi" fontWeight="bold" textColor="neutral800">
|
|
100
|
+
Raw Transaction Data
|
|
101
|
+
</Typography>
|
|
102
|
+
</Box>
|
|
103
|
+
<Box>
|
|
104
|
+
<JsonView value={transaction} style={{ fontSize: "12px" }} />
|
|
105
|
+
</Box>
|
|
106
|
+
</Box>
|
|
107
|
+
)}
|
|
108
|
+
</Box>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export default RawDataPopover;
|
|
113
|
+
|
|
@@ -1,23 +1,81 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Badge } from "@strapi/design-system";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Badge, Box, Typography, Flex } from "@strapi/design-system";
|
|
3
|
+
import { ExclamationMarkCircle } from "@strapi/icons";
|
|
3
4
|
|
|
4
|
-
const StatusBadge = ({ status }) => {
|
|
5
|
+
const StatusBadge = ({ status, transaction }) => {
|
|
6
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
7
|
+
|
|
5
8
|
const statusColors = {
|
|
6
|
-
APPROVED: "
|
|
7
|
-
PENDING: "
|
|
8
|
-
ERROR: "
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
APPROVED: "success200",
|
|
10
|
+
PENDING: "warning200",
|
|
11
|
+
ERROR: "danger200",
|
|
12
|
+
CANCELLED: "warning100",
|
|
13
|
+
REDIRECTED: "success100",
|
|
14
|
+
CREATED: "success100"
|
|
12
15
|
};
|
|
13
16
|
|
|
17
|
+
const getDisplayText = () => {
|
|
18
|
+
if (status === "ERROR" && transaction?.raw_response?.Error?.ErrorCode) {
|
|
19
|
+
return `${status} - ${transaction.raw_response.Error.ErrorCode}`;
|
|
20
|
+
}
|
|
21
|
+
return status;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const displayText = getDisplayText();
|
|
25
|
+
const errorMessage = status === "ERROR" && transaction?.raw_response?.Error?.ErrorMessage
|
|
26
|
+
? transaction.raw_response.Error.ErrorMessage
|
|
27
|
+
: null;
|
|
28
|
+
|
|
29
|
+
const errorCode = status === "ERROR" && transaction?.raw_response?.Error?.ErrorCode
|
|
30
|
+
? transaction.raw_response.Error.ErrorCode
|
|
31
|
+
: null;
|
|
32
|
+
|
|
33
|
+
const showExclamationIcon = status === "ERROR" && !errorCode && !errorMessage;
|
|
34
|
+
|
|
14
35
|
return (
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
36
|
+
<Box
|
|
37
|
+
position="relative"
|
|
38
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
39
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
40
|
+
style={{ display: "inline-block", cursor: status === "ERROR" ? "pointer" : "default" }}
|
|
18
41
|
>
|
|
19
|
-
{
|
|
20
|
-
|
|
42
|
+
<Flex gap={2} alignItems="center">
|
|
43
|
+
<Badge backgroundColor={statusColors[status] || "warning100"}>
|
|
44
|
+
{displayText}
|
|
45
|
+
</Badge>
|
|
46
|
+
{showExclamationIcon && (
|
|
47
|
+
<ExclamationMarkCircle color="danger500"
|
|
48
|
+
style={{
|
|
49
|
+
width: "16px",
|
|
50
|
+
height: "16px"
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
)}
|
|
54
|
+
</Flex>
|
|
55
|
+
{isHovered && errorMessage && (
|
|
56
|
+
<Box
|
|
57
|
+
position="absolute"
|
|
58
|
+
zIndex={1000}
|
|
59
|
+
bottom="100%"
|
|
60
|
+
left="50%"
|
|
61
|
+
transform="translateX(-50%)"
|
|
62
|
+
marginBottom={2}
|
|
63
|
+
padding={3}
|
|
64
|
+
background="neutral900"
|
|
65
|
+
hasRadius
|
|
66
|
+
style={{
|
|
67
|
+
whiteSpace: "pre-line",
|
|
68
|
+
minWidth: "200px",
|
|
69
|
+
maxWidth: "300px",
|
|
70
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<Typography variant="pi" textColor="neutral0" style={{ fontSize: "12px" }}>
|
|
74
|
+
Error: {errorMessage}
|
|
75
|
+
</Typography>
|
|
76
|
+
</Box>
|
|
77
|
+
)}
|
|
78
|
+
</Box>
|
|
21
79
|
);
|
|
22
80
|
};
|
|
23
81
|
|
package/admin/src/pages/App/components/TransactionHistoryTable/TransactionHistoryTableFilters.jsx
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Flex,
|
|
5
|
+
Select,
|
|
6
|
+
Option,
|
|
7
|
+
Button,
|
|
8
|
+
TextInput,
|
|
9
|
+
Typography,
|
|
10
|
+
} from "@strapi/design-system";
|
|
11
|
+
import { Search } from "@strapi/icons";
|
|
12
|
+
|
|
13
|
+
const TransactionHistoryTableFilters = ({
|
|
14
|
+
filters,
|
|
15
|
+
onFilterChange,
|
|
16
|
+
onFilterApply,
|
|
17
|
+
isLoading,
|
|
18
|
+
}) => {
|
|
19
|
+
const handleDateFromClick = (e) => {
|
|
20
|
+
const input = e.target.closest('div')?.querySelector('input[type="date"]');
|
|
21
|
+
if (input) {
|
|
22
|
+
input.showPicker?.();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const handleDateToClick = (e) => {
|
|
27
|
+
const input = e.target.closest('div')?.querySelector('input[type="date"]');
|
|
28
|
+
if (input) {
|
|
29
|
+
input.showPicker?.();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return (
|
|
33
|
+
<Box marginBottom={2}>
|
|
34
|
+
<Flex gap={3} marginBottom={3} alignItems="center">
|
|
35
|
+
<TextInput
|
|
36
|
+
label="Search"
|
|
37
|
+
name="search"
|
|
38
|
+
value={filters?.search || ""}
|
|
39
|
+
onChange={(e) => onFilterChange("search", e.target.value)}
|
|
40
|
+
placeholder="Search by Status, Transaction ID, or Reference"
|
|
41
|
+
style={{ flex: 1, minWidth: "250px" }}
|
|
42
|
+
/>
|
|
43
|
+
<Select
|
|
44
|
+
label="Status"
|
|
45
|
+
name="status"
|
|
46
|
+
value={filters?.status || ""}
|
|
47
|
+
onChange={(value) => onFilterChange("status", value)}
|
|
48
|
+
placeholder="All Statuses"
|
|
49
|
+
style={{ width: "180px", minWidth: "180px" }}
|
|
50
|
+
>
|
|
51
|
+
<Option value="">All Statuses</Option>
|
|
52
|
+
<Option value="APPROVED">APPROVED</Option>
|
|
53
|
+
<Option value="PENDING">PENDING</Option>
|
|
54
|
+
<Option value="ERROR">ERROR</Option>
|
|
55
|
+
<Option value="CANCELLED">CANCELLED</Option>
|
|
56
|
+
<Option value="REDIRECTED">REDIRECTED</Option>
|
|
57
|
+
<Option value="CREATED">CREATED</Option>
|
|
58
|
+
</Select>
|
|
59
|
+
<Select
|
|
60
|
+
label="Request Type"
|
|
61
|
+
name="request_type"
|
|
62
|
+
value={filters?.request_type || ""}
|
|
63
|
+
onChange={(value) => onFilterChange("request_type", value)}
|
|
64
|
+
placeholder="Select request type"
|
|
65
|
+
style={{ width: "220px", minWidth: "220px" }}
|
|
66
|
+
>
|
|
67
|
+
<Option value="">All Types</Option>
|
|
68
|
+
<Option value="preauthorization">Preauthorization</Option>
|
|
69
|
+
<Option value="authorization">Authorization</Option>
|
|
70
|
+
<Option value="capture">Capture</Option>
|
|
71
|
+
<Option value="refund">Refund</Option>
|
|
72
|
+
</Select>
|
|
73
|
+
</Flex>
|
|
74
|
+
<Flex gap={3} marginBottom={3} alignItems="center">
|
|
75
|
+
<Box onClick={handleDateFromClick} style={{ cursor: "pointer" }}>
|
|
76
|
+
<TextInput
|
|
77
|
+
label="Date From"
|
|
78
|
+
name="date_from"
|
|
79
|
+
value={filters?.date_from || ""}
|
|
80
|
+
onChange={(e) => onFilterChange("date_from", e.target.value)}
|
|
81
|
+
placeholder="YYYY-MM-DD"
|
|
82
|
+
type="date"
|
|
83
|
+
style={{ minWidth: "150px" }}
|
|
84
|
+
/>
|
|
85
|
+
</Box>
|
|
86
|
+
<Box onClick={handleDateToClick} style={{ cursor: "pointer" }}>
|
|
87
|
+
<TextInput
|
|
88
|
+
label="Date To"
|
|
89
|
+
name="date_to"
|
|
90
|
+
value={filters?.date_to || ""}
|
|
91
|
+
onChange={(e) => onFilterChange("date_to", e.target.value)}
|
|
92
|
+
placeholder="YYYY-MM-DD"
|
|
93
|
+
type="date"
|
|
94
|
+
style={{ minWidth: "150px" }}
|
|
95
|
+
/>
|
|
96
|
+
</Box>
|
|
97
|
+
<Typography variant="pi" textColor="neutral600" style={{ fontSize: "12px", marginTop: "20px" }}>
|
|
98
|
+
By default, the last 30 days are shown
|
|
99
|
+
</Typography>
|
|
100
|
+
</Flex>
|
|
101
|
+
<Button
|
|
102
|
+
variant="default"
|
|
103
|
+
onClick={onFilterApply}
|
|
104
|
+
loading={isLoading}
|
|
105
|
+
startIcon={<Search />}
|
|
106
|
+
>
|
|
107
|
+
Apply Filters
|
|
108
|
+
</Button>
|
|
109
|
+
</Box>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default TransactionHistoryTableFilters;
|