@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.
@@ -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: string;
13
+ subscriptionId?: string;
14
+ customerId?: string;
9
15
  mode?: 'default' | 'custom';
10
- onPaid?: (subscriptionId: string, currencyId: string) => void;
16
+ onPaid?: (id: string, currencyId: string, type: 'subscription' | 'customer') => void;
11
17
  dialogProps?: DialogProps;
12
- inSubscriptionDetail?: boolean;
18
+ detailLinkOptions?: DetailLinkOptions;
19
+ successToast?: boolean;
13
20
  children?: (handlePay: (item: SummaryItem) => void, data: {
14
- subscription: Subscription;
21
+ subscription?: Subscription;
15
22
  summary: {
16
23
  [key: string]: SummaryItem;
17
24
  };
18
25
  invoices: Invoice[];
19
- subscriptionUrl: string;
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, inSubscriptionDetail, }: Props): import("react").JSX.Element | null;
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
- inSubscriptionDetail: boolean;
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, CircularProgress, Alert } from "@mui/material";
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
- const fetchOverdueInvoices = async (subscriptionId) => {
14
- const res = await api.get(`/api/subscriptions/${subscriptionId}/overdue/invoices`);
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
- inSubscriptionDetail = false
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
- const subscriptionUrl = joinURL(getPrefix(), `/customer/subscription/${subscriptionId}`);
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 uniqueKey = `${response.subscription_id}-${response.currency_id}`;
54
- if (response.subscription_id === subscriptionId && !processedCurrencies[uniqueKey]) {
55
- Toast.success(t("payment.customer.invoice.paySuccess"));
56
- setPayLoading(false);
57
- setProcessedCurrencies({ ...processedCurrencies, [uniqueKey]: 1 });
58
- refresh().then((res) => {
59
- if (res.invoices?.length === 0) {
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: { currencyId: currency.id, subscriptionId },
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 (inSubscriptionDetail) {
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 renderPayButton = (item, props) => {
114
- const isPayLoading = payLoading && item.currency.id === selectCurrencyId;
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(subscriptionUrl, "_blank"), ...props, children: t("payment.subscription.overdue.viewNow") });
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 /* @__PURE__ */ jsxs(Button, { variant: "contained", color: "primary", onClick: () => handlePay(item), ...props, disabled: isPayLoading, children: [
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: t("payment.subscription.overdue.empty", {
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
- t("payment.subscription.overdue.title", {
153
- // @ts-ignore
154
- name: data?.subscription?.description,
155
- count: data?.invoices?.length,
156
- total: formatAmount(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
157
- symbol: summaryList[0]?.currency?.symbol
158
- }),
159
- /* @__PURE__ */ jsx("br", {}),
160
- t("payment.subscription.overdue.description"),
161
- /* @__PURE__ */ jsx(
162
- "a",
163
- {
164
- href: subscriptionUrl,
165
- target: "_blank",
166
- onClick: handleViewDetailClick,
167
- rel: "noreferrer",
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
- t("payment.subscription.overdue.simpleTitle", {
181
- // @ts-ignore
182
- name: data?.subscription?.description,
183
- count: data?.invoices?.length
184
- }),
185
- /* @__PURE__ */ jsx("br", {}),
186
- t("payment.subscription.overdue.description"),
187
- /* @__PURE__ */ jsx(
188
- "a",
189
- {
190
- href: subscriptionUrl,
191
- target: "_blank",
192
- rel: "noreferrer",
193
- onClick: handleViewDetailClick,
194
- style: { color: "var(--foregrounds-fg-interactive, 0086FF)" },
195
- children: t("payment.subscription.overdue.view")
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
- inSubscriptionDetail: false
379
+ detailLinkOptions: { enabled: true },
380
+ subscriptionId: void 0,
381
+ customerId: void 0,
382
+ successToast: true
244
383
  };
245
384
  export default OverdueInvoicePayment;
@@ -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, Hidden, Stack, Typography } from "@mui/material";
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
- return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: /* @__PURE__ */ jsxs(Typography, { children: [
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(Hidden, { mdDown: true, children: /* @__PURE__ */ jsx(OpenInNewOutlined, { fontSize: "small", sx: { color: "text.secondary" } }) })
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(Hidden, { mdDown: true, children: /* @__PURE__ */ jsx(Box, { flex: 2, className: "invoice-description", textAlign: "right", children: /* @__PURE__ */ jsx(Typography, { children: invoice.description || invoice.id }) }) }),
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. Please pay these invoices one by one.",
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
  },