@blocklet/payment-react
Version:
Reusable react components for payment kit v2
880 lines (878 loc) • 33.8 kB
JavaScript
import { jsx, jsxs } from "react/jsx-runtime";
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
import Toast from "@arcblock/ux/lib/Toast";
import { OpenInNewOutlined } from "@mui/icons-material";
import { Box, Button, CircularProgress, Stack, Typography, Tooltip, Avatar } from "@mui/material";
import { styled } from "@mui/system";
import { useInfiniteScroll, useRequest, useSetState } from "ahooks";
import React, { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import debounce from "lodash/debounce";
import { BN } from "@ocap/util";
import Status from "../../components/status.js";
import { usePaymentContext } from "../../contexts/payment.js";
import { useSubscription } from "../../hooks/subscription.js";
import api from "../../libs/api.js";
import StripePaymentAction from "../../components/stripe-payment-action.js";
import {
formatCreditAmount,
formatError,
formatToDate,
formatToDatetime,
formatTime,
formatExchangeRate,
getInvoiceDescriptionAndReason,
getInvoiceStatusColor,
getTxLink,
isCrossOrigin,
getUsdAmountFromTokenUnits,
formatUsdAmount,
formatAmount
} from "../../libs/util.js";
import Table from "../../components/table.js";
import { createLink, handleNavigation } from "../../libs/navigation.js";
const groupByDate = (items) => {
const grouped = {};
items.forEach((item) => {
const date = new Date(item.created_at).toLocaleDateString();
if (!grouped[date]) {
grouped[date] = [];
}
grouped[date]?.push(item);
});
return grouped;
};
const fetchData = (params = {}) => {
const search = new URLSearchParams();
const mergedParams = { include_quote: true, ...params };
Object.keys(mergedParams).forEach((key) => {
if (mergedParams[key]) {
search.set(key, String(mergedParams[key]));
}
});
return api.get(`/api/invoices?${search.toString()}`).then((res) => res.data);
};
const getInvoiceQuoteInfo = (invoice) => {
const lines = invoice.lines || [];
for (const line of lines) {
const quote = line.metadata?.quote;
if (quote?.exchange_rate) {
return quote;
}
}
return null;
};
const getInvoiceLink = (invoice, action) => {
if (invoice.id.startsWith("in_")) {
const path = `/customer/invoice/${invoice.id}${invoice.status === "uncollectible" && action ? `?action=${action}` : ""}`;
return {
connect: invoice.status === "uncollectible",
link: createLink(path)
};
}
return {
connect: false,
link: createLink(getTxLink(invoice.paymentMethod, invoice.metadata?.payment_details).link, true)
};
};
const linkStyle = {
cursor: "pointer"
};
const InvoiceTable = React.memo((props) => {
const {
pageSize,
target,
action,
onPay,
status,
customer_id,
currency_id,
subscription_id,
include_staking,
include_return_staking,
include_recovered_from,
onTableDataChange,
relatedSubscription
} = props;
const listKey = "invoice-table";
const { t, locale } = useLocaleContext();
const navigate = useNavigate();
const { getCurrency } = usePaymentContext();
const [search, setSearch] = useState({
pageSize: pageSize || 10,
page: 1
});
const {
loading,
data = { list: [], count: 0 },
refresh
} = useRequest(
() => fetchData({
...search,
status,
customer_id,
currency_id,
subscription_id,
include_staking,
include_return_staking,
include_recovered_from,
ignore_zero: true
}),
{
refreshDeps: [search, status, customer_id, currency_id, subscription_id, include_staking, include_recovered_from]
}
);
const prevData = useRef(data);
useEffect(() => {
if (onTableDataChange) {
onTableDataChange(data, prevData.current);
prevData.current = data;
}
}, [data]);
const subscription = useSubscription("events");
const debouncedHandleInvoicePaid = debounce(
async () => {
Toast.close();
Toast.success(t("payment.customer.invoice.paySuccess"));
await refresh();
},
1e3,
{
leading: false,
trailing: true,
maxWait: 5e3
}
);
useEffect(() => {
if (subscription && customer_id) {
subscription.on("invoice.paid", ({ response }) => {
if (response.customer_id === customer_id) {
debouncedHandleInvoicePaid();
}
});
}
}, [subscription]);
const handleLinkClick = (e, invoice) => {
const { link } = getInvoiceLink(invoice, action);
handleNavigation(e, link, navigate, { target: link.external ? "_blank" : target });
};
const handleRelatedSubscriptionClick = (e, invoice) => {
if (invoice.subscription_id) {
handleNavigation(e, createLink(`/customer/subscription/${invoice.subscription_id}`), navigate);
}
};
const columns = [
{
label: t("common.amount"),
name: "total",
width: 80,
align: "right",
options: {
customBodyRenderLite: (_, index) => {
const invoice = data?.list[index];
const isVoid = invoice.status === "void";
const quoteInfo = getInvoiceQuoteInfo(invoice);
const providers = quoteInfo?.providers || [];
const providerNames = providers.map((provider) => provider.provider_name).filter(Boolean);
const providerDisplay = providerNames.length > 0 ? providerNames.join(", ") : quoteInfo?.rate_provider_name || quoteInfo?.rate_provider_id || "\u2014";
const providerRates = quoteInfo?.providers?.map((provider) => {
const name = provider.provider_name || provider.provider_id || "\u2014";
return provider.rate ? `${name}` : name;
}).filter(Boolean) || [];
const rateTimestamp = quoteInfo?.rate_timestamp_ms ? formatTime(quoteInfo.rate_timestamp_ms) : "\u2014";
const formattedRate = formatExchangeRate(quoteInfo?.exchange_rate || null);
const rateLine = formattedRate ? (() => {
const currencyMap = {
USD: "$",
CNY: "\xA5"
};
const currencySymbol = currencyMap[quoteInfo?.base_currency];
return `1 ${invoice.paymentCurrency.symbol} \u2248 ${currencySymbol ? `${currencySymbol}${formattedRate}` : `${formattedRate} ${quoteInfo?.base_currency || "USD"}`}`;
})() : null;
let usdAmount = null;
if (quoteInfo?.base_amount) {
usdAmount = formatUsdAmount(quoteInfo.base_amount, locale);
} else if (quoteInfo?.exchange_rate && invoice.total) {
const calculatedUsd = getUsdAmountFromTokenUnits(
new BN(invoice.total),
invoice.paymentCurrency.decimal,
quoteInfo.exchange_rate
);
if (calculatedUsd) {
usdAmount = formatUsdAmount(calculatedUsd, locale);
}
}
const tooltipContent = quoteInfo ? /* @__PURE__ */ jsxs(Stack, { spacing: 0.5, sx: { p: 1 }, children: [
/* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", spacing: 2, children: [
/* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: [
t("payment.customer.invoice.quote.providers"),
":"
] }),
/* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.primary" }, children: (providerRates.length > 0 ? providerRates.join(", ") : providerDisplay) || "\u2014" })
] }),
rateLine && /* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", spacing: 2, children: [
/* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: [
t("payment.customer.invoice.quote.exchangeRate"),
":"
] }),
/* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.primary" }, children: rateLine })
] }),
/* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", spacing: 2, children: [
/* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: [
t("payment.customer.invoice.quote.rateTimestamp"),
":"
] }),
/* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.primary" }, children: rateTimestamp })
] })
] }) : null;
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: /* @__PURE__ */ jsxs(Stack, { spacing: 0.25, alignItems: "flex-end", children: [
/* @__PURE__ */ jsxs(Typography, { sx: isVoid ? { textDecoration: "line-through" } : {}, children: [
formatAmount(invoice.total, invoice.paymentCurrency.decimal),
"\xA0",
invoice.paymentCurrency.symbol
] }),
(usdAmount || rateLine) && /* @__PURE__ */ jsx(
Tooltip,
{
title: tooltipContent,
placement: "top",
arrow: true,
slotProps: {
tooltip: {
sx: {
backgroundColor: "background.paper",
boxShadow: 1
}
}
},
children: /* @__PURE__ */ jsx(Stack, { spacing: 0.25, alignItems: "flex-end", children: usdAmount && /* @__PURE__ */ jsxs(
Typography,
{
variant: "caption",
sx: {
color: "text.secondary",
fontSize: "0.75rem",
fontWeight: 400,
lineHeight: 1.2
},
children: [
"\u2248 $",
usdAmount
]
}
) })
}
)
] }) });
}
}
},
{
label: t("common.paymentMethod"),
name: "paymentMethod",
options: {
customBodyRenderLite: (_, index) => {
const invoice = data?.list[index];
return /* @__PURE__ */ jsxs(
Typography,
{
sx: { display: "flex", alignItems: "center", whiteSpace: "nowrap" },
onClick: (e) => handleLinkClick(e, invoice),
children: [
/* @__PURE__ */ jsx(Avatar, { src: invoice.paymentMethod.logo, sx: { width: 18, height: 18, mr: 1 } }),
invoice.paymentMethod.name
]
}
);
}
}
},
{
label: t("common.type"),
name: "billing_reason",
options: {
customBodyRenderLite: (_, index) => {
const invoice = data.list[index];
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: /* @__PURE__ */ jsx(Status, { label: getInvoiceDescriptionAndReason(invoice, locale)?.type }) });
}
}
},
{
label: t("payment.customer.invoice.invoiceNumber"),
name: "number",
options: {
customBodyRenderLite: (_, index) => {
const invoice = data?.list[index];
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: invoice?.number });
}
}
},
...relatedSubscription ? [
{
label: t("common.purchaseItems"),
name: "purchase_items",
options: {
customBodyRenderLite: (_, index) => {
const invoice = data?.list[index];
const lines = invoice.lines || [];
const items = lines.map((line) => {
const name = line.price?.product?.name || line.description;
if (!name) {
return null;
}
const quantity = Number(line.quantity || 0);
const label = Number.isFinite(quantity) && quantity > 1 ? `${name} x${quantity}` : name;
const lineKey = line.id || line.price?.id || line.price?.product?.id || line.description || name;
return { key: String(lineKey), label };
}).filter(Boolean);
if (items.length === 0 && invoice.subscription?.description) {
items.push({
key: `subscription-${invoice.subscription_id || invoice.id}`,
label: invoice.subscription.description
});
}
const isSubscription = Boolean(invoice.subscription_id);
const clickableProps = isSubscription ? { onClick: (e) => handleRelatedSubscriptionClick(e, invoice) } : {};
if (items.length === 0) {
return /* @__PURE__ */ jsx(Box, { sx: { color: "text.lighter" }, children: /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14 }, children: t("common.none") }) });
}
return /* @__PURE__ */ jsx(Box, { ...clickableProps, sx: isSubscription ? { cursor: "pointer" } : void 0, children: /* @__PURE__ */ jsx(Stack, { spacing: 0.5, children: items.map((item) => /* @__PURE__ */ jsx(
Typography,
{
sx: { fontSize: 14, color: isSubscription ? "text.link" : "text.primary" },
noWrap: true,
children: item.label
},
`${invoice.id}-item-${item.key}`
)) }) });
}
}
},
{
label: t("common.credits"),
name: "credits",
options: {
customBodyRenderLite: (_, index) => {
const invoice = data?.list[index];
const lines = invoice.lines || [];
const creditItems = [];
lines.forEach((line) => {
const lineKey = String(
line.id || line.price?.id || line.price?.product?.id || line.description || invoice.id
);
const pushCreditItem = (suffix, label) => {
creditItems.push({ key: `${lineKey}-${suffix}`, label });
};
const creditConfig = line.price?.metadata?.credit_config;
const creditAmount = Number(creditConfig?.credit_amount || 0);
if (creditAmount > 0) {
const quantity = Number(line.quantity || 0);
const totalAmount = creditAmount * (Number.isFinite(quantity) && quantity > 0 ? quantity : 1);
const currencySymbol = getCurrency(creditConfig?.currency_id)?.symbol || creditConfig?.currency_id || "Credits";
pushCreditItem("amount", `+${formatCreditAmount(String(totalAmount), currencySymbol)}`);
return;
}
const scheduleConfig = creditConfig?.schedule;
const scheduledAmount = Number(scheduleConfig?.amount_per_grant || 0);
if (scheduleConfig?.enabled && scheduleConfig?.delivery_mode === "schedule" && scheduledAmount > 0) {
const quantity = Number(line.quantity || 0);
const totalAmount = scheduledAmount * (Number.isFinite(quantity) && quantity > 0 ? quantity : 1);
const currencySymbol = getCurrency(creditConfig?.currency_id)?.symbol || creditConfig?.currency_id || "Credits";
pushCreditItem("schedule", `+${formatCreditAmount(String(totalAmount), currencySymbol)}`);
return;
}
const creditInfo = line.price?.credit;
const creditInfoAmount = Number(creditInfo?.amount || 0);
if (creditInfoAmount > 0) {
const currencySymbol = creditInfo.currency?.symbol || "Credits";
pushCreditItem("credit", `+${formatCreditAmount(String(creditInfoAmount), currencySymbol)}`);
}
});
if (creditItems.length === 0) {
return "-";
}
return /* @__PURE__ */ jsx(Stack, { spacing: 0.5, children: creditItems.map((creditItem) => /* @__PURE__ */ jsx(
Typography,
{
sx: { fontSize: 14, color: "success.main" },
noWrap: true,
children: creditItem.label
},
`${invoice.id}-credit-${creditItem.key}`
)) });
}
}
}
] : [],
{
label: t("common.updatedAt"),
name: "name",
options: {
customBodyRenderLite: (val, index) => {
const invoice = data?.list[index];
const periodTooltip = invoice.subscription_id && invoice.period_start && invoice.period_end ? `${t("common.billingPeriod")}: ${formatToDate(invoice.period_start * 1e3, locale, "YYYY-MM-DD HH:mm")} ~ ${formatToDate(invoice.period_end * 1e3, locale, "YYYY-MM-DD HH:mm")}` : "";
return /* @__PURE__ */ jsx(Tooltip, { title: periodTooltip, arrow: true, placement: "top-start", children: /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: formatToDate(
invoice.created_at,
locale,
relatedSubscription ? "YYYY-MM-DD HH:mm" : "YYYY-MM-DD HH:mm:ss"
) }) });
}
}
},
{
label: t("common.status"),
name: "status",
options: {
customBodyRenderLite: (val, index) => {
const invoice = data?.list[index];
const hidePay = invoice.billing_reason === "overdraft-protection";
const { connect } = getInvoiceLink(invoice, action);
const isVoid = invoice.status === "void";
if (action && !hidePay) {
if (connect) {
if (invoice.paymentMethod?.type === "stripe") {
return /* @__PURE__ */ jsx(
StripePaymentAction,
{
invoice,
paymentMethod: invoice.paymentMethod,
onSuccess: () => {
refresh();
},
children: (handlePay, paying) => /* @__PURE__ */ jsx(
Button,
{
variant: "text",
size: "small",
sx: { color: "text.link" },
disabled: paying,
onClick: (e) => {
e.preventDefault();
e.stopPropagation();
handlePay();
},
children: paying ? t("payment.checkout.processing") : t("payment.customer.invoice.pay")
}
)
}
);
}
return /* @__PURE__ */ jsx(Button, { variant: "text", size: "small", onClick: () => onPay(invoice.id), sx: { color: "text.link" }, children: t("payment.customer.invoice.pay") });
}
return /* @__PURE__ */ jsx(
Button,
{
component: "a",
variant: "text",
size: "small",
onClick: (e) => handleLinkClick(e, invoice),
sx: { color: "text.link" },
rel: "noreferrer",
children: t("payment.customer.invoice.pay")
}
);
}
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) }) });
}
}
}
];
const onTableChange = ({ page, rowsPerPage }) => {
if (search.pageSize !== rowsPerPage) {
setSearch((x) => ({ ...x, pageSize: rowsPerPage, page: 1 }));
} else if (search.page !== page + 1) {
setSearch((x) => ({ ...x, page: page + 1 }));
}
};
return /* @__PURE__ */ jsx(InvoiceTableRoot, { children: /* @__PURE__ */ jsx(
Table,
{
hasRowLink: true,
durable: `__${listKey}__`,
durableKeys: ["page", "rowsPerPage", "searchText"],
data: data.list,
columns,
options: {
count: data.count,
page: search.page - 1,
rowsPerPage: search.pageSize
},
loading,
onChange: onTableChange,
toolbar: false,
sx: { mt: 2 },
showMobile: false,
mobileTDFlexDirection: "row",
emptyNodeText: t("payment.customer.invoice.emptyList")
}
) });
});
const InvoiceTableRoot = styled(Box)`
@media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
.MuiTable-root > .MuiTableBody-root > .MuiTableRow-root > td.MuiTableCell-root {
> div {
width: fit-content;
flex: inherit;
font-size: 14px;
}
}
.invoice-summary {
padding-right: 20px;
}
}
`;
const InvoiceList = React.memo((props) => {
const {
customer_id,
subscription_id,
include_recovered_from,
currency_id,
include_staking,
status,
pageSize,
target,
action,
onPay,
onTableDataChange
} = props;
const size = pageSize || 10;
const subscription = useSubscription("events");
const { t, locale } = useLocaleContext();
const navigate = useNavigate();
const { data, loadMore, loadingMore, loading, reloadAsync } = useInfiniteScroll(
(d) => {
const page = d ? Math.ceil(d.list.length / size) + 1 : 1;
return fetchData({
page,
pageSize: size,
status,
customer_id,
currency_id,
subscription_id,
include_staking,
include_recovered_from,
ignore_zero: true
});
},
{
reloadDeps: [customer_id, subscription_id, status, include_staking, include_recovered_from]
}
);
const prevData = useRef(data);
useEffect(() => {
if (onTableDataChange) {
onTableDataChange(data, prevData.current);
prevData.current = data;
}
}, [data]);
const debouncedHandleInvoicePaid = debounce(
async () => {
Toast.close();
Toast.success(t("payment.customer.invoice.paySuccess"));
await reloadAsync();
},
1e3,
{
leading: false,
trailing: true,
maxWait: 5e3
}
);
useEffect(() => {
if (subscription && customer_id) {
subscription.on("invoice.paid", ({ response }) => {
if (response.customer_id === customer_id) {
debouncedHandleInvoicePaid();
}
});
}
}, [subscription]);
if (loading || !data) {
return /* @__PURE__ */ jsx(CircularProgress, {});
}
if (data && data.list.length === 0) {
if (data.subscription && ["active", "trialing"].includes(data.subscription.status)) {
return /* @__PURE__ */ jsx(
Typography,
{
sx: {
color: "text.secondary",
my: 0.5
},
children: t("payment.customer.invoice.next", { date: formatToDatetime(data.subscription.current_period_end * 1e3) })
}
);
}
return /* @__PURE__ */ jsx(
Typography,
{
sx: {
color: "text.secondary",
my: 0.5
},
children: t("payment.customer.invoice.empty")
}
);
}
const hasMore = data && data.list.length < data.count;
const grouped = groupByDate(data.list);
const handleLinkClick = (e, link) => {
handleNavigation(e, link, navigate, { target: link.external ? "_blank" : target });
};
return /* @__PURE__ */ jsxs(Root, { direction: "column", gap: 1, sx: { mt: 1 }, children: [
Object.entries(grouped).map(([date, invoices]) => /* @__PURE__ */ jsxs(Box, { children: [
/* @__PURE__ */ jsx(Typography, { sx: { fontWeight: "bold", color: "text.secondary", mt: 2, mb: 1 }, children: date }),
invoices.map((invoice) => {
const { link, connect } = getInvoiceLink(invoice, action);
const isVoid = invoice.status === "void";
const quoteInfo = getInvoiceQuoteInfo(invoice);
const formattedRate = formatExchangeRate(quoteInfo?.exchange_rate || null);
const rateLine = formattedRate ? `1 ${invoice.paymentCurrency.symbol} \u2248 ${formattedRate} ${quoteInfo?.base_currency || "USD"}` : null;
return /* @__PURE__ */ jsxs(
Stack,
{
direction: "row",
sx: {
gap: {
xs: 0.5,
sm: 1.5,
md: 3
},
alignItems: "center",
flexWrap: "nowrap",
my: 1
},
children: [
/* @__PURE__ */ jsx(
Box,
{
sx: {
flex: 2
},
children: /* @__PURE__ */ jsx(
"a",
{
href: link.url,
target: link.external ? "_blank" : target,
rel: "noreferrer",
onClick: (e) => handleLinkClick(e, link),
children: /* @__PURE__ */ jsxs(
Stack,
{
direction: "row",
spacing: 0.5,
sx: {
alignItems: "center"
},
children: [
/* @__PURE__ */ jsx(Typography, { component: "span", children: invoice.number }),
link.external && /* @__PURE__ */ jsx(
OpenInNewOutlined,
{
fontSize: "small",
sx: {
color: "text.secondary",
display: { xs: "none", md: "inline-flex" }
}
}
)
]
}
)
}
)
}
),
/* @__PURE__ */ jsxs(
Box,
{
sx: {
flex: 1,
textAlign: "right"
},
children: [
/* @__PURE__ */ jsxs(Typography, { sx: isVoid ? { textDecoration: "line-through" } : {}, children: [
formatAmount(invoice.total, invoice.paymentCurrency.decimal),
"\xA0",
invoice.paymentCurrency.symbol
] }),
rateLine && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary", display: "block" }, children: rateLine })
]
}
),
/* @__PURE__ */ jsx(
Tooltip,
{
title: invoice.subscription_id && invoice.period_start && invoice.period_end ? `${t("common.billingPeriod")}: ${formatToDate(invoice.period_start * 1e3, locale, "YYYY-MM-DD HH:mm")} ~ ${formatToDate(invoice.period_end * 1e3, locale, "YYYY-MM-DD HH:mm")}` : "",
arrow: true,
placement: "top-start",
children: /* @__PURE__ */ jsx(
Box,
{
sx: {
flex: 1,
textAlign: "right"
},
children: /* @__PURE__ */ jsx(Typography, { children: formatToDate(invoice.created_at, locale, "HH:mm:ss") })
}
)
}
),
!action && /* @__PURE__ */ jsx(
Box,
{
className: "invoice-description",
sx: {
flex: 2,
textAlign: "right",
display: { xs: "none", lg: "inline-flex" }
},
children: /* @__PURE__ */ jsx(Typography, { children: invoice.description || invoice.id })
}
),
/* @__PURE__ */ jsx(
Box,
{
sx: {
flex: 1,
textAlign: "right"
},
children: action ? connect ? invoice.paymentMethod?.type === "stripe" ? /* @__PURE__ */ jsx(
StripePaymentAction,
{
invoice,
paymentMethod: invoice.paymentMethod,
onSuccess: async () => {
await reloadAsync();
},
children: (handlePay, paying) => /* @__PURE__ */ jsx(
Button,
{
variant: "contained",
color: "primary",
size: "small",
sx: { whiteSpace: "nowrap" },
disabled: paying,
onClick: (e) => {
e.preventDefault();
e.stopPropagation();
handlePay();
},
children: paying ? t("payment.checkout.processing") : t("payment.customer.invoice.pay")
}
)
}
) : /* @__PURE__ */ jsx(
Button,
{
variant: "contained",
color: "primary",
size: "small",
onClick: () => onPay(invoice.id),
sx: { whiteSpace: "nowrap" },
children: t("payment.customer.invoice.pay")
}
) : /* @__PURE__ */ jsx(
Button,
{
component: "a",
variant: "contained",
size: "small",
onClick: (e) => handleLinkClick(e, link),
sx: { whiteSpace: "nowrap" },
rel: "noreferrer",
children: t("payment.customer.invoice.pay")
}
) : 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) })
}
)
]
},
invoice.id
);
})
] }, date)),
/* @__PURE__ */ jsxs(Box, { children: [
hasMore && /* @__PURE__ */ jsx(Button, { variant: "text", type: "button", color: "inherit", onClick: loadMore, disabled: loadingMore, children: loadingMore ? t("common.loadingMore", { resource: t("payment.customer.invoices") }) : t("common.loadMore", { resource: t("payment.customer.invoices") }) }),
!hasMore && data.count > size && /* @__PURE__ */ jsx(
Typography,
{
sx: {
color: "text.secondary"
},
children: t("common.noMore", { resource: t("payment.customer.invoices") })
}
)
] })
] });
});
export default function CustomerInvoiceList(rawProps) {
const props = Object.assign(
{
customer_id: "",
subscription_id: "",
currency_id: "",
include_staking: false,
include_recovered_from: false,
status: "open,paid,uncollectible",
pageSize: 10,
target: "_self",
action: "",
type: "list",
onTableDataChange: () => {
},
relatedSubscription: false
},
rawProps
);
const { action, type } = props;
const { t, locale } = useLocaleContext();
const { connect } = usePaymentContext();
const [state, setState] = useSetState({ paying: "" });
const onPay = (invoiceId) => {
if (state.paying) {
return;
}
setState({ paying: invoiceId });
connect.open({
action: "collect",
saveConnect: false,
locale,
useSocket: isCrossOrigin() === false,
messages: {
scan: "",
title: t(`payment.customer.invoice.${action || "pay"}`),
success: t(`payment.customer.invoice.${action || "pay"}Success`),
error: t(`payment.customer.invoice.${action || "pay"}Error`),
confirm: ""
},
extraParams: { invoiceId, action },
onSuccess: () => {
connect.close();
setState({ paying: "" });
},
onClose: () => {
connect.close();
setState({ paying: "" });
},
onError: (err) => {
setState({ paying: "" });
Toast.error(formatError(err));
}
});
};
if (type === "table") {
return /* @__PURE__ */ jsx(InvoiceTable, { ...props, onPay });
}
return /* @__PURE__ */ jsx(InvoiceList, { ...props, onPay });
}
const Root = styled(Stack)`
@media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
svg.MuiSvgIcon-root {
display: none !important;
}
}
a.MuiButton-root {
text-decoration: none !important;
}
`;