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
package/admin/src/pages/App/components/TransactionHistoryTable/TransactionHistoryTablePagination.jsx
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Flex,
|
|
5
|
+
Button,
|
|
6
|
+
TextInput,
|
|
7
|
+
Typography,
|
|
8
|
+
Select,
|
|
9
|
+
Option,
|
|
10
|
+
} from "@strapi/design-system";
|
|
11
|
+
import { ArrowLeft, ArrowRight, ChevronLeft, ChevronRight } from "@strapi/icons";
|
|
12
|
+
|
|
13
|
+
const TransactionHistoryTablePagination = ({
|
|
14
|
+
currentPage,
|
|
15
|
+
totalPages,
|
|
16
|
+
pageSize,
|
|
17
|
+
totalItems,
|
|
18
|
+
onPageChange,
|
|
19
|
+
onPageSizeChange,
|
|
20
|
+
isLoading,
|
|
21
|
+
}) => {
|
|
22
|
+
const [pageInput, setPageInput] = useState(currentPage.toString());
|
|
23
|
+
|
|
24
|
+
// Update input when currentPage changes externally
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
setPageInput(currentPage.toString());
|
|
27
|
+
}, [currentPage]);
|
|
28
|
+
|
|
29
|
+
const handlePageInputChange = (e) => {
|
|
30
|
+
const value = e.target.value;
|
|
31
|
+
// Allow empty input or valid numbers
|
|
32
|
+
if (value === "" || /^\d+$/.test(value)) {
|
|
33
|
+
setPageInput(value);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const handlePageInputBlur = () => {
|
|
38
|
+
const pageNum = parseInt(pageInput, 10);
|
|
39
|
+
if (pageNum && pageNum >= 1 && pageNum <= totalPages) {
|
|
40
|
+
onPageChange(pageNum);
|
|
41
|
+
} else {
|
|
42
|
+
// Reset to current page if invalid
|
|
43
|
+
setPageInput(currentPage.toString());
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handlePageInputKeyPress = (e) => {
|
|
48
|
+
if (e.key === "Enter") {
|
|
49
|
+
handlePageInputBlur();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const goToFirstPage = () => {
|
|
54
|
+
if (currentPage > 1) {
|
|
55
|
+
onPageChange(1);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const goToLastPage = () => {
|
|
60
|
+
if (currentPage < totalPages) {
|
|
61
|
+
onPageChange(totalPages);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const goToPreviousPage = () => {
|
|
66
|
+
if (currentPage > 1) {
|
|
67
|
+
onPageChange(currentPage - 1);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const goToNextPage = () => {
|
|
72
|
+
if (currentPage < totalPages) {
|
|
73
|
+
onPageChange(currentPage + 1);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const startItem = totalItems > 0 ? (currentPage - 1) * pageSize + 1 : 0;
|
|
78
|
+
const endItem = Math.min(currentPage * pageSize, totalItems);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Box>
|
|
82
|
+
<Flex
|
|
83
|
+
justifyContent="space-between"
|
|
84
|
+
alignItems="center"
|
|
85
|
+
gap={4}
|
|
86
|
+
wrap="wrap"
|
|
87
|
+
>
|
|
88
|
+
<Flex alignItems="center" gap={2}>
|
|
89
|
+
<Typography variant="pi" textColor="neutral600">
|
|
90
|
+
{totalItems > 0
|
|
91
|
+
? `Showing ${startItem}-${endItem} of ${totalItems} transactions`
|
|
92
|
+
: `No transactions`}
|
|
93
|
+
</Typography>
|
|
94
|
+
<Flex alignItems="center" gap={2}>
|
|
95
|
+
<Typography variant="pi" textColor="neutral600">
|
|
96
|
+
Page size:
|
|
97
|
+
</Typography>
|
|
98
|
+
<Select
|
|
99
|
+
value={pageSize.toString()}
|
|
100
|
+
onChange={(value) => onPageSizeChange(parseInt(value, 10))}
|
|
101
|
+
disabled={isLoading}
|
|
102
|
+
style={{ width: "80px", minWidth: "80px" }}
|
|
103
|
+
>
|
|
104
|
+
<Option value="10">10</Option>
|
|
105
|
+
<Option value="20">20</Option>
|
|
106
|
+
<Option value="30">30</Option>
|
|
107
|
+
<Option value="50">50</Option>
|
|
108
|
+
<Option value="100">100</Option>
|
|
109
|
+
</Select>
|
|
110
|
+
</Flex>
|
|
111
|
+
</Flex>
|
|
112
|
+
|
|
113
|
+
{totalPages > 1 && (
|
|
114
|
+
<Flex alignItems="center" gap={2}>
|
|
115
|
+
<Button
|
|
116
|
+
variant="tertiary"
|
|
117
|
+
onClick={goToFirstPage}
|
|
118
|
+
disabled={currentPage === 1 || isLoading}
|
|
119
|
+
startIcon={<ChevronLeft />}
|
|
120
|
+
size="S"
|
|
121
|
+
>
|
|
122
|
+
First
|
|
123
|
+
</Button>
|
|
124
|
+
|
|
125
|
+
<Button
|
|
126
|
+
variant="tertiary"
|
|
127
|
+
onClick={goToPreviousPage}
|
|
128
|
+
disabled={currentPage === 1 || isLoading}
|
|
129
|
+
startIcon={<ArrowLeft />}
|
|
130
|
+
size="S"
|
|
131
|
+
>
|
|
132
|
+
Prev
|
|
133
|
+
</Button>
|
|
134
|
+
|
|
135
|
+
<Flex alignItems="center" gap={2}>
|
|
136
|
+
<Typography variant="pi" textColor="neutral600">
|
|
137
|
+
Page
|
|
138
|
+
</Typography>
|
|
139
|
+
<TextInput
|
|
140
|
+
value={pageInput}
|
|
141
|
+
onChange={handlePageInputChange}
|
|
142
|
+
onBlur={handlePageInputBlur}
|
|
143
|
+
onKeyPress={handlePageInputKeyPress}
|
|
144
|
+
disabled={isLoading}
|
|
145
|
+
aria-label="Page number"
|
|
146
|
+
style={{ width: "60px", textAlign: "center" }}
|
|
147
|
+
/>
|
|
148
|
+
<Typography variant="pi" textColor="neutral600">
|
|
149
|
+
of {totalPages}
|
|
150
|
+
</Typography>
|
|
151
|
+
</Flex>
|
|
152
|
+
|
|
153
|
+
<Button
|
|
154
|
+
variant="tertiary"
|
|
155
|
+
onClick={goToNextPage}
|
|
156
|
+
disabled={currentPage === totalPages || isLoading}
|
|
157
|
+
endIcon={<ArrowRight />}
|
|
158
|
+
size="S"
|
|
159
|
+
>
|
|
160
|
+
Next
|
|
161
|
+
</Button>
|
|
162
|
+
|
|
163
|
+
<Button
|
|
164
|
+
variant="tertiary"
|
|
165
|
+
onClick={goToLastPage}
|
|
166
|
+
disabled={currentPage === totalPages || isLoading}
|
|
167
|
+
endIcon={<ChevronRight />}
|
|
168
|
+
size="S"
|
|
169
|
+
>
|
|
170
|
+
Last
|
|
171
|
+
</Button>
|
|
172
|
+
</Flex>
|
|
173
|
+
)}
|
|
174
|
+
</Flex>
|
|
175
|
+
</Box>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export default TransactionHistoryTablePagination;
|
|
180
|
+
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Table,
|
|
5
|
+
Thead,
|
|
6
|
+
Tbody,
|
|
7
|
+
Tr,
|
|
8
|
+
Th,
|
|
9
|
+
Td,
|
|
10
|
+
Typography,
|
|
11
|
+
Button,
|
|
12
|
+
Flex,
|
|
13
|
+
} from "@strapi/design-system";
|
|
14
|
+
import { User, File, ArrowUp, ArrowDown } from "@strapi/icons";
|
|
15
|
+
import StatusBadge from "../StatusBadge";
|
|
16
|
+
import CustomerInfoPopover from "../CustomerInfoPopover";
|
|
17
|
+
import RawDataPopover from "../RawDataPopover";
|
|
18
|
+
import TransactionHistoryTableFilters from "./TransactionHistoryTableFilters";
|
|
19
|
+
import TransactionHistoryTablePagination from "./TransactionHistoryTablePagination";
|
|
20
|
+
|
|
21
|
+
const TransactionHistoryTable = ({
|
|
22
|
+
transactions,
|
|
23
|
+
isLoading,
|
|
24
|
+
filters,
|
|
25
|
+
onFilterChange,
|
|
26
|
+
onFilterApply,
|
|
27
|
+
sorting,
|
|
28
|
+
onSort,
|
|
29
|
+
currentPage,
|
|
30
|
+
totalPages,
|
|
31
|
+
pageSize,
|
|
32
|
+
totalItems,
|
|
33
|
+
onPageChange,
|
|
34
|
+
onPageSizeChange,
|
|
35
|
+
}) => {
|
|
36
|
+
const SortableHeader = ({ column, label, sortable = true }) => {
|
|
37
|
+
const isActive = sorting?.sortBy === column;
|
|
38
|
+
const sortOrder = isActive ? sorting?.sortOrder : null;
|
|
39
|
+
|
|
40
|
+
const handleClick = () => {
|
|
41
|
+
if (sortable && onSort) {
|
|
42
|
+
onSort(column);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Th>
|
|
48
|
+
<Flex
|
|
49
|
+
alignItems="center"
|
|
50
|
+
gap={2}
|
|
51
|
+
onClick={handleClick}
|
|
52
|
+
style={{ cursor: sortable ? "pointer" : "default" }}
|
|
53
|
+
>
|
|
54
|
+
<Typography fontWeight="bold" textColor="primary500">
|
|
55
|
+
{label}
|
|
56
|
+
</Typography>
|
|
57
|
+
{sortable && (
|
|
58
|
+
<Box style={{ display: "flex", alignItems: "center" }}>
|
|
59
|
+
{sortOrder === "asc" && <ArrowUp size={8} />}
|
|
60
|
+
{sortOrder === "desc" && <ArrowDown size={8} />}
|
|
61
|
+
{!isActive && (
|
|
62
|
+
<Box style={{ opacity: 0.3 }}>
|
|
63
|
+
<ArrowUp size={8} />
|
|
64
|
+
</Box>
|
|
65
|
+
)}
|
|
66
|
+
</Box>
|
|
67
|
+
)}
|
|
68
|
+
</Flex>
|
|
69
|
+
</Th>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
const getCardTypeName = (cardtype) => {
|
|
73
|
+
switch (cardtype) {
|
|
74
|
+
case "V":
|
|
75
|
+
return "Visa";
|
|
76
|
+
case "M":
|
|
77
|
+
return "MasterCard";
|
|
78
|
+
default:
|
|
79
|
+
return cardtype || "";
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const getPaymentMethodName = (clearingtype, wallettype, cardtype) => {
|
|
84
|
+
switch (clearingtype) {
|
|
85
|
+
case "cc":
|
|
86
|
+
const cardTypeName = getCardTypeName(cardtype);
|
|
87
|
+
return cardTypeName ? `CC / ${cardTypeName}` : "Credit Card";
|
|
88
|
+
case "sb":
|
|
89
|
+
return "Online Banking";
|
|
90
|
+
case "wlt":
|
|
91
|
+
return wallettype === "PPE" ? "PayPal" : "Wallet";
|
|
92
|
+
case "elv":
|
|
93
|
+
return "Direct Debit (SEPA)";
|
|
94
|
+
default:
|
|
95
|
+
return "Unknown";
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const formatAmount = (amount, currency) => {
|
|
100
|
+
return `${(amount / 100).toFixed(2)} ${currency}`;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const formatDate = (dateString) => {
|
|
104
|
+
return new Date(dateString).toLocaleString("de-DE", {
|
|
105
|
+
year: "numeric",
|
|
106
|
+
month: "2-digit",
|
|
107
|
+
day: "2-digit",
|
|
108
|
+
hour: "2-digit",
|
|
109
|
+
minute: "2-digit",
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<Box>
|
|
115
|
+
{/* Table Filters */}
|
|
116
|
+
<TransactionHistoryTableFilters
|
|
117
|
+
filters={filters}
|
|
118
|
+
onFilterChange={onFilterChange}
|
|
119
|
+
onFilterApply={onFilterApply}
|
|
120
|
+
isLoading={isLoading}
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
<Box style={{ maxHeight: "600px", overflow: "auto", marginBottom: "1rem" }}>
|
|
125
|
+
<Table colCount={6} rowCount={transactions.length}>
|
|
126
|
+
<Thead>
|
|
127
|
+
<Tr>
|
|
128
|
+
<SortableHeader column="amount" label="Amount" />
|
|
129
|
+
<SortableHeader column="created_at" label="Created At" />
|
|
130
|
+
<SortableHeader column="status" label="Status" />
|
|
131
|
+
<SortableHeader column="reference" label="Reference" />
|
|
132
|
+
<SortableHeader column="method" label="Method" />
|
|
133
|
+
<Th>
|
|
134
|
+
<Typography fontWeight="bold" textColor="primary500">
|
|
135
|
+
Actions
|
|
136
|
+
</Typography>
|
|
137
|
+
</Th>
|
|
138
|
+
</Tr>
|
|
139
|
+
</Thead>
|
|
140
|
+
<Tbody>
|
|
141
|
+
{transactions.length === 0 && !isLoading ? (
|
|
142
|
+
<Tr>
|
|
143
|
+
<Td colSpan={6}>
|
|
144
|
+
<Box padding={6}>
|
|
145
|
+
<Typography textAlign="center" textColor="neutral600">
|
|
146
|
+
No transactions found
|
|
147
|
+
</Typography>
|
|
148
|
+
</Box>
|
|
149
|
+
</Td>
|
|
150
|
+
</Tr>
|
|
151
|
+
) : (
|
|
152
|
+
transactions.map((transaction) => (
|
|
153
|
+
<Tr key={transaction.id}>
|
|
154
|
+
<Td>
|
|
155
|
+
<Typography variant="pi" fontWeight="bold" textColor="primary600">
|
|
156
|
+
{formatAmount(transaction.amount, transaction.currency)}
|
|
157
|
+
</Typography>
|
|
158
|
+
</Td>
|
|
159
|
+
<Td>
|
|
160
|
+
<Typography variant="pi" textColor="neutral800">
|
|
161
|
+
{formatDate(transaction.created_at)}
|
|
162
|
+
</Typography>
|
|
163
|
+
</Td>
|
|
164
|
+
<Td>
|
|
165
|
+
<StatusBadge status={transaction.status} transaction={transaction} />
|
|
166
|
+
</Td>
|
|
167
|
+
<Td>
|
|
168
|
+
<Typography variant="pi" textColor="neutral800">
|
|
169
|
+
{transaction.reference}
|
|
170
|
+
</Typography>
|
|
171
|
+
</Td>
|
|
172
|
+
<Td>
|
|
173
|
+
<Typography variant="pi" textColor="neutral800">
|
|
174
|
+
{getPaymentMethodName(
|
|
175
|
+
transaction.raw_request?.clearingtype,
|
|
176
|
+
transaction.raw_request?.wallettype,
|
|
177
|
+
transaction.raw_request?.cardtype
|
|
178
|
+
)}
|
|
179
|
+
</Typography>
|
|
180
|
+
</Td>
|
|
181
|
+
<Td>
|
|
182
|
+
<Flex gap={2} justifyContent="flex-start">
|
|
183
|
+
<CustomerInfoPopover transaction={transaction}>
|
|
184
|
+
<Button
|
|
185
|
+
variant="secondary"
|
|
186
|
+
size="S"
|
|
187
|
+
startIcon={<User />}
|
|
188
|
+
>
|
|
189
|
+
View Customer
|
|
190
|
+
</Button>
|
|
191
|
+
</CustomerInfoPopover>
|
|
192
|
+
<RawDataPopover transaction={transaction}>
|
|
193
|
+
<Button
|
|
194
|
+
variant="secondary"
|
|
195
|
+
size="S"
|
|
196
|
+
startIcon={<File />}
|
|
197
|
+
>
|
|
198
|
+
View Raw Data
|
|
199
|
+
</Button>
|
|
200
|
+
</RawDataPopover>
|
|
201
|
+
</Flex>
|
|
202
|
+
</Td>
|
|
203
|
+
</Tr>
|
|
204
|
+
))
|
|
205
|
+
)}
|
|
206
|
+
</Tbody>
|
|
207
|
+
</Table>
|
|
208
|
+
</Box>
|
|
209
|
+
|
|
210
|
+
{/* Pagination */}
|
|
211
|
+
<TransactionHistoryTablePagination
|
|
212
|
+
currentPage={currentPage}
|
|
213
|
+
totalPages={totalPages}
|
|
214
|
+
pageSize={pageSize}
|
|
215
|
+
totalItems={totalItems}
|
|
216
|
+
onPageChange={onPageChange}
|
|
217
|
+
onPageSizeChange={onPageSizeChange}
|
|
218
|
+
isLoading={isLoading}
|
|
219
|
+
/>
|
|
220
|
+
</Box>
|
|
221
|
+
);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export default TransactionHistoryTable;
|
|
225
|
+
|
|
@@ -4,11 +4,30 @@ import PaymentMethodSelector from "./PaymentMethodSelector";
|
|
|
4
4
|
import AuthorizationForm from "./AuthorizationForm";
|
|
5
5
|
|
|
6
6
|
const ApplePayOnlyPanel = ({
|
|
7
|
+
paymentAmount,
|
|
8
|
+
setPaymentAmount,
|
|
9
|
+
authReference,
|
|
10
|
+
setAuthReference,
|
|
11
|
+
isProcessingPayment,
|
|
12
|
+
onAuthorization,
|
|
7
13
|
paymentMethod,
|
|
14
|
+
settings,
|
|
15
|
+
setGooglePayToken,
|
|
8
16
|
setPaymentMethod,
|
|
9
17
|
captureMode,
|
|
10
18
|
setCaptureMode,
|
|
11
19
|
onNavigateToConfig,
|
|
20
|
+
isLiveMode,
|
|
21
|
+
setCardcvc2,
|
|
22
|
+
cardtype,
|
|
23
|
+
setCardtype,
|
|
24
|
+
cardpan,
|
|
25
|
+
setCardpan,
|
|
26
|
+
cardexpiredate,
|
|
27
|
+
setCardexpiredate,
|
|
28
|
+
cardcvc2,
|
|
29
|
+
applePayToken,
|
|
30
|
+
setApplePayToken,
|
|
12
31
|
}) => {
|
|
13
32
|
return (
|
|
14
33
|
<Box
|
|
@@ -42,7 +61,32 @@ const ApplePayOnlyPanel = ({
|
|
|
42
61
|
captureMode={captureMode}
|
|
43
62
|
setCaptureMode={setCaptureMode}
|
|
44
63
|
onNavigateToConfig={onNavigateToConfig}
|
|
45
|
-
isLiveMode={
|
|
64
|
+
isLiveMode={isLiveMode}
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
<hr className="payment-divider" />
|
|
68
|
+
|
|
69
|
+
<AuthorizationForm
|
|
70
|
+
paymentAmount={paymentAmount}
|
|
71
|
+
setPaymentAmount={setPaymentAmount}
|
|
72
|
+
authReference={authReference}
|
|
73
|
+
setAuthReference={setAuthReference}
|
|
74
|
+
isProcessingPayment={isProcessingPayment}
|
|
75
|
+
onAuthorization={onAuthorization}
|
|
76
|
+
paymentMethod={paymentMethod}
|
|
77
|
+
settings={settings}
|
|
78
|
+
setGooglePayToken={setGooglePayToken}
|
|
79
|
+
applePayToken={applePayToken}
|
|
80
|
+
setApplePayToken={setApplePayToken}
|
|
81
|
+
cardtype={cardtype}
|
|
82
|
+
setCardtype={setCardtype}
|
|
83
|
+
cardpan={cardpan}
|
|
84
|
+
setCardpan={setCardpan}
|
|
85
|
+
cardexpiredate={cardexpiredate}
|
|
86
|
+
setCardexpiredate={setCardexpiredate}
|
|
87
|
+
cardcvc2={cardcvc2}
|
|
88
|
+
setCardcvc2={setCardcvc2}
|
|
89
|
+
isLiveMode={isLiveMode}
|
|
46
90
|
/>
|
|
47
91
|
</Box>
|
|
48
92
|
);
|
|
@@ -110,9 +110,12 @@ const App = () => {
|
|
|
110
110
|
onSave={settings.handleSave}
|
|
111
111
|
onTestConnection={settings.handleTestConnection}
|
|
112
112
|
onInputChange={settings.handleInputChange}
|
|
113
|
+
onPaymentMethodToggle={settings.handlePaymentMethodToggle}
|
|
113
114
|
filters={transactionHistory.filters}
|
|
114
115
|
onFilterChange={transactionHistory.handleFilterChange}
|
|
115
116
|
onFilterApply={transactionHistory.handleFilterApply}
|
|
117
|
+
sorting={transactionHistory.sorting}
|
|
118
|
+
onSort={transactionHistory.handleSort}
|
|
116
119
|
isLoadingHistory={transactionHistory.isLoadingHistory}
|
|
117
120
|
transactionHistory={transactionHistory.transactionHistory}
|
|
118
121
|
paginatedTransactions={transactionHistory.paginatedTransactions}
|
|
@@ -121,6 +124,7 @@ const App = () => {
|
|
|
121
124
|
pageSize={transactionHistory.pageSize}
|
|
122
125
|
onRefresh={transactionHistory.loadTransactionHistory}
|
|
123
126
|
onPageChange={transactionHistory.handlePageChange}
|
|
127
|
+
onPageSizeChange={transactionHistory.handlePageSizeChange}
|
|
124
128
|
selectedTransaction={transactionHistory.selectedTransaction}
|
|
125
129
|
onTransactionSelect={transactionHistory.handleTransactionSelect}
|
|
126
130
|
paymentActions={paymentActions}
|
|
@@ -41,6 +41,31 @@ const useSettings = () => {
|
|
|
41
41
|
setSettings((prev) => ({ ...prev, [field]: value }));
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
const handlePaymentMethodToggle = async (field, value) => {
|
|
45
|
+
let updatedSettings;
|
|
46
|
+
setSettings((prev) => {
|
|
47
|
+
updatedSettings = { ...prev, [field]: value };
|
|
48
|
+
return updatedSettings;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
setIsSaving(true);
|
|
52
|
+
try {
|
|
53
|
+
await payoneRequests.updateSettings(updatedSettings);
|
|
54
|
+
toggleNotification({
|
|
55
|
+
type: "success",
|
|
56
|
+
message: "Payment method updated successfully"
|
|
57
|
+
});
|
|
58
|
+
} catch (error) {
|
|
59
|
+
setSettings((prev) => ({ ...prev, [field]: !value }));
|
|
60
|
+
toggleNotification({
|
|
61
|
+
type: "warning",
|
|
62
|
+
message: "Failed to update payment method"
|
|
63
|
+
});
|
|
64
|
+
} finally {
|
|
65
|
+
setIsSaving(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
44
69
|
const handleSave = async () => {
|
|
45
70
|
setIsSaving(true);
|
|
46
71
|
try {
|
|
@@ -103,6 +128,7 @@ const useSettings = () => {
|
|
|
103
128
|
isTesting,
|
|
104
129
|
testResult,
|
|
105
130
|
handleInputChange,
|
|
131
|
+
handlePaymentMethodToggle,
|
|
106
132
|
handleSave,
|
|
107
133
|
handleTestConnection
|
|
108
134
|
};
|
|
@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
|
|
|
2
2
|
import { useNotification } from "@strapi/helper-plugin";
|
|
3
3
|
import payoneRequests from "../utils/api";
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const DEFAULT_PAGE_SIZE = 10;
|
|
6
6
|
|
|
7
7
|
const useTransactionHistory = () => {
|
|
8
8
|
const toggleNotification = useNotification();
|
|
@@ -10,13 +10,32 @@ const useTransactionHistory = () => {
|
|
|
10
10
|
const [isLoadingHistory, setIsLoadingHistory] = useState(false);
|
|
11
11
|
const [selectedTransaction, setSelectedTransaction] = useState(null);
|
|
12
12
|
const [currentPage, setCurrentPage] = useState(1);
|
|
13
|
+
const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
|
|
14
|
+
// Calculate default dates
|
|
15
|
+
const getDefaultDateFrom = () => {
|
|
16
|
+
const date = new Date();
|
|
17
|
+
date.setDate(date.getDate() - 30);
|
|
18
|
+
return date.toISOString().split('T')[0];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getDefaultDateTo = () => {
|
|
22
|
+
const date = new Date();
|
|
23
|
+
date.setDate(date.getDate() + 1); // Add 1 day to include today's transactions
|
|
24
|
+
return date.toISOString().split('T')[0];
|
|
25
|
+
};
|
|
26
|
+
|
|
13
27
|
const [filters, setFilters] = useState({
|
|
14
|
-
|
|
28
|
+
search: "",
|
|
15
29
|
request_type: "",
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
payment_method: "",
|
|
31
|
+
date_from: getDefaultDateFrom(),
|
|
32
|
+
date_to: getDefaultDateTo(),
|
|
33
|
+
status: ""
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const [sorting, setSorting] = useState({
|
|
37
|
+
sortBy: null,
|
|
38
|
+
sortOrder: null // 'asc' or 'desc'
|
|
20
39
|
});
|
|
21
40
|
|
|
22
41
|
useEffect(() => {
|
|
@@ -26,7 +45,12 @@ const useTransactionHistory = () => {
|
|
|
26
45
|
const loadTransactionHistory = async () => {
|
|
27
46
|
setIsLoadingHistory(true);
|
|
28
47
|
try {
|
|
29
|
-
const
|
|
48
|
+
const params = { ...filters };
|
|
49
|
+
if (sorting.sortBy && sorting.sortOrder) {
|
|
50
|
+
params.sort_by = sorting.sortBy;
|
|
51
|
+
params.sort_order = sorting.sortOrder;
|
|
52
|
+
}
|
|
53
|
+
const result = await payoneRequests.getTransactionHistory(params);
|
|
30
54
|
setTransactionHistory(result.data || []);
|
|
31
55
|
setCurrentPage(1);
|
|
32
56
|
} catch (error) {
|
|
@@ -47,6 +71,37 @@ const useTransactionHistory = () => {
|
|
|
47
71
|
loadTransactionHistory();
|
|
48
72
|
};
|
|
49
73
|
|
|
74
|
+
const handleSort = (column) => {
|
|
75
|
+
setSorting((prev) => {
|
|
76
|
+
// If clicking the same column, cycle through: null -> asc -> desc -> null
|
|
77
|
+
if (prev.sortBy === column) {
|
|
78
|
+
if (!prev.sortOrder) {
|
|
79
|
+
return { sortBy: column, sortOrder: "asc" };
|
|
80
|
+
} else if (prev.sortOrder === "asc") {
|
|
81
|
+
return { sortBy: column, sortOrder: "desc" };
|
|
82
|
+
} else {
|
|
83
|
+
return { sortBy: null, sortOrder: null };
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
// If clicking a different column, reset and set new column to asc
|
|
87
|
+
return { sortBy: column, sortOrder: "asc" };
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Reload when sorting changes (but not on initial mount)
|
|
93
|
+
const [isInitialMount, setIsInitialMount] = useState(true);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (isInitialMount) {
|
|
97
|
+
setIsInitialMount(false);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Only reload if sorting is actually set or cleared
|
|
101
|
+
loadTransactionHistory();
|
|
102
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
103
|
+
}, [sorting.sortBy, sorting.sortOrder]);
|
|
104
|
+
|
|
50
105
|
const handleTransactionSelect = (transaction) => {
|
|
51
106
|
if (selectedTransaction?.id === transaction?.id) {
|
|
52
107
|
setSelectedTransaction(null);
|
|
@@ -60,10 +115,16 @@ const useTransactionHistory = () => {
|
|
|
60
115
|
setSelectedTransaction(null);
|
|
61
116
|
};
|
|
62
117
|
|
|
118
|
+
const handlePageSizeChange = (newPageSize) => {
|
|
119
|
+
setPageSize(newPageSize);
|
|
120
|
+
setCurrentPage(1); // Reset to first page when page size changes
|
|
121
|
+
setSelectedTransaction(null);
|
|
122
|
+
};
|
|
123
|
+
|
|
63
124
|
// Pagination calculations
|
|
64
|
-
const totalPages = Math.ceil(transactionHistory.length /
|
|
65
|
-
const startIndex = (currentPage - 1) *
|
|
66
|
-
const endIndex = startIndex +
|
|
125
|
+
const totalPages = Math.ceil(transactionHistory.length / pageSize);
|
|
126
|
+
const startIndex = (currentPage - 1) * pageSize;
|
|
127
|
+
const endIndex = startIndex + pageSize;
|
|
67
128
|
const paginatedTransactions = transactionHistory.slice(startIndex, endIndex);
|
|
68
129
|
|
|
69
130
|
return {
|
|
@@ -72,13 +133,16 @@ const useTransactionHistory = () => {
|
|
|
72
133
|
isLoadingHistory,
|
|
73
134
|
selectedTransaction,
|
|
74
135
|
filters,
|
|
136
|
+
sorting,
|
|
75
137
|
currentPage,
|
|
76
138
|
totalPages,
|
|
77
|
-
pageSize
|
|
139
|
+
pageSize,
|
|
78
140
|
handleFilterChange,
|
|
79
141
|
handleFilterApply,
|
|
142
|
+
handleSort,
|
|
80
143
|
handleTransactionSelect,
|
|
81
144
|
handlePageChange,
|
|
145
|
+
handlePageSizeChange,
|
|
82
146
|
loadTransactionHistory
|
|
83
147
|
};
|
|
84
148
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-payone-provider",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.6.9",
|
|
4
4
|
"description": "Strapi plugin for Payone payment gateway integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"maintainers": [
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"apple-pay-button": "^1.2.1",
|
|
14
14
|
"axios": "^1.6.3",
|
|
15
|
-
"prop-types": "^15.7.2"
|
|
15
|
+
"prop-types": "^15.7.2",
|
|
16
|
+
"@uiw/react-json-view": "^2.0.0-alpha.40"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
19
|
"react": "^18.2.0",
|