@blocklet/payment-react 1.18.14 → 1.18.16
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/components/over-due-invoice-payment.d.ts +20 -7
- package/es/components/over-due-invoice-payment.js +213 -74
- package/es/contexts/payment.js +14 -1
- package/es/history/invoice/list.js +28 -10
- package/es/locales/en.js +13 -4
- package/es/locales/zh.js +13 -4
- package/es/payment/form/address.js +63 -65
- package/lib/components/over-due-invoice-payment.d.ts +20 -7
- package/lib/components/over-due-invoice-payment.js +208 -75
- package/lib/contexts/payment.js +14 -1
- package/lib/history/invoice/list.js +50 -20
- package/lib/locales/en.js +13 -4
- package/lib/locales/zh.js +13 -4
- package/lib/payment/form/address.js +46 -50
- package/package.json +8 -7
- package/src/components/over-due-invoice-payment.tsx +267 -81
- package/src/contexts/payment.tsx +15 -1
- package/src/history/invoice/list.tsx +36 -15
- package/src/locales/en.tsx +11 -2
- package/src/locales/zh.tsx +11 -1
- package/src/payment/form/address.tsx +36 -38
|
@@ -4,19 +4,27 @@ type DialogProps = {
|
|
|
4
4
|
onClose?: () => void;
|
|
5
5
|
title?: string;
|
|
6
6
|
};
|
|
7
|
+
type DetailLinkOptions = {
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
onClick?: (e: React.MouseEvent) => void;
|
|
10
|
+
title?: string;
|
|
11
|
+
};
|
|
7
12
|
type Props = {
|
|
8
|
-
subscriptionId
|
|
13
|
+
subscriptionId?: string;
|
|
14
|
+
customerId?: string;
|
|
9
15
|
mode?: 'default' | 'custom';
|
|
10
|
-
onPaid?: (
|
|
16
|
+
onPaid?: (id: string, currencyId: string, type: 'subscription' | 'customer') => void;
|
|
11
17
|
dialogProps?: DialogProps;
|
|
12
|
-
|
|
18
|
+
detailLinkOptions?: DetailLinkOptions;
|
|
19
|
+
successToast?: boolean;
|
|
13
20
|
children?: (handlePay: (item: SummaryItem) => void, data: {
|
|
14
|
-
subscription
|
|
21
|
+
subscription?: Subscription;
|
|
15
22
|
summary: {
|
|
16
23
|
[key: string]: SummaryItem;
|
|
17
24
|
};
|
|
18
25
|
invoices: Invoice[];
|
|
19
|
-
|
|
26
|
+
subscriptionCount?: number;
|
|
27
|
+
detailUrl: string;
|
|
20
28
|
}) => React.ReactNode;
|
|
21
29
|
};
|
|
22
30
|
type SummaryItem = {
|
|
@@ -24,7 +32,7 @@ type SummaryItem = {
|
|
|
24
32
|
currency: PaymentCurrency;
|
|
25
33
|
method: PaymentMethod;
|
|
26
34
|
};
|
|
27
|
-
declare function OverdueInvoicePayment({ subscriptionId, mode, dialogProps, children, onPaid,
|
|
35
|
+
declare function OverdueInvoicePayment({ subscriptionId, customerId, mode, dialogProps, children, onPaid, detailLinkOptions, successToast, }: Props): import("react").JSX.Element | null;
|
|
28
36
|
declare namespace OverdueInvoicePayment {
|
|
29
37
|
var defaultProps: {
|
|
30
38
|
mode: string;
|
|
@@ -33,7 +41,12 @@ declare namespace OverdueInvoicePayment {
|
|
|
33
41
|
open: boolean;
|
|
34
42
|
};
|
|
35
43
|
children: null;
|
|
36
|
-
|
|
44
|
+
detailLinkOptions: {
|
|
45
|
+
enabled: boolean;
|
|
46
|
+
};
|
|
47
|
+
subscriptionId: undefined;
|
|
48
|
+
customerId: undefined;
|
|
49
|
+
successToast: boolean;
|
|
37
50
|
};
|
|
38
51
|
}
|
|
39
52
|
export default OverdueInvoicePayment;
|
|
@@ -1,27 +1,41 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { Button, Typography, Stack,
|
|
3
|
+
import { Button, Typography, Stack, Alert } from "@mui/material";
|
|
4
4
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
5
5
|
import Toast from "@arcblock/ux/lib/Toast";
|
|
6
6
|
import { joinURL } from "ufo";
|
|
7
7
|
import { useRequest } from "ahooks";
|
|
8
8
|
import { Dialog } from "@arcblock/ux";
|
|
9
|
+
import { CheckCircle as CheckCircleIcon } from "@mui/icons-material";
|
|
10
|
+
import debounce from "lodash/debounce";
|
|
9
11
|
import { usePaymentContext } from "../contexts/payment.js";
|
|
10
12
|
import { formatAmount, formatError, getPrefix } from "../libs/util.js";
|
|
11
13
|
import { useSubscription } from "../hooks/subscription.js";
|
|
12
14
|
import api from "../libs/api.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
import LoadingButton from "./loading-button.js";
|
|
16
|
+
const fetchOverdueInvoices = async (params) => {
|
|
17
|
+
if (!params.subscriptionId && !params.customerId) {
|
|
18
|
+
throw new Error("Either subscriptionId or customerId must be provided");
|
|
19
|
+
}
|
|
20
|
+
let url;
|
|
21
|
+
if (params.subscriptionId) {
|
|
22
|
+
url = `/api/subscriptions/${params.subscriptionId}/overdue/invoices`;
|
|
23
|
+
} else {
|
|
24
|
+
url = `/api/customers/${params.customerId}/overdue/invoices`;
|
|
25
|
+
}
|
|
26
|
+
const res = await api.get(url);
|
|
15
27
|
return res.data;
|
|
16
28
|
};
|
|
17
29
|
function OverdueInvoicePayment({
|
|
18
30
|
subscriptionId,
|
|
31
|
+
customerId,
|
|
19
32
|
mode = "default",
|
|
20
33
|
dialogProps = {},
|
|
21
34
|
children,
|
|
22
35
|
onPaid = () => {
|
|
23
36
|
},
|
|
24
|
-
|
|
37
|
+
detailLinkOptions = { enabled: true },
|
|
38
|
+
successToast = true
|
|
25
39
|
}) {
|
|
26
40
|
const { t } = useLocaleContext();
|
|
27
41
|
const { connect } = usePaymentContext();
|
|
@@ -29,42 +43,70 @@ function OverdueInvoicePayment({
|
|
|
29
43
|
const [payLoading, setPayLoading] = useState(false);
|
|
30
44
|
const [dialogOpen, setDialogOpen] = useState(dialogProps.open || false);
|
|
31
45
|
const [processedCurrencies, setProcessedCurrencies] = useState({});
|
|
46
|
+
const [paymentStatus, setPaymentStatus] = useState({});
|
|
47
|
+
const sourceType = subscriptionId ? "subscription" : "customer";
|
|
48
|
+
const sourceId = subscriptionId || customerId;
|
|
32
49
|
const {
|
|
33
50
|
data = {
|
|
34
|
-
subscription: {},
|
|
35
51
|
summary: {},
|
|
36
52
|
invoices: []
|
|
37
53
|
},
|
|
38
54
|
error,
|
|
39
55
|
loading,
|
|
40
56
|
runAsync: refresh
|
|
41
|
-
} = useRequest(() => fetchOverdueInvoices(subscriptionId)
|
|
42
|
-
|
|
57
|
+
} = useRequest(() => fetchOverdueInvoices({ subscriptionId, customerId }), {
|
|
58
|
+
ready: !!subscriptionId || !!customerId
|
|
59
|
+
});
|
|
60
|
+
const detailUrl = useMemo(() => {
|
|
61
|
+
if (subscriptionId) {
|
|
62
|
+
return joinURL(getPrefix(), `/customer/subscription/${subscriptionId}`);
|
|
63
|
+
}
|
|
64
|
+
if (customerId) {
|
|
65
|
+
return joinURL(getPrefix(), "/customer/invoice/past-due");
|
|
66
|
+
}
|
|
67
|
+
return "";
|
|
68
|
+
}, [subscriptionId, customerId]);
|
|
43
69
|
const summaryList = useMemo(() => {
|
|
44
70
|
if (!data?.summary) {
|
|
45
71
|
return [];
|
|
46
72
|
}
|
|
47
73
|
return Object.values(data.summary);
|
|
48
74
|
}, [data?.summary]);
|
|
75
|
+
const debouncedHandleInvoicePaid = debounce(
|
|
76
|
+
async (currencyId) => {
|
|
77
|
+
if (successToast) {
|
|
78
|
+
Toast.close();
|
|
79
|
+
Toast.success(t("payment.customer.invoice.paySuccess"));
|
|
80
|
+
}
|
|
81
|
+
setPayLoading(false);
|
|
82
|
+
const res = await refresh();
|
|
83
|
+
if (res.invoices?.length === 0) {
|
|
84
|
+
setDialogOpen(false);
|
|
85
|
+
onPaid(sourceId, currencyId, sourceType);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
1e3,
|
|
89
|
+
{
|
|
90
|
+
leading: false,
|
|
91
|
+
trailing: true,
|
|
92
|
+
maxWait: 5e3
|
|
93
|
+
}
|
|
94
|
+
);
|
|
49
95
|
const subscription = useSubscription("events");
|
|
50
96
|
useEffect(() => {
|
|
51
97
|
if (subscription) {
|
|
52
98
|
subscription.on("invoice.paid", ({ response }) => {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
setDialogOpen(false);
|
|
61
|
-
onPaid(subscriptionId, response.currency_id);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
99
|
+
const relevantId = subscriptionId || response.customer_id;
|
|
100
|
+
const uniqueKey = `${relevantId}-${response.currency_id}`;
|
|
101
|
+
if (subscriptionId && response.subscription_id === subscriptionId || customerId && response.customer_id === customerId) {
|
|
102
|
+
if (!processedCurrencies[uniqueKey]) {
|
|
103
|
+
setProcessedCurrencies((prev) => ({ ...prev, [uniqueKey]: 1 }));
|
|
104
|
+
debouncedHandleInvoicePaid(response.currency_id);
|
|
105
|
+
}
|
|
64
106
|
}
|
|
65
107
|
});
|
|
66
108
|
}
|
|
67
|
-
}, [subscription]);
|
|
109
|
+
}, [subscription, subscriptionId, customerId]);
|
|
68
110
|
const handlePay = (item) => {
|
|
69
111
|
const { currency, method } = item;
|
|
70
112
|
if (method.type === "stripe") {
|
|
@@ -76,15 +118,30 @@ function OverdueInvoicePayment({
|
|
|
76
118
|
}
|
|
77
119
|
setSelectCurrencyId(currency.id);
|
|
78
120
|
setPayLoading(true);
|
|
121
|
+
setPaymentStatus((prev) => ({
|
|
122
|
+
...prev,
|
|
123
|
+
[currency.id]: "idle"
|
|
124
|
+
}));
|
|
79
125
|
if (["arcblock", "ethereum", "base"].includes(method.type)) {
|
|
126
|
+
const extraParams = { currencyId: currency.id };
|
|
127
|
+
if (subscriptionId) {
|
|
128
|
+
extraParams.subscriptionId = subscriptionId;
|
|
129
|
+
} else if (customerId) {
|
|
130
|
+
extraParams.customerId = customerId;
|
|
131
|
+
}
|
|
80
132
|
connect.open({
|
|
81
133
|
containerEl: void 0,
|
|
82
134
|
saveConnect: false,
|
|
83
135
|
action: "collect-batch",
|
|
84
136
|
prefix: joinURL(getPrefix(), "/api/did"),
|
|
85
|
-
extraParams
|
|
137
|
+
extraParams,
|
|
86
138
|
onSuccess: () => {
|
|
87
139
|
connect.close();
|
|
140
|
+
setPayLoading(false);
|
|
141
|
+
setPaymentStatus((prev) => ({
|
|
142
|
+
...prev,
|
|
143
|
+
[currency.id]: "success"
|
|
144
|
+
}));
|
|
88
145
|
},
|
|
89
146
|
onClose: () => {
|
|
90
147
|
connect.close();
|
|
@@ -92,6 +149,10 @@ function OverdueInvoicePayment({
|
|
|
92
149
|
},
|
|
93
150
|
onError: (err) => {
|
|
94
151
|
Toast.error(formatError(err));
|
|
152
|
+
setPaymentStatus((prev) => ({
|
|
153
|
+
...prev,
|
|
154
|
+
[currency.id]: "error"
|
|
155
|
+
}));
|
|
95
156
|
setPayLoading(false);
|
|
96
157
|
}
|
|
97
158
|
});
|
|
@@ -102,7 +163,10 @@ function OverdueInvoicePayment({
|
|
|
102
163
|
dialogProps.onClose?.();
|
|
103
164
|
};
|
|
104
165
|
const handleViewDetailClick = (e) => {
|
|
105
|
-
if (
|
|
166
|
+
if (detailLinkOptions.onClick) {
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
detailLinkOptions.onClick(e);
|
|
169
|
+
} else if (!detailLinkOptions.enabled) {
|
|
106
170
|
e.preventDefault();
|
|
107
171
|
handleClose();
|
|
108
172
|
}
|
|
@@ -110,22 +174,103 @@ function OverdueInvoicePayment({
|
|
|
110
174
|
if (loading) {
|
|
111
175
|
return null;
|
|
112
176
|
}
|
|
113
|
-
const
|
|
114
|
-
|
|
177
|
+
const getDetailLinkText = () => {
|
|
178
|
+
if (detailLinkOptions.title) {
|
|
179
|
+
return detailLinkOptions.title;
|
|
180
|
+
}
|
|
181
|
+
if (subscriptionId) {
|
|
182
|
+
return t("payment.subscription.overdue.view");
|
|
183
|
+
}
|
|
184
|
+
return t("payment.customer.pastDue.view");
|
|
185
|
+
};
|
|
186
|
+
const renderPayButton = (item, primaryButton = true, props = {
|
|
187
|
+
variant: "contained"
|
|
188
|
+
}) => {
|
|
189
|
+
const { currency } = item;
|
|
190
|
+
const inProcess = payLoading && selectCurrencyId === currency.id;
|
|
191
|
+
const status = paymentStatus[currency.id] || "idle";
|
|
192
|
+
if (status === "success") {
|
|
193
|
+
return /* @__PURE__ */ jsx(
|
|
194
|
+
Button,
|
|
195
|
+
{
|
|
196
|
+
variant: props?.variant || "contained",
|
|
197
|
+
size: "small",
|
|
198
|
+
...primaryButton ? {} : {
|
|
199
|
+
color: "success",
|
|
200
|
+
startIcon: /* @__PURE__ */ jsx(CheckCircleIcon, {})
|
|
201
|
+
},
|
|
202
|
+
children: t("payment.subscription.overdue.paid")
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
if (status === "error") {
|
|
207
|
+
return /* @__PURE__ */ jsx(Button, { variant: "contained", size: "small", onClick: () => handlePay(item), ...props, children: t("payment.subscription.overdue.retry") });
|
|
208
|
+
}
|
|
115
209
|
if (item.method.type === "stripe") {
|
|
116
|
-
return /* @__PURE__ */ jsx(Button, { variant: "contained", color: "primary", onClick: () => window.open(
|
|
210
|
+
return /* @__PURE__ */ jsx(Button, { variant: "contained", color: "primary", onClick: () => window.open(detailUrl, "_blank"), ...props, children: t("payment.subscription.overdue.viewNow") });
|
|
211
|
+
}
|
|
212
|
+
return /* @__PURE__ */ jsx(
|
|
213
|
+
LoadingButton,
|
|
214
|
+
{
|
|
215
|
+
variant: "contained",
|
|
216
|
+
size: "small",
|
|
217
|
+
disabled: inProcess,
|
|
218
|
+
loading: inProcess,
|
|
219
|
+
onClick: () => handlePay(item),
|
|
220
|
+
...props,
|
|
221
|
+
children: t("payment.subscription.overdue.payNow")
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
const getOverdueTitle = () => {
|
|
226
|
+
if (subscriptionId && data.subscription) {
|
|
227
|
+
if (summaryList.length === 1) {
|
|
228
|
+
return t("payment.subscription.overdue.title", {
|
|
229
|
+
name: data.subscription?.description,
|
|
230
|
+
count: data.invoices?.length,
|
|
231
|
+
total: formatAmount(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
|
|
232
|
+
symbol: summaryList[0]?.currency?.symbol
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return t("payment.subscription.overdue.simpleTitle", {
|
|
236
|
+
name: data.subscription?.description,
|
|
237
|
+
count: data.invoices?.length
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
if (customerId) {
|
|
241
|
+
if (summaryList.length === 1) {
|
|
242
|
+
return t("payment.customer.overdue.title", {
|
|
243
|
+
subscriptionCount: data.subscriptionCount || 0,
|
|
244
|
+
count: data.invoices?.length,
|
|
245
|
+
total: formatAmount(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
|
|
246
|
+
symbol: summaryList[0]?.currency?.symbol
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return t("payment.customer.overdue.simpleTitle", {
|
|
250
|
+
subscriptionCount: data.subscriptionCount || 0,
|
|
251
|
+
count: data.invoices?.length
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
return "";
|
|
255
|
+
};
|
|
256
|
+
const getEmptyStateMessage = () => {
|
|
257
|
+
if (subscriptionId && data.subscription) {
|
|
258
|
+
return t("payment.subscription.overdue.empty", {
|
|
259
|
+
name: data.subscription?.description
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (customerId) {
|
|
263
|
+
return t("payment.customer.overdue.empty");
|
|
117
264
|
}
|
|
118
|
-
return
|
|
119
|
-
isPayLoading && /* @__PURE__ */ jsx(CircularProgress, { size: 14, sx: { mr: 1, color: "text.lighter" } }),
|
|
120
|
-
t("payment.subscription.overdue.payNow")
|
|
121
|
-
] });
|
|
265
|
+
return "";
|
|
122
266
|
};
|
|
123
267
|
if (mode === "custom" && children && typeof children === "function") {
|
|
124
268
|
return /* @__PURE__ */ jsx(Stack, { children: children(handlePay, {
|
|
125
269
|
subscription: data?.subscription,
|
|
126
|
-
subscriptionUrl,
|
|
127
270
|
summary: data?.summary,
|
|
128
|
-
invoices: data?.invoices
|
|
271
|
+
invoices: data?.invoices,
|
|
272
|
+
subscriptionCount: data?.subscriptionCount,
|
|
273
|
+
detailUrl
|
|
129
274
|
}) });
|
|
130
275
|
}
|
|
131
276
|
return /* @__PURE__ */ jsx(
|
|
@@ -141,34 +286,27 @@ function OverdueInvoicePayment({
|
|
|
141
286
|
onClose: handleClose,
|
|
142
287
|
children: error ? /* @__PURE__ */ jsx(Alert, { severity: "error", children: error.message }) : /* @__PURE__ */ jsxs(Stack, { gap: 1, children: [
|
|
143
288
|
summaryList.length === 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
144
|
-
/* @__PURE__ */ jsx(Alert, { severity: "success", children:
|
|
145
|
-
// @ts-ignore
|
|
146
|
-
name: data?.subscription?.description
|
|
147
|
-
}) }),
|
|
289
|
+
/* @__PURE__ */ jsx(Alert, { severity: "success", children: getEmptyStateMessage() }),
|
|
148
290
|
/* @__PURE__ */ jsx(Stack, { direction: "row", justifyContent: "flex-end", mt: 2, children: /* @__PURE__ */ jsx(Button, { variant: "outlined", color: "primary", onClick: handleClose, sx: { width: "fit-content" }, children: t("common.know") }) })
|
|
149
291
|
] }),
|
|
150
292
|
summaryList.length === 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
151
293
|
/* @__PURE__ */ jsxs(Typography, { color: "text.secondary", variant: "body1", children: [
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
style: { color: "var(--foregrounds-fg-interactive, 0086FF)" },
|
|
169
|
-
children: t("payment.subscription.overdue.view")
|
|
170
|
-
}
|
|
171
|
-
)
|
|
294
|
+
getOverdueTitle(),
|
|
295
|
+
detailLinkOptions.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
296
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
297
|
+
t("payment.subscription.overdue.description"),
|
|
298
|
+
/* @__PURE__ */ jsx(
|
|
299
|
+
"a",
|
|
300
|
+
{
|
|
301
|
+
href: detailUrl,
|
|
302
|
+
target: "_blank",
|
|
303
|
+
onClick: handleViewDetailClick,
|
|
304
|
+
rel: "noreferrer",
|
|
305
|
+
style: { color: "var(--foregrounds-fg-interactive, 0086FF)" },
|
|
306
|
+
children: getDetailLinkText()
|
|
307
|
+
}
|
|
308
|
+
)
|
|
309
|
+
] })
|
|
172
310
|
] }),
|
|
173
311
|
/* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "flex-end", gap: 2, mt: 2, children: [
|
|
174
312
|
/* @__PURE__ */ jsx(Button, { variant: "outlined", color: "primary", onClick: handleClose, children: t("common.cancel") }),
|
|
@@ -177,24 +315,22 @@ function OverdueInvoicePayment({
|
|
|
177
315
|
] }),
|
|
178
316
|
summaryList.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
179
317
|
/* @__PURE__ */ jsxs(Typography, { color: "text.secondary", variant: "body1", children: [
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
)
|
|
318
|
+
getOverdueTitle(),
|
|
319
|
+
detailLinkOptions.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
320
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
321
|
+
t("payment.subscription.overdue.description"),
|
|
322
|
+
/* @__PURE__ */ jsx(
|
|
323
|
+
"a",
|
|
324
|
+
{
|
|
325
|
+
href: detailUrl,
|
|
326
|
+
target: "_blank",
|
|
327
|
+
rel: "noreferrer",
|
|
328
|
+
onClick: handleViewDetailClick,
|
|
329
|
+
style: { color: "var(--foregrounds-fg-interactive, 0086FF)" },
|
|
330
|
+
children: getDetailLinkText()
|
|
331
|
+
}
|
|
332
|
+
)
|
|
333
|
+
] })
|
|
198
334
|
] }),
|
|
199
335
|
/* @__PURE__ */ jsx(Typography, { color: "text.secondary", variant: "body1", children: t("payment.subscription.overdue.list") }),
|
|
200
336
|
/* @__PURE__ */ jsx(Stack, { children: summaryList.map((item) => /* @__PURE__ */ jsxs(
|
|
@@ -217,7 +353,7 @@ function OverdueInvoicePayment({
|
|
|
217
353
|
total: formatAmount(item?.amount, item?.currency?.decimal),
|
|
218
354
|
currency: item?.currency?.symbol
|
|
219
355
|
}) }),
|
|
220
|
-
renderPayButton(item, {
|
|
356
|
+
renderPayButton(item, false, {
|
|
221
357
|
variant: "text",
|
|
222
358
|
sx: {
|
|
223
359
|
color: "text.link"
|
|
@@ -240,6 +376,9 @@ OverdueInvoicePayment.defaultProps = {
|
|
|
240
376
|
open: true
|
|
241
377
|
},
|
|
242
378
|
children: null,
|
|
243
|
-
|
|
379
|
+
detailLinkOptions: { enabled: true },
|
|
380
|
+
subscriptionId: void 0,
|
|
381
|
+
customerId: void 0,
|
|
382
|
+
successToast: true
|
|
244
383
|
};
|
|
245
384
|
export default OverdueInvoicePayment;
|
package/es/contexts/payment.js
CHANGED
|
@@ -5,6 +5,19 @@ import { createContext, useContext, useState } from "react";
|
|
|
5
5
|
import api from "../libs/api.js";
|
|
6
6
|
import { getPrefix } from "../libs/util.js";
|
|
7
7
|
import { CachedRequest } from "../libs/cached-request.js";
|
|
8
|
+
const formatData = (data) => {
|
|
9
|
+
if (!data) {
|
|
10
|
+
return {
|
|
11
|
+
paymentMethods: [],
|
|
12
|
+
baseCurrency: {}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
...data,
|
|
17
|
+
paymentMethods: data.paymentMethods || [],
|
|
18
|
+
baseCurrency: data.baseCurrency || {}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
8
21
|
const PaymentContext = createContext({ api });
|
|
9
22
|
const { Provider, Consumer } = PaymentContext;
|
|
10
23
|
const getSettings = (forceRefresh = false) => {
|
|
@@ -52,7 +65,7 @@ function PaymentProvider({ session, connect, children, baseUrl }) {
|
|
|
52
65
|
connect,
|
|
53
66
|
prefix,
|
|
54
67
|
livemode: !!livemode,
|
|
55
|
-
settings: data,
|
|
68
|
+
settings: formatData(data),
|
|
56
69
|
getCurrency: (currencyId) => getCurrency(currencyId, data?.paymentMethods || []),
|
|
57
70
|
getMethod: (methodId) => getMethod(methodId, data?.paymentMethods || []),
|
|
58
71
|
refresh: run,
|
|
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
3
3
|
import Toast from "@arcblock/ux/lib/Toast";
|
|
4
4
|
import { OpenInNewOutlined } from "@mui/icons-material";
|
|
5
|
-
import { Box, Button, CircularProgress,
|
|
5
|
+
import { Box, Button, CircularProgress, Stack, Typography, Tooltip } from "@mui/material";
|
|
6
6
|
import { styled } from "@mui/system";
|
|
7
7
|
import { useInfiniteScroll, useRequest, useSetState } from "ahooks";
|
|
8
8
|
import React, { useEffect, useRef, useState } from "react";
|
|
@@ -144,7 +144,8 @@ const InvoiceTable = React.memo((props) => {
|
|
|
144
144
|
options: {
|
|
145
145
|
customBodyRenderLite: (_, index) => {
|
|
146
146
|
const invoice = data?.list[index];
|
|
147
|
-
|
|
147
|
+
const isVoid = invoice.status === "void";
|
|
148
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: /* @__PURE__ */ jsxs(Typography, { sx: isVoid ? { textDecoration: "line-through" } : {}, children: [
|
|
148
149
|
formatBNStr(invoice.total, invoice.paymentCurrency.decimal),
|
|
149
150
|
"\xA0",
|
|
150
151
|
invoice.paymentCurrency.symbol
|
|
@@ -201,6 +202,7 @@ const InvoiceTable = React.memo((props) => {
|
|
|
201
202
|
const invoice = data?.list[index];
|
|
202
203
|
const hidePay = invoice.billing_reason === "overdraft-protection";
|
|
203
204
|
const { connect } = getInvoiceLink(invoice, action);
|
|
205
|
+
const isVoid = invoice.status === "void";
|
|
204
206
|
if (action && !hidePay) {
|
|
205
207
|
return connect ? /* @__PURE__ */ jsx(Button, { variant: "text", size: "small", onClick: () => onPay(invoice.id), sx: { color: "text.link" }, children: t("payment.customer.invoice.pay") }) : /* @__PURE__ */ jsx(
|
|
206
208
|
Button,
|
|
@@ -215,7 +217,7 @@ const InvoiceTable = React.memo((props) => {
|
|
|
215
217
|
}
|
|
216
218
|
);
|
|
217
219
|
}
|
|
218
|
-
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: /* @__PURE__ */ jsx(Status, { label: invoice.status, color: getInvoiceStatusColor(invoice.status) }) });
|
|
220
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: isVoid ? /* @__PURE__ */ jsx(Tooltip, { title: t("payment.customer.invoice.noPaymentRequired"), arrow: true, placement: "top", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(Status, { label: invoice.status, color: getInvoiceStatusColor(invoice.status) }) }) }) : /* @__PURE__ */ jsx(Status, { label: invoice.status, color: getInvoiceStatusColor(invoice.status) }) });
|
|
219
221
|
}
|
|
220
222
|
}
|
|
221
223
|
}
|
|
@@ -349,6 +351,7 @@ const InvoiceList = React.memo((props) => {
|
|
|
349
351
|
/* @__PURE__ */ jsx(Typography, { sx: { fontWeight: "bold", color: "text.secondary", mt: 2, mb: 1 }, children: date }),
|
|
350
352
|
invoices.map((invoice) => {
|
|
351
353
|
const { link, connect } = getInvoiceLink(invoice, action);
|
|
354
|
+
const isVoid = invoice.status === "void";
|
|
352
355
|
return /* @__PURE__ */ jsxs(
|
|
353
356
|
Stack,
|
|
354
357
|
{
|
|
@@ -371,17 +374,35 @@ const InvoiceList = React.memo((props) => {
|
|
|
371
374
|
onClick: (e) => handleLinkClick(e, link),
|
|
372
375
|
children: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
|
|
373
376
|
/* @__PURE__ */ jsx(Typography, { component: "span", children: invoice.number }),
|
|
374
|
-
link.external && /* @__PURE__ */ jsx(
|
|
377
|
+
link.external && /* @__PURE__ */ jsx(
|
|
378
|
+
OpenInNewOutlined,
|
|
379
|
+
{
|
|
380
|
+
fontSize: "small",
|
|
381
|
+
sx: {
|
|
382
|
+
color: "text.secondary",
|
|
383
|
+
display: { xs: "none", md: "inline-flex" }
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
)
|
|
375
387
|
] })
|
|
376
388
|
}
|
|
377
389
|
) }),
|
|
378
|
-
/* @__PURE__ */ jsx(Box, { flex: 1, textAlign: "right", children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
390
|
+
/* @__PURE__ */ jsx(Box, { flex: 1, textAlign: "right", children: /* @__PURE__ */ jsxs(Typography, { sx: isVoid ? { textDecoration: "line-through" } : {}, children: [
|
|
379
391
|
formatBNStr(invoice.total, invoice.paymentCurrency.decimal),
|
|
380
392
|
"\xA0",
|
|
381
393
|
invoice.paymentCurrency.symbol
|
|
382
394
|
] }) }),
|
|
383
395
|
/* @__PURE__ */ jsx(Box, { flex: 1, textAlign: "right", children: /* @__PURE__ */ jsx(Typography, { children: formatToDate(invoice.created_at, locale, "HH:mm:ss") }) }),
|
|
384
|
-
!action && /* @__PURE__ */ jsx(
|
|
396
|
+
!action && /* @__PURE__ */ jsx(
|
|
397
|
+
Box,
|
|
398
|
+
{
|
|
399
|
+
flex: 2,
|
|
400
|
+
className: "invoice-description",
|
|
401
|
+
textAlign: "right",
|
|
402
|
+
sx: { display: { xs: "none", lg: "inline-flex" } },
|
|
403
|
+
children: /* @__PURE__ */ jsx(Typography, { children: invoice.description || invoice.id })
|
|
404
|
+
}
|
|
405
|
+
),
|
|
385
406
|
/* @__PURE__ */ jsx(Box, { flex: 1, textAlign: "right", children: action ? connect ? /* @__PURE__ */ jsx(
|
|
386
407
|
Button,
|
|
387
408
|
{
|
|
@@ -403,7 +424,7 @@ const InvoiceList = React.memo((props) => {
|
|
|
403
424
|
rel: "noreferrer",
|
|
404
425
|
children: t("payment.customer.invoice.pay")
|
|
405
426
|
}
|
|
406
|
-
) : /* @__PURE__ */ jsx(Status, { label: invoice.status, color: getInvoiceStatusColor(invoice.status) }) })
|
|
427
|
+
) : isVoid ? /* @__PURE__ */ jsx(Tooltip, { title: t("payment.customer.invoice.noPaymentRequired"), arrow: true, placement: "top", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(Status, { label: invoice.status, color: getInvoiceStatusColor(invoice.status) }) }) }) : /* @__PURE__ */ jsx(Status, { label: invoice.status, color: getInvoiceStatusColor(invoice.status) }) })
|
|
407
428
|
]
|
|
408
429
|
},
|
|
409
430
|
invoice.id
|
|
@@ -472,9 +493,6 @@ CustomerInvoiceList.defaultProps = {
|
|
|
472
493
|
};
|
|
473
494
|
const Root = styled(Stack)`
|
|
474
495
|
@media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
|
|
475
|
-
.invoice-description {
|
|
476
|
-
display: none !important;
|
|
477
|
-
}
|
|
478
496
|
svg.MuiSvgIcon-root {
|
|
479
497
|
display: none !important;
|
|
480
498
|
}
|
package/es/locales/en.js
CHANGED
|
@@ -237,12 +237,13 @@ export default flat({
|
|
|
237
237
|
pastDue: {
|
|
238
238
|
button: "Pay",
|
|
239
239
|
invoices: "Past Due Invoices",
|
|
240
|
-
warning: "Past due invoices need to be paid immediately, otherwise you can not make new purchases anymore.
|
|
240
|
+
warning: "Past due invoices need to be paid immediately, otherwise you can not make new purchases anymore.",
|
|
241
241
|
alert: {
|
|
242
242
|
title: "You have unpaid invoices",
|
|
243
243
|
description: "Seems you have unpaid invoices from previous subscriptions, new purchases are not allowed unless you have paid all past due invoices.",
|
|
244
244
|
confirm: "Pay Now"
|
|
245
|
-
}
|
|
245
|
+
},
|
|
246
|
+
view: "View Due Invoices"
|
|
246
247
|
},
|
|
247
248
|
recover: {
|
|
248
249
|
button: "Resume Subscription",
|
|
@@ -291,7 +292,8 @@ export default flat({
|
|
|
291
292
|
empty: "There are no invoices",
|
|
292
293
|
next: "No invoices yet, next invoice will be generated on {date}",
|
|
293
294
|
invoiceNumber: "Invoice Number",
|
|
294
|
-
emptyList: "No Invoice"
|
|
295
|
+
emptyList: "No Invoice",
|
|
296
|
+
noPaymentRequired: "No Payment Required"
|
|
295
297
|
},
|
|
296
298
|
payment: {
|
|
297
299
|
empty: "There are no payments",
|
|
@@ -312,6 +314,11 @@ export default flat({
|
|
|
312
314
|
changePayment: "Change payment method",
|
|
313
315
|
trialLeft: "Trail Left",
|
|
314
316
|
owner: "Subscription Owner"
|
|
317
|
+
},
|
|
318
|
+
overdue: {
|
|
319
|
+
title: "You have {count} due invoices for {subscriptionCount} subscriptions, totaling {total} {symbol}. Please pay immediately to avoid service disruption.",
|
|
320
|
+
simpleTitle: "You have {count} due invoices. Please pay now to ensure uninterrupted service.",
|
|
321
|
+
empty: "Great! You have no due invoices."
|
|
315
322
|
}
|
|
316
323
|
},
|
|
317
324
|
invoice: {
|
|
@@ -351,7 +358,9 @@ export default flat({
|
|
|
351
358
|
pastDue: "Past Due Invoices",
|
|
352
359
|
description: "If you have any questions, you can choose ",
|
|
353
360
|
list: "Past Due Invoices:",
|
|
354
|
-
empty: "There are no overdue invoices for your subscription {name}."
|
|
361
|
+
empty: "There are no overdue invoices for your subscription {name}.",
|
|
362
|
+
retry: "Retry",
|
|
363
|
+
paid: "Paid"
|
|
355
364
|
}
|
|
356
365
|
}
|
|
357
366
|
},
|