@blocklet/payment-react 1.24.2 → 1.24.4
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/es/contexts/payment.js +7 -0
- package/es/history/credit/transactions-list.js +177 -138
- package/es/history/invoice/list.js +88 -19
- package/es/locales/en.js +20 -4
- package/es/locales/zh.js +20 -4
- package/es/payment/product-item.js +2 -0
- package/lib/contexts/payment.js +4 -0
- package/lib/history/credit/transactions-list.js +257 -142
- package/lib/history/invoice/list.js +113 -28
- package/lib/locales/en.js +20 -4
- package/lib/locales/zh.js +20 -4
- package/lib/payment/product-item.js +2 -0
- package/package.json +3 -3
- package/src/contexts/payment.tsx +9 -0
- package/src/history/credit/transactions-list.tsx +227 -147
- package/src/history/invoice/list.tsx +114 -28
- package/src/locales/en.tsx +18 -2
- package/src/locales/zh.tsx +18 -2
- package/src/payment/product-item.tsx +4 -0
package/es/contexts/payment.js
CHANGED
|
@@ -4,6 +4,7 @@ import { useLocalStorageState, useRequest } from "ahooks";
|
|
|
4
4
|
import { createContext, useContext, useEffect, useState } from "react";
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
import { joinURL } from "ufo";
|
|
7
|
+
import useBus from "use-bus";
|
|
7
8
|
import api from "../libs/api.js";
|
|
8
9
|
import { getPrefix, PAYMENT_KIT_DID } from "../libs/util.js";
|
|
9
10
|
import { CachedRequest } from "../libs/cached-request.js";
|
|
@@ -116,6 +117,12 @@ function PaymentProvider({
|
|
|
116
117
|
} = useRequest(getSettings, {
|
|
117
118
|
refreshDeps: [livemode]
|
|
118
119
|
});
|
|
120
|
+
useBus(
|
|
121
|
+
// @ts-ignore
|
|
122
|
+
["paymentMethod.created", "paymentMethod.updated", "paymentCurrency.added", "paymentCurrency.updated"],
|
|
123
|
+
() => run(true),
|
|
124
|
+
[run]
|
|
125
|
+
);
|
|
119
126
|
useEffect(() => {
|
|
120
127
|
const didSpace = session?.user?.didSpace;
|
|
121
128
|
const userDid = session?.user?.did;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
3
|
-
import { Box, Typography, Grid, Stack, Link, Button
|
|
3
|
+
import { Box, Typography, Grid, Stack, Link, Button } from "@mui/material";
|
|
4
4
|
import { useRequest } from "ahooks";
|
|
5
5
|
import { useNavigate } from "react-router-dom";
|
|
6
|
-
import React, {
|
|
6
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
7
7
|
import { styled } from "@mui/system";
|
|
8
8
|
import { joinURL } from "ufo";
|
|
9
9
|
import DateRangePicker from "../../components/date-range-picker.js";
|
|
@@ -41,16 +41,102 @@ const getInvoiceDetailLink = (invoiceId, inDashboard) => {
|
|
|
41
41
|
connect: false
|
|
42
42
|
};
|
|
43
43
|
};
|
|
44
|
-
const
|
|
45
|
-
let path = `/customer/
|
|
44
|
+
const getSubscriptionDetailLink = (subscriptionId, inDashboard) => {
|
|
45
|
+
let path = `/customer/subscription/${subscriptionId}`;
|
|
46
46
|
if (inDashboard) {
|
|
47
|
-
path = `/admin/
|
|
47
|
+
path = `/admin/billing/${subscriptionId}`;
|
|
48
48
|
}
|
|
49
49
|
return {
|
|
50
50
|
link: createLink(path),
|
|
51
51
|
connect: false
|
|
52
52
|
};
|
|
53
53
|
};
|
|
54
|
+
const getCreditTransactionDetailLink = (transactionId) => {
|
|
55
|
+
const path = `/customer/credit-transaction/${transactionId}`;
|
|
56
|
+
return {
|
|
57
|
+
link: createLink(path),
|
|
58
|
+
connect: false
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const getMeterEventDetailLink = (meterEventId) => {
|
|
62
|
+
const path = `/admin/billing/${meterEventId}`;
|
|
63
|
+
return {
|
|
64
|
+
link: createLink(path),
|
|
65
|
+
connect: false
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
const getSubscriptionId = (item) => item.metadata?.subscription_id || item.subscription_id || item.invoice?.subscription_id;
|
|
69
|
+
const getInvoiceId = (item) => item.metadata?.invoice_id || item.invoice?.id;
|
|
70
|
+
const getMeterEventId = (item) => item.source || item.metadata?.meter_event_id;
|
|
71
|
+
const getCreditActivityFlags = (item) => {
|
|
72
|
+
const isGrant = item.activity_type === "grant";
|
|
73
|
+
const isScheduled = isGrant && item.metadata?.delivery_mode === "schedule";
|
|
74
|
+
const isDepleted = isGrant && item.status === "depleted";
|
|
75
|
+
const isExpired = isGrant && (item.status === "expired" || item.status === "voided");
|
|
76
|
+
const isInactive = isDepleted || isExpired;
|
|
77
|
+
return { isGrant, isScheduled, isDepleted, isExpired, isInactive };
|
|
78
|
+
};
|
|
79
|
+
const getTransactionDetailLink = (item, inDashboard) => {
|
|
80
|
+
if (item.activity_type === "grant") {
|
|
81
|
+
const invoiceId = getInvoiceId(item);
|
|
82
|
+
if (invoiceId) {
|
|
83
|
+
return getInvoiceDetailLink(invoiceId, inDashboard);
|
|
84
|
+
}
|
|
85
|
+
return getGrantDetailLink(item.id, inDashboard);
|
|
86
|
+
}
|
|
87
|
+
if (!inDashboard) {
|
|
88
|
+
return getCreditTransactionDetailLink(item.id);
|
|
89
|
+
}
|
|
90
|
+
const meterEventId = getMeterEventId(item);
|
|
91
|
+
if (!meterEventId) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return getMeterEventDetailLink(meterEventId);
|
|
95
|
+
};
|
|
96
|
+
const getTransactionDescription = (item, t) => {
|
|
97
|
+
const { isGrant, isScheduled, isInactive } = getCreditActivityFlags(item);
|
|
98
|
+
const isPaid = isGrant && item.category === "paid" && (!isScheduled || item.metadata?.schedule_seq === 1);
|
|
99
|
+
if (!isGrant) {
|
|
100
|
+
const secondLine = item.metadata?.is_repayment ? t("common.creditActivity.repayment") : item.description || "";
|
|
101
|
+
return {
|
|
102
|
+
isGrant,
|
|
103
|
+
isInactive,
|
|
104
|
+
activityType: t("common.creditActivity.consumption"),
|
|
105
|
+
secondLine
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (isPaid) {
|
|
109
|
+
let secondLine = item.description || "";
|
|
110
|
+
if (item.invoice?.total && item.invoice?.paymentCurrency) {
|
|
111
|
+
const invoiceCurrency = item.invoice.paymentCurrency;
|
|
112
|
+
const paidAmount = formatCreditAmount(
|
|
113
|
+
formatBNStr(item.invoice.total, invoiceCurrency.decimal || 0),
|
|
114
|
+
invoiceCurrency.symbol || ""
|
|
115
|
+
);
|
|
116
|
+
secondLine = t("common.creditActivity.paidAmount", { amount: paidAmount });
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
isGrant,
|
|
120
|
+
isInactive,
|
|
121
|
+
activityType: t("common.creditActivity.paidGrant"),
|
|
122
|
+
secondLine
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (isScheduled) {
|
|
126
|
+
return {
|
|
127
|
+
isGrant,
|
|
128
|
+
isInactive,
|
|
129
|
+
activityType: t("common.creditActivity.resetGrant"),
|
|
130
|
+
secondLine: item.description || ""
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
isGrant,
|
|
135
|
+
isInactive,
|
|
136
|
+
activityType: t("common.creditActivity.promotionalGrant"),
|
|
137
|
+
secondLine: item.description || ""
|
|
138
|
+
};
|
|
139
|
+
};
|
|
54
140
|
const TransactionsTable = React.memo((props) => {
|
|
55
141
|
const {
|
|
56
142
|
pageSize,
|
|
@@ -68,6 +154,7 @@ const TransactionsTable = React.memo((props) => {
|
|
|
68
154
|
const { t, locale } = useLocaleContext();
|
|
69
155
|
const { session } = usePaymentContext();
|
|
70
156
|
const isAdmin = ["owner", "admin"].includes(session?.user?.role || "");
|
|
157
|
+
const isDashboard = isAdmin && mode === "dashboard";
|
|
71
158
|
const navigate = useNavigate();
|
|
72
159
|
const effectiveCustomerId = customer_id || session?.user?.did;
|
|
73
160
|
const [search, setSearch] = useState({
|
|
@@ -78,7 +165,7 @@ const TransactionsTable = React.memo((props) => {
|
|
|
78
165
|
start: void 0,
|
|
79
166
|
end: void 0
|
|
80
167
|
});
|
|
81
|
-
const handleDateRangeChange =
|
|
168
|
+
const handleDateRangeChange = (newValue) => {
|
|
82
169
|
setFilters(newValue);
|
|
83
170
|
setSearch((prev) => ({
|
|
84
171
|
...prev,
|
|
@@ -86,7 +173,7 @@ const TransactionsTable = React.memo((props) => {
|
|
|
86
173
|
start: newValue.start || void 0,
|
|
87
174
|
end: newValue.end || void 0
|
|
88
175
|
}));
|
|
89
|
-
}
|
|
176
|
+
};
|
|
90
177
|
const { loading, data = { list: [], count: 0 } } = useRequest(
|
|
91
178
|
() => fetchData({
|
|
92
179
|
...search,
|
|
@@ -102,9 +189,14 @@ const TransactionsTable = React.memo((props) => {
|
|
|
102
189
|
);
|
|
103
190
|
useEffect(() => {
|
|
104
191
|
if (showTimeFilter && !search.start && !search.end) {
|
|
105
|
-
|
|
192
|
+
setSearch((prev) => ({
|
|
193
|
+
...prev,
|
|
194
|
+
page: 1,
|
|
195
|
+
start: filters.start || void 0,
|
|
196
|
+
end: filters.end || void 0
|
|
197
|
+
}));
|
|
106
198
|
}
|
|
107
|
-
}, [showTimeFilter,
|
|
199
|
+
}, [showTimeFilter, search.start, search.end, filters.start, filters.end]);
|
|
108
200
|
const prevData = useRef(data);
|
|
109
201
|
useEffect(() => {
|
|
110
202
|
if (onTableDataChange) {
|
|
@@ -113,105 +205,94 @@ const TransactionsTable = React.memo((props) => {
|
|
|
113
205
|
}
|
|
114
206
|
}, [data]);
|
|
115
207
|
const handleTransactionClick = (e, item) => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
} else {
|
|
120
|
-
const { link } = getTransactionDetailLink(item.id, isAdmin && mode === "dashboard");
|
|
121
|
-
handleNavigation(e, link, navigate, { target: link.external ? "_blank" : "_self" });
|
|
208
|
+
const detail = getTransactionDetailLink(item, isDashboard);
|
|
209
|
+
if (!detail) {
|
|
210
|
+
return;
|
|
122
211
|
}
|
|
212
|
+
handleNavigation(e, detail.link, navigate, { target: detail.link.external ? "_blank" : "_self" });
|
|
213
|
+
};
|
|
214
|
+
const openSubscription = (e, subscriptionId) => {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
const link = getSubscriptionDetailLink(subscriptionId, isDashboard);
|
|
217
|
+
handleNavigation(e, link.link, navigate);
|
|
123
218
|
};
|
|
219
|
+
const openInvoice = (e, invoiceId) => {
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
const link = getInvoiceDetailLink(invoiceId, isDashboard);
|
|
222
|
+
handleNavigation(e, link.link, navigate);
|
|
223
|
+
};
|
|
224
|
+
const renderActionButton = (label, onClick) => /* @__PURE__ */ jsx(Button, { variant: "text", size: "small", color: "primary", sx: { whiteSpace: "nowrap" }, onClick, children: label });
|
|
124
225
|
const columns = [
|
|
125
226
|
{
|
|
126
|
-
label: t("common.
|
|
127
|
-
name: "
|
|
128
|
-
align: "right",
|
|
227
|
+
label: t("common.date"),
|
|
228
|
+
name: "created_at",
|
|
129
229
|
options: {
|
|
230
|
+
setCellProps: () => ({ style: { width: "25%" } }),
|
|
130
231
|
customBodyRenderLite: (_, index) => {
|
|
131
232
|
const item = data?.list[index];
|
|
132
|
-
|
|
133
|
-
const isExpiredGrant = isGrant && item.status === "expired";
|
|
134
|
-
const amount = isGrant ? item.amount : item.credit_amount;
|
|
135
|
-
const currency = item.paymentCurrency || item.currency;
|
|
136
|
-
const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
|
|
137
|
-
const displayAmount = formatCreditAmount(formatBNStr(amount, currency?.decimal || 0), unit);
|
|
138
|
-
if (!includeGrants) {
|
|
139
|
-
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsx(Typography, { children: displayAmount }) });
|
|
140
|
-
}
|
|
141
|
-
const amountNode = /* @__PURE__ */ jsxs(
|
|
142
|
-
Typography,
|
|
143
|
-
{
|
|
144
|
-
sx: {
|
|
145
|
-
color: isGrant ? isExpiredGrant ? "text.disabled" : "success.main" : "error.main"
|
|
146
|
-
},
|
|
147
|
-
children: [
|
|
148
|
-
isGrant ? "+" : "-",
|
|
149
|
-
" ",
|
|
150
|
-
displayAmount
|
|
151
|
-
]
|
|
152
|
-
}
|
|
153
|
-
);
|
|
154
|
-
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", justifyContent: "flex-end", sx: { width: "100%" }, children: [
|
|
155
|
-
isExpiredGrant && /* @__PURE__ */ jsx(
|
|
156
|
-
Chip,
|
|
157
|
-
{
|
|
158
|
-
label: t("admin.creditGrants.status.expired"),
|
|
159
|
-
size: "small",
|
|
160
|
-
variant: "outlined",
|
|
161
|
-
sx: {
|
|
162
|
-
mr: 2,
|
|
163
|
-
height: 18,
|
|
164
|
-
fontSize: "12px",
|
|
165
|
-
color: "text.disabled",
|
|
166
|
-
borderColor: "text.disabled"
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
),
|
|
170
|
-
amountNode
|
|
171
|
-
] }) });
|
|
233
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontSize: "0.875rem" }, children: formatToDate(item.created_at, locale, "YYYY-MM-DD HH:mm") }) });
|
|
172
234
|
}
|
|
173
235
|
}
|
|
174
236
|
},
|
|
175
237
|
{
|
|
176
|
-
label: t("common.
|
|
177
|
-
name: "
|
|
238
|
+
label: t("common.description"),
|
|
239
|
+
name: "description",
|
|
178
240
|
options: {
|
|
241
|
+
setCellProps: () => ({ style: { width: "25%" } }),
|
|
179
242
|
customBodyRenderLite: (_, index) => {
|
|
180
243
|
const item = data?.list[index];
|
|
181
|
-
const isGrant = item
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
sx: {
|
|
196
|
-
alignItems: "center"
|
|
197
|
-
},
|
|
198
|
-
children: nameNode
|
|
199
|
-
}
|
|
200
|
-
);
|
|
244
|
+
const { activityType, secondLine, isInactive, isGrant } = getTransactionDescription(item, t);
|
|
245
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { cursor: "pointer" }, children: /* @__PURE__ */ jsxs(Stack, { direction: "column", spacing: 0.25, children: [
|
|
246
|
+
/* @__PURE__ */ jsx(
|
|
247
|
+
Typography,
|
|
248
|
+
{
|
|
249
|
+
variant: "body2",
|
|
250
|
+
sx: {
|
|
251
|
+
color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main"
|
|
252
|
+
},
|
|
253
|
+
children: activityType
|
|
254
|
+
}
|
|
255
|
+
),
|
|
256
|
+
secondLine && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: secondLine })
|
|
257
|
+
] }) });
|
|
201
258
|
}
|
|
202
259
|
}
|
|
203
260
|
},
|
|
204
261
|
{
|
|
205
|
-
label: t("common.
|
|
206
|
-
name: "
|
|
262
|
+
label: t("common.amount"),
|
|
263
|
+
name: "credit_amount",
|
|
264
|
+
align: "right",
|
|
207
265
|
options: {
|
|
266
|
+
setCellProps: () => ({ style: { width: "20%" } }),
|
|
267
|
+
customHeadLabelRender: () => /* @__PURE__ */ jsx(Box, { sx: { pr: 5 }, children: t("common.amount") }),
|
|
208
268
|
customBodyRenderLite: (_, index) => {
|
|
209
269
|
const item = data?.list[index];
|
|
210
|
-
const isGrant = item
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
const
|
|
214
|
-
|
|
270
|
+
const { isGrant, isDepleted, isExpired, isInactive } = getCreditActivityFlags(item);
|
|
271
|
+
const amount = isGrant ? item.amount : item.credit_amount;
|
|
272
|
+
const currency = item.paymentCurrency || item.currency;
|
|
273
|
+
const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
|
|
274
|
+
const displayAmount = formatCreditAmount(formatBNStr(amount, currency?.decimal || 0), unit);
|
|
275
|
+
if (!includeGrants) {
|
|
276
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { pr: 5 }, children: /* @__PURE__ */ jsx(Typography, { children: displayAmount }) });
|
|
277
|
+
}
|
|
278
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { pr: 5 }, children: /* @__PURE__ */ jsxs(Stack, { direction: "column", spacing: 0.25, alignItems: "flex-end", children: [
|
|
279
|
+
/* @__PURE__ */ jsxs(
|
|
280
|
+
Typography,
|
|
281
|
+
{
|
|
282
|
+
sx: {
|
|
283
|
+
fontWeight: 500,
|
|
284
|
+
color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main",
|
|
285
|
+
whiteSpace: "nowrap"
|
|
286
|
+
},
|
|
287
|
+
children: [
|
|
288
|
+
isGrant ? "+" : "-",
|
|
289
|
+
" ",
|
|
290
|
+
displayAmount
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
),
|
|
294
|
+
isDepleted ? /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: t("common.consumed") }) : isExpired ? /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: t("common.expired") }) : null
|
|
295
|
+
] }) });
|
|
215
296
|
}
|
|
216
297
|
}
|
|
217
298
|
},
|
|
@@ -230,63 +311,21 @@ const TransactionsTable = React.memo((props) => {
|
|
|
230
311
|
}
|
|
231
312
|
}
|
|
232
313
|
] : [],
|
|
233
|
-
{
|
|
234
|
-
label: t("common.date"),
|
|
235
|
-
name: "created_at",
|
|
236
|
-
options: {
|
|
237
|
-
customBodyRenderLite: (_, index) => {
|
|
238
|
-
const item = data?.list[index];
|
|
239
|
-
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsx(
|
|
240
|
-
Typography,
|
|
241
|
-
{
|
|
242
|
-
variant: "body2",
|
|
243
|
-
sx: {
|
|
244
|
-
color: "text.secondary",
|
|
245
|
-
fontSize: "0.875rem"
|
|
246
|
-
},
|
|
247
|
-
children: formatToDate(item.created_at, locale, "YYYY-MM-DD HH:mm")
|
|
248
|
-
}
|
|
249
|
-
) });
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
314
|
{
|
|
254
315
|
label: t("common.actions"),
|
|
255
316
|
name: "actions",
|
|
256
317
|
options: {
|
|
318
|
+
setCellProps: () => ({ style: { width: "25%" } }),
|
|
257
319
|
customBodyRenderLite: (_, index) => {
|
|
258
320
|
const item = data?.list[index];
|
|
259
|
-
const isGrant = item
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
color: "primary",
|
|
268
|
-
onClick: (e) => {
|
|
269
|
-
e.preventDefault();
|
|
270
|
-
const link = getInvoiceDetailLink(invoiceId, isAdmin && mode === "dashboard");
|
|
271
|
-
handleNavigation(e, link.link, navigate);
|
|
272
|
-
},
|
|
273
|
-
children: t("common.viewInvoice")
|
|
274
|
-
}
|
|
275
|
-
),
|
|
276
|
-
!isGrant && /* @__PURE__ */ jsx(
|
|
277
|
-
Button,
|
|
278
|
-
{
|
|
279
|
-
variant: "text",
|
|
280
|
-
size: "small",
|
|
281
|
-
color: "primary",
|
|
282
|
-
onClick: (e) => {
|
|
283
|
-
e.preventDefault();
|
|
284
|
-
const link = getTransactionDetailLink(item.id, isAdmin && mode === "dashboard");
|
|
285
|
-
handleNavigation(e, link.link, navigate);
|
|
286
|
-
},
|
|
287
|
-
children: t("common.viewDetail")
|
|
288
|
-
}
|
|
289
|
-
)
|
|
321
|
+
const { isGrant, isScheduled } = getCreditActivityFlags(item);
|
|
322
|
+
const isPaid = isGrant && item.category === "paid" && !isScheduled;
|
|
323
|
+
const subscriptionId = getSubscriptionId(item);
|
|
324
|
+
const invoiceId = isGrant ? getInvoiceId(item) : null;
|
|
325
|
+
const shouldShowSubscription = Boolean(subscriptionId) && (!isGrant || isScheduled || isPaid);
|
|
326
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 1, alignItems: "center", flexWrap: "nowrap" }, children: [
|
|
327
|
+
shouldShowSubscription && subscriptionId ? renderActionButton(t("common.viewSubscription"), (e) => openSubscription(e, subscriptionId)) : null,
|
|
328
|
+
isPaid && invoiceId ? renderActionButton(t("common.viewInvoice"), (e) => openInvoice(e, invoiceId)) : null
|
|
290
329
|
] });
|
|
291
330
|
}
|
|
292
331
|
}
|
|
@@ -15,6 +15,7 @@ import api from "../../libs/api.js";
|
|
|
15
15
|
import StripePaymentAction from "../../components/stripe-payment-action.js";
|
|
16
16
|
import {
|
|
17
17
|
formatBNStr,
|
|
18
|
+
formatCreditAmount,
|
|
18
19
|
formatError,
|
|
19
20
|
formatToDate,
|
|
20
21
|
formatToDatetime,
|
|
@@ -80,6 +81,7 @@ const InvoiceTable = React.memo((props) => {
|
|
|
80
81
|
const listKey = "invoice-table";
|
|
81
82
|
const { t, locale } = useLocaleContext();
|
|
82
83
|
const navigate = useNavigate();
|
|
84
|
+
const { getCurrency } = usePaymentContext();
|
|
83
85
|
const [search, setSearch] = useState({
|
|
84
86
|
pageSize: pageSize || 10,
|
|
85
87
|
page: 1
|
|
@@ -203,19 +205,97 @@ const InvoiceTable = React.memo((props) => {
|
|
|
203
205
|
},
|
|
204
206
|
...relatedSubscription ? [
|
|
205
207
|
{
|
|
206
|
-
label: t("common.
|
|
207
|
-
name: "
|
|
208
|
+
label: t("common.purchaseItems"),
|
|
209
|
+
name: "purchase_items",
|
|
208
210
|
options: {
|
|
209
211
|
customBodyRenderLite: (_, index) => {
|
|
210
212
|
const invoice = data?.list[index];
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
const lines = invoice.lines || [];
|
|
214
|
+
const items = lines.map((line) => {
|
|
215
|
+
const name = line.price?.product?.name || line.description;
|
|
216
|
+
if (!name) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
const quantity = Number(line.quantity || 0);
|
|
220
|
+
const label = Number.isFinite(quantity) && quantity > 1 ? `${name} x${quantity}` : name;
|
|
221
|
+
const lineKey = line.id || line.price?.id || line.price?.product?.id || line.description || name;
|
|
222
|
+
return { key: String(lineKey), label };
|
|
223
|
+
}).filter(Boolean);
|
|
224
|
+
if (items.length === 0 && invoice.subscription?.description) {
|
|
225
|
+
items.push({
|
|
226
|
+
key: `subscription-${invoice.subscription_id || invoice.id}`,
|
|
227
|
+
label: invoice.subscription.description
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const isSubscription = Boolean(invoice.subscription_id);
|
|
231
|
+
const clickableProps = isSubscription ? { onClick: (e) => handleRelatedSubscriptionClick(e, invoice) } : {};
|
|
232
|
+
if (items.length === 0) {
|
|
233
|
+
return /* @__PURE__ */ jsx(Box, { sx: { color: "text.lighter" }, children: /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14 }, children: t("common.none") }) });
|
|
234
|
+
}
|
|
235
|
+
return /* @__PURE__ */ jsx(Box, { ...clickableProps, sx: isSubscription ? { cursor: "pointer" } : void 0, children: /* @__PURE__ */ jsx(Stack, { spacing: 0.5, children: items.map((item) => /* @__PURE__ */ jsx(
|
|
236
|
+
Typography,
|
|
213
237
|
{
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
children:
|
|
238
|
+
sx: { fontSize: 14, color: isSubscription ? "text.link" : "text.primary" },
|
|
239
|
+
noWrap: true,
|
|
240
|
+
children: item.label
|
|
241
|
+
},
|
|
242
|
+
`${invoice.id}-item-${item.key}`
|
|
243
|
+
)) }) });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
label: t("common.credits"),
|
|
249
|
+
name: "credits",
|
|
250
|
+
options: {
|
|
251
|
+
customBodyRenderLite: (_, index) => {
|
|
252
|
+
const invoice = data?.list[index];
|
|
253
|
+
const lines = invoice.lines || [];
|
|
254
|
+
const creditItems = [];
|
|
255
|
+
lines.forEach((line) => {
|
|
256
|
+
const lineKey = String(
|
|
257
|
+
line.id || line.price?.id || line.price?.product?.id || line.description || invoice.id
|
|
258
|
+
);
|
|
259
|
+
const pushCreditItem = (suffix, label) => {
|
|
260
|
+
creditItems.push({ key: `${lineKey}-${suffix}`, label });
|
|
261
|
+
};
|
|
262
|
+
const creditConfig = line.price?.metadata?.credit_config;
|
|
263
|
+
const creditAmount = Number(creditConfig?.credit_amount || 0);
|
|
264
|
+
if (creditAmount > 0) {
|
|
265
|
+
const quantity = Number(line.quantity || 0);
|
|
266
|
+
const totalAmount = creditAmount * (Number.isFinite(quantity) && quantity > 0 ? quantity : 1);
|
|
267
|
+
const currencySymbol = getCurrency(creditConfig?.currency_id)?.symbol || creditConfig?.currency_id || "Credits";
|
|
268
|
+
pushCreditItem("amount", `+${formatCreditAmount(String(totalAmount), currencySymbol)}`);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const scheduleConfig = creditConfig?.schedule;
|
|
272
|
+
const scheduledAmount = Number(scheduleConfig?.amount_per_grant || 0);
|
|
273
|
+
if (scheduleConfig?.enabled && scheduleConfig?.delivery_mode === "schedule" && scheduledAmount > 0) {
|
|
274
|
+
const quantity = Number(line.quantity || 0);
|
|
275
|
+
const totalAmount = scheduledAmount * (Number.isFinite(quantity) && quantity > 0 ? quantity : 1);
|
|
276
|
+
const currencySymbol = getCurrency(creditConfig?.currency_id)?.symbol || creditConfig?.currency_id || "Credits";
|
|
277
|
+
pushCreditItem("schedule", `+${formatCreditAmount(String(totalAmount), currencySymbol)}`);
|
|
278
|
+
return;
|
|
217
279
|
}
|
|
218
|
-
|
|
280
|
+
const creditInfo = line.price?.credit;
|
|
281
|
+
const creditInfoAmount = Number(creditInfo?.amount || 0);
|
|
282
|
+
if (creditInfoAmount > 0) {
|
|
283
|
+
const currencySymbol = creditInfo.currency?.symbol || "Credits";
|
|
284
|
+
pushCreditItem("credit", `+${formatCreditAmount(String(creditInfoAmount), currencySymbol)}`);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (creditItems.length === 0) {
|
|
288
|
+
return "-";
|
|
289
|
+
}
|
|
290
|
+
return /* @__PURE__ */ jsx(Stack, { spacing: 0.5, children: creditItems.map((creditItem) => /* @__PURE__ */ jsx(
|
|
291
|
+
Typography,
|
|
292
|
+
{
|
|
293
|
+
sx: { fontSize: 14, color: "success.main" },
|
|
294
|
+
noWrap: true,
|
|
295
|
+
children: creditItem.label
|
|
296
|
+
},
|
|
297
|
+
`${invoice.id}-credit-${creditItem.key}`
|
|
298
|
+
)) });
|
|
219
299
|
}
|
|
220
300
|
}
|
|
221
301
|
}
|
|
@@ -234,17 +314,6 @@ const InvoiceTable = React.memo((props) => {
|
|
|
234
314
|
}
|
|
235
315
|
}
|
|
236
316
|
},
|
|
237
|
-
{
|
|
238
|
-
label: t("common.description"),
|
|
239
|
-
name: "",
|
|
240
|
-
options: {
|
|
241
|
-
sort: false,
|
|
242
|
-
customBodyRenderLite: (val, index) => {
|
|
243
|
-
const invoice = data?.list[index];
|
|
244
|
-
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: getInvoiceDescriptionAndReason(invoice, locale)?.description || invoice.id });
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
317
|
{
|
|
249
318
|
label: t("common.status"),
|
|
250
319
|
name: "status",
|
package/es/locales/en.js
CHANGED
|
@@ -65,14 +65,24 @@ export default flat({
|
|
|
65
65
|
remainingBalance: "Remaining Balance",
|
|
66
66
|
credits: "credits",
|
|
67
67
|
ofCredits: "of credits",
|
|
68
|
+
creditActivity: {
|
|
69
|
+
consumption: "Credit Consumed",
|
|
70
|
+
paidGrant: "Credit Top-up",
|
|
71
|
+
paidAmount: "Paid {amount}",
|
|
72
|
+
promotionalGrant: "Bonus",
|
|
73
|
+
resetGrant: "Credit Reset",
|
|
74
|
+
repayment: "Repayment"
|
|
75
|
+
},
|
|
68
76
|
transferStatus: "Transaction Status",
|
|
69
77
|
sourceData: "Source Data",
|
|
70
78
|
viewGrant: "View Grant",
|
|
71
|
-
viewSubscription: "
|
|
79
|
+
viewSubscription: "Subscription Detail",
|
|
72
80
|
view: "View",
|
|
73
81
|
meterEvent: "Meter Event",
|
|
74
82
|
source: "Source",
|
|
75
83
|
viewDetail: "View Detail",
|
|
84
|
+
viewTransactionDetail: "Transaction Detail",
|
|
85
|
+
viewConsumptionDetail: "Consumption Detail",
|
|
76
86
|
customer: "Customer",
|
|
77
87
|
currency: "Currency",
|
|
78
88
|
custom: "Custom",
|
|
@@ -119,6 +129,8 @@ export default flat({
|
|
|
119
129
|
slashStakeAmount: "Slash Stake Amount",
|
|
120
130
|
know: "I know",
|
|
121
131
|
relatedSubscription: "Subscription",
|
|
132
|
+
subscriptionOrCredit: "Subscription / Credit",
|
|
133
|
+
purchaseItems: "Purchase Items",
|
|
122
134
|
connect: {
|
|
123
135
|
defaultScan: "Use the following methods to complete this action",
|
|
124
136
|
scan: "Use the following methods to complete this {action}",
|
|
@@ -126,8 +138,10 @@ export default flat({
|
|
|
126
138
|
cancel: "Cancel"
|
|
127
139
|
},
|
|
128
140
|
paymentMethod: "Payment Method",
|
|
129
|
-
viewInvoice: "
|
|
130
|
-
submit: "Submit"
|
|
141
|
+
viewInvoice: "Invoice Detail",
|
|
142
|
+
submit: "Submit",
|
|
143
|
+
expired: "Expired",
|
|
144
|
+
consumed: "Consumed"
|
|
131
145
|
},
|
|
132
146
|
payment: {
|
|
133
147
|
checkout: {
|
|
@@ -488,7 +502,9 @@ export default flat({
|
|
|
488
502
|
amount: "Amount",
|
|
489
503
|
paymentConfirmTitle: "Payment Confirmation",
|
|
490
504
|
paymentConfirmDescription: "After completing this payment, the payment method you use will be automatically set as the default for this subscription. Additionally, we will retry payment for any other unpaid invoices associated with this subscription.",
|
|
491
|
-
continue: "Continue"
|
|
505
|
+
continue: "Continue",
|
|
506
|
+
credit: "Credit",
|
|
507
|
+
creditRefresh: "Refresh every {interval} {unit}"
|
|
492
508
|
},
|
|
493
509
|
overduePayment: {
|
|
494
510
|
setupPaymentDescription: "Use your saved card or add a new one to complete payment via Stripe.",
|