@blocklet/payment-react 1.24.2 → 1.24.3
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 +167 -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 +247 -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 +214 -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,92 @@ 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/subscriptions/${subscriptionId}`;
|
|
48
48
|
}
|
|
49
49
|
return {
|
|
50
50
|
link: createLink(path),
|
|
51
51
|
connect: false
|
|
52
52
|
};
|
|
53
53
|
};
|
|
54
|
+
const getMeterEventDetailLink = (meterEventId) => {
|
|
55
|
+
const path = `/admin/billing/${meterEventId}`;
|
|
56
|
+
return {
|
|
57
|
+
link: createLink(path),
|
|
58
|
+
connect: false
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const getSubscriptionId = (item) => item.metadata?.subscription_id || item.subscription_id || item.invoice?.subscription_id;
|
|
62
|
+
const getInvoiceId = (item) => item.metadata?.invoice_id || item.invoice?.id;
|
|
63
|
+
const getMeterEventId = (item) => item.source || item.metadata?.meter_event_id;
|
|
64
|
+
const getCreditActivityFlags = (item) => {
|
|
65
|
+
const isGrant = item.activity_type === "grant";
|
|
66
|
+
const isScheduled = isGrant && item.metadata?.delivery_mode === "schedule";
|
|
67
|
+
const isDepleted = isGrant && item.status === "depleted";
|
|
68
|
+
const isExpired = isGrant && (item.status === "expired" || item.status === "voided");
|
|
69
|
+
const isInactive = isDepleted || isExpired;
|
|
70
|
+
return { isGrant, isScheduled, isDepleted, isExpired, isInactive };
|
|
71
|
+
};
|
|
72
|
+
const getTransactionDetailLink = (item, inDashboard) => {
|
|
73
|
+
if (item.activity_type === "grant") {
|
|
74
|
+
const invoiceId = getInvoiceId(item);
|
|
75
|
+
if (invoiceId) {
|
|
76
|
+
return getInvoiceDetailLink(invoiceId, inDashboard);
|
|
77
|
+
}
|
|
78
|
+
return getGrantDetailLink(item.id, inDashboard);
|
|
79
|
+
}
|
|
80
|
+
const meterEventId = getMeterEventId(item);
|
|
81
|
+
if (!meterEventId) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return getMeterEventDetailLink(meterEventId);
|
|
85
|
+
};
|
|
86
|
+
const getTransactionDescription = (item, t) => {
|
|
87
|
+
const { isGrant, isScheduled, isInactive } = getCreditActivityFlags(item);
|
|
88
|
+
const isPaid = isGrant && item.category === "paid" && (!isScheduled || item.metadata?.schedule_seq === 1);
|
|
89
|
+
if (!isGrant) {
|
|
90
|
+
const secondLine = item.metadata?.is_repayment ? t("common.creditActivity.repayment") : item.description || "";
|
|
91
|
+
return {
|
|
92
|
+
isGrant,
|
|
93
|
+
isInactive,
|
|
94
|
+
activityType: t("common.creditActivity.consumption"),
|
|
95
|
+
secondLine
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (isPaid) {
|
|
99
|
+
let secondLine = item.description || "";
|
|
100
|
+
if (item.invoice?.total && item.invoice?.paymentCurrency) {
|
|
101
|
+
const invoiceCurrency = item.invoice.paymentCurrency;
|
|
102
|
+
const paidAmount = formatCreditAmount(
|
|
103
|
+
formatBNStr(item.invoice.total, invoiceCurrency.decimal || 0),
|
|
104
|
+
invoiceCurrency.symbol || ""
|
|
105
|
+
);
|
|
106
|
+
secondLine = t("common.creditActivity.paidAmount", { amount: paidAmount });
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
isGrant,
|
|
110
|
+
isInactive,
|
|
111
|
+
activityType: t("common.creditActivity.paidGrant"),
|
|
112
|
+
secondLine
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (isScheduled) {
|
|
116
|
+
return {
|
|
117
|
+
isGrant,
|
|
118
|
+
isInactive,
|
|
119
|
+
activityType: t("common.creditActivity.resetGrant"),
|
|
120
|
+
secondLine: item.description || ""
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
isGrant,
|
|
125
|
+
isInactive,
|
|
126
|
+
activityType: t("common.creditActivity.promotionalGrant"),
|
|
127
|
+
secondLine: item.description || ""
|
|
128
|
+
};
|
|
129
|
+
};
|
|
54
130
|
const TransactionsTable = React.memo((props) => {
|
|
55
131
|
const {
|
|
56
132
|
pageSize,
|
|
@@ -68,6 +144,7 @@ const TransactionsTable = React.memo((props) => {
|
|
|
68
144
|
const { t, locale } = useLocaleContext();
|
|
69
145
|
const { session } = usePaymentContext();
|
|
70
146
|
const isAdmin = ["owner", "admin"].includes(session?.user?.role || "");
|
|
147
|
+
const isDashboard = isAdmin && mode === "dashboard";
|
|
71
148
|
const navigate = useNavigate();
|
|
72
149
|
const effectiveCustomerId = customer_id || session?.user?.did;
|
|
73
150
|
const [search, setSearch] = useState({
|
|
@@ -78,7 +155,7 @@ const TransactionsTable = React.memo((props) => {
|
|
|
78
155
|
start: void 0,
|
|
79
156
|
end: void 0
|
|
80
157
|
});
|
|
81
|
-
const handleDateRangeChange =
|
|
158
|
+
const handleDateRangeChange = (newValue) => {
|
|
82
159
|
setFilters(newValue);
|
|
83
160
|
setSearch((prev) => ({
|
|
84
161
|
...prev,
|
|
@@ -86,7 +163,7 @@ const TransactionsTable = React.memo((props) => {
|
|
|
86
163
|
start: newValue.start || void 0,
|
|
87
164
|
end: newValue.end || void 0
|
|
88
165
|
}));
|
|
89
|
-
}
|
|
166
|
+
};
|
|
90
167
|
const { loading, data = { list: [], count: 0 } } = useRequest(
|
|
91
168
|
() => fetchData({
|
|
92
169
|
...search,
|
|
@@ -102,9 +179,14 @@ const TransactionsTable = React.memo((props) => {
|
|
|
102
179
|
);
|
|
103
180
|
useEffect(() => {
|
|
104
181
|
if (showTimeFilter && !search.start && !search.end) {
|
|
105
|
-
|
|
182
|
+
setSearch((prev) => ({
|
|
183
|
+
...prev,
|
|
184
|
+
page: 1,
|
|
185
|
+
start: filters.start || void 0,
|
|
186
|
+
end: filters.end || void 0
|
|
187
|
+
}));
|
|
106
188
|
}
|
|
107
|
-
}, [showTimeFilter,
|
|
189
|
+
}, [showTimeFilter, search.start, search.end, filters.start, filters.end]);
|
|
108
190
|
const prevData = useRef(data);
|
|
109
191
|
useEffect(() => {
|
|
110
192
|
if (onTableDataChange) {
|
|
@@ -113,105 +195,94 @@ const TransactionsTable = React.memo((props) => {
|
|
|
113
195
|
}
|
|
114
196
|
}, [data]);
|
|
115
197
|
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" });
|
|
198
|
+
const detail = getTransactionDetailLink(item, isDashboard);
|
|
199
|
+
if (!detail) {
|
|
200
|
+
return;
|
|
122
201
|
}
|
|
202
|
+
handleNavigation(e, detail.link, navigate, { target: detail.link.external ? "_blank" : "_self" });
|
|
203
|
+
};
|
|
204
|
+
const openSubscription = (e, subscriptionId) => {
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
const link = getSubscriptionDetailLink(subscriptionId, isDashboard);
|
|
207
|
+
handleNavigation(e, link.link, navigate);
|
|
208
|
+
};
|
|
209
|
+
const openInvoice = (e, invoiceId) => {
|
|
210
|
+
e.preventDefault();
|
|
211
|
+
const link = getInvoiceDetailLink(invoiceId, isDashboard);
|
|
212
|
+
handleNavigation(e, link.link, navigate);
|
|
123
213
|
};
|
|
214
|
+
const renderActionButton = (label, onClick) => /* @__PURE__ */ jsx(Button, { variant: "text", size: "small", color: "primary", sx: { whiteSpace: "nowrap" }, onClick, children: label });
|
|
124
215
|
const columns = [
|
|
125
216
|
{
|
|
126
|
-
label: t("common.
|
|
127
|
-
name: "
|
|
128
|
-
align: "right",
|
|
217
|
+
label: t("common.date"),
|
|
218
|
+
name: "created_at",
|
|
129
219
|
options: {
|
|
220
|
+
setCellProps: () => ({ style: { width: "25%" } }),
|
|
130
221
|
customBodyRenderLite: (_, index) => {
|
|
131
222
|
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
|
-
] }) });
|
|
223
|
+
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
224
|
}
|
|
173
225
|
}
|
|
174
226
|
},
|
|
175
227
|
{
|
|
176
|
-
label: t("common.
|
|
177
|
-
name: "
|
|
228
|
+
label: t("common.description"),
|
|
229
|
+
name: "description",
|
|
178
230
|
options: {
|
|
231
|
+
setCellProps: () => ({ style: { width: "25%" } }),
|
|
179
232
|
customBodyRenderLite: (_, index) => {
|
|
180
233
|
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
|
-
);
|
|
234
|
+
const { activityType, secondLine, isInactive, isGrant } = getTransactionDescription(item, t);
|
|
235
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { cursor: "pointer" }, children: /* @__PURE__ */ jsxs(Stack, { direction: "column", spacing: 0.25, children: [
|
|
236
|
+
/* @__PURE__ */ jsx(
|
|
237
|
+
Typography,
|
|
238
|
+
{
|
|
239
|
+
variant: "body2",
|
|
240
|
+
sx: {
|
|
241
|
+
color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main"
|
|
242
|
+
},
|
|
243
|
+
children: activityType
|
|
244
|
+
}
|
|
245
|
+
),
|
|
246
|
+
secondLine && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: secondLine })
|
|
247
|
+
] }) });
|
|
201
248
|
}
|
|
202
249
|
}
|
|
203
250
|
},
|
|
204
251
|
{
|
|
205
|
-
label: t("common.
|
|
206
|
-
name: "
|
|
252
|
+
label: t("common.amount"),
|
|
253
|
+
name: "credit_amount",
|
|
254
|
+
align: "right",
|
|
207
255
|
options: {
|
|
256
|
+
setCellProps: () => ({ style: { width: "20%" } }),
|
|
257
|
+
customHeadLabelRender: () => /* @__PURE__ */ jsx(Box, { sx: { pr: 5 }, children: t("common.amount") }),
|
|
208
258
|
customBodyRenderLite: (_, index) => {
|
|
209
259
|
const item = data?.list[index];
|
|
210
|
-
const isGrant = item
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
const
|
|
214
|
-
|
|
260
|
+
const { isGrant, isDepleted, isExpired, isInactive } = getCreditActivityFlags(item);
|
|
261
|
+
const amount = isGrant ? item.amount : item.credit_amount;
|
|
262
|
+
const currency = item.paymentCurrency || item.currency;
|
|
263
|
+
const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
|
|
264
|
+
const displayAmount = formatCreditAmount(formatBNStr(amount, currency?.decimal || 0), unit);
|
|
265
|
+
if (!includeGrants) {
|
|
266
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { pr: 5 }, children: /* @__PURE__ */ jsx(Typography, { children: displayAmount }) });
|
|
267
|
+
}
|
|
268
|
+
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: [
|
|
269
|
+
/* @__PURE__ */ jsxs(
|
|
270
|
+
Typography,
|
|
271
|
+
{
|
|
272
|
+
sx: {
|
|
273
|
+
fontWeight: 500,
|
|
274
|
+
color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main",
|
|
275
|
+
whiteSpace: "nowrap"
|
|
276
|
+
},
|
|
277
|
+
children: [
|
|
278
|
+
isGrant ? "+" : "-",
|
|
279
|
+
" ",
|
|
280
|
+
displayAmount
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
),
|
|
284
|
+
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
|
|
285
|
+
] }) });
|
|
215
286
|
}
|
|
216
287
|
}
|
|
217
288
|
},
|
|
@@ -230,63 +301,21 @@ const TransactionsTable = React.memo((props) => {
|
|
|
230
301
|
}
|
|
231
302
|
}
|
|
232
303
|
] : [],
|
|
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
304
|
{
|
|
254
305
|
label: t("common.actions"),
|
|
255
306
|
name: "actions",
|
|
256
307
|
options: {
|
|
308
|
+
setCellProps: () => ({ style: { width: "25%" } }),
|
|
257
309
|
customBodyRenderLite: (_, index) => {
|
|
258
310
|
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
|
-
)
|
|
311
|
+
const { isGrant, isScheduled } = getCreditActivityFlags(item);
|
|
312
|
+
const isPaid = isGrant && item.category === "paid" && !isScheduled;
|
|
313
|
+
const subscriptionId = getSubscriptionId(item);
|
|
314
|
+
const invoiceId = isGrant ? getInvoiceId(item) : null;
|
|
315
|
+
const shouldShowSubscription = Boolean(subscriptionId) && (!isGrant || isScheduled || isPaid);
|
|
316
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 1, alignItems: "center", flexWrap: "nowrap" }, children: [
|
|
317
|
+
shouldShowSubscription && subscriptionId ? renderActionButton(t("common.viewSubscription"), (e) => openSubscription(e, subscriptionId)) : null,
|
|
318
|
+
isPaid && invoiceId ? renderActionButton(t("common.viewInvoice"), (e) => openInvoice(e, invoiceId)) : null
|
|
290
319
|
] });
|
|
291
320
|
}
|
|
292
321
|
}
|
|
@@ -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.",
|