@blocklet/payment-react
Version:
Reusable react components for payment kit v2
382 lines (381 loc) • 12.2 kB
JavaScript
import { jsx, jsxs } from "react/jsx-runtime";
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
import { Box, Typography, Grid, Stack, Link, Button, Popover } from "@mui/material";
import { useRequest } from "ahooks";
import { useNavigate } from "react-router-dom";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { styled } from "@mui/system";
import { joinURL } from "ufo";
import DateRangePicker from "../../components/date-range-picker.js";
import { formatBNStr, formatToDate, getPrefix } from "../../libs/util.js";
import { usePaymentContext } from "../../contexts/payment.js";
import api from "../../libs/api.js";
import Table from "../../components/table.js";
import { createLink, handleNavigation } from "../../libs/navigation.js";
import SourceDataViewer from "../../components/source-data-viewer.js";
const fetchData = (params = {}) => {
const search = new URLSearchParams();
Object.keys(params).forEach((key) => {
if (params[key]) {
search.set(key, String(params[key]));
}
});
return api.get(`/api/credit-transactions?${search.toString()}`).then((res) => res.data);
};
const getGrantDetailLink = (grantId, inDashboard) => {
let path = `/customer/credit-grant/${grantId}`;
if (inDashboard) {
path = `/admin/customers/${grantId}`;
}
return {
link: createLink(path),
connect: false
};
};
const getInvoiceDetailLink = (invoiceId, inDashboard) => {
let path = `/customer/invoice/${invoiceId}`;
if (inDashboard) {
path = `/admin/billing/${invoiceId}`;
}
return {
link: createLink(path),
connect: false
};
};
const TransactionsTable = React.memo((props) => {
const {
pageSize,
customer_id,
subscription_id,
credit_grant_id,
onTableDataChange,
showAdminColumns = false,
showTimeFilter = false,
includeGrants = false,
source,
mode = "portal"
} = props;
const listKey = "credit-transactions-table";
const { t, locale } = useLocaleContext();
const { session } = usePaymentContext();
const isAdmin = ["owner", "admin"].includes(session?.user?.role || "");
const navigate = useNavigate();
const effectiveCustomerId = customer_id || session?.user?.did;
const [search, setSearch] = useState({
pageSize: pageSize || 10,
page: 1
});
const [filters, setFilters] = useState({
start: void 0,
end: void 0
});
const [sourceDataPopover, setSourceDataPopover] = useState({ anchorEl: null, data: null });
const handleDateRangeChange = useCallback((newValue) => {
setFilters(newValue);
setSearch((prev) => ({
...prev,
page: 1,
start: newValue.start || void 0,
end: newValue.end || void 0
}));
}, []);
const { loading, data = { list: [], count: 0 } } = useRequest(
() => fetchData({
...search,
customer_id: effectiveCustomerId,
subscription_id,
credit_grant_id,
source,
include_grants: includeGrants
}),
{
refreshDeps: [search, effectiveCustomerId, subscription_id, credit_grant_id, source, includeGrants]
}
);
useEffect(() => {
if (showTimeFilter && !search.start && !search.end) {
handleDateRangeChange(filters);
}
}, [showTimeFilter, handleDateRangeChange, search.start, search.end, filters]);
const prevData = useRef(data);
useEffect(() => {
if (onTableDataChange) {
onTableDataChange(data, prevData.current);
prevData.current = data;
}
}, [data]);
const columns = [
{
label: t("common.amount"),
name: "credit_amount",
align: "right",
options: {
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
const isGrant = item.activity_type === "grant";
const amount = isGrant ? item.amount : item.credit_amount;
const currency = item.paymentCurrency || item.currency;
const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
const displayAmount = formatBNStr(amount, currency?.decimal || 0);
if (!includeGrants) {
return /* @__PURE__ */ jsxs(Typography, { children: [
displayAmount,
" ",
unit
] });
}
return /* @__PURE__ */ jsxs(
Typography,
{
sx: {
color: isGrant ? "success.main" : "error.main"
},
children: [
isGrant ? "+" : "-",
" ",
displayAmount,
" ",
unit
]
}
);
}
}
},
{
label: t("common.creditGrant"),
name: "credit_grant",
options: {
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
const isGrant = item.activity_type === "grant";
const grantName = isGrant ? item.name : item.creditGrant.name;
const grantId = isGrant ? item.id : item.credit_grant_id;
return /* @__PURE__ */ jsx(
Stack,
{
direction: "row",
spacing: 1,
onClick: (e) => {
const link = getGrantDetailLink(grantId, isAdmin && mode === "dashboard");
handleNavigation(e, link.link, navigate);
},
sx: {
alignItems: "center"
},
children: /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { cursor: "pointer" }, children: grantName || `Grant ${grantId.slice(-6)}` })
}
);
}
}
},
{
label: t("common.description"),
name: "description",
options: {
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
const isGrant = item.activity_type === "grant";
const description = isGrant ? item.name || item.description || "Credit Granted" : item.subscription?.description || item.description || `${item.meter_event_name} usage`;
return /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 400 }, children: description });
}
}
},
...showAdminColumns && isAdmin ? [
{
label: t("common.meterEvent"),
name: "meter_event",
options: {
customBodyRenderLite: (_, index) => {
const transaction = data?.list[index];
if (!transaction.meter) {
return /* @__PURE__ */ jsx(Typography, { variant: "body2", children: "-" });
}
return /* @__PURE__ */ jsx(Link, { href: joinURL(getPrefix(), `/admin/billing/${transaction.meter.id}`), children: /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.link" }, children: transaction.meter.event_name }) });
}
}
}
] : [],
{
label: t("common.date"),
name: "created_at",
options: {
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
return /* @__PURE__ */ jsx(
Typography,
{
variant: "body2",
sx: {
color: "text.secondary",
fontSize: "0.875rem"
},
children: formatToDate(item.created_at, locale, "YYYY-MM-DD HH:mm")
}
);
}
}
},
{
label: t("common.actions"),
name: "actions",
options: {
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
const isGrant = item.activity_type === "grant";
const invoiceId = isGrant ? item.metadata?.invoice_id : null;
const sourceData = !isGrant && item.meterEvent?.source_data;
return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 1, alignItems: "center" }, children: [
isGrant && invoiceId && /* @__PURE__ */ jsx(
Button,
{
variant: "text",
size: "small",
color: "primary",
onClick: (e) => {
e.preventDefault();
const link = getInvoiceDetailLink(invoiceId, isAdmin && mode === "dashboard");
handleNavigation(e, link.link, navigate);
},
children: t("common.viewInvoice")
}
),
sourceData && /* @__PURE__ */ jsx(
Button,
{
variant: "text",
size: "small",
color: "primary",
onClick: (e) => {
e.preventDefault();
setSourceDataPopover({
anchorEl: e.currentTarget,
data: sourceData
});
},
children: t("common.viewSourceData")
}
)
] });
}
}
}
].filter(Boolean);
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__ */ jsxs(TableRoot, { children: [
showTimeFilter && /* @__PURE__ */ jsx(Box, { sx: { my: 2 }, children: /* @__PURE__ */ jsx(Box, { sx: { mt: 2 }, children: /* @__PURE__ */ jsx(
Grid,
{
container: true,
spacing: 2,
sx: {
alignItems: "center"
},
children: /* @__PURE__ */ jsx(
Grid,
{
size: {
xs: 12,
sm: 6,
md: 4
},
children: /* @__PURE__ */ jsx(DateRangePicker, { value: filters, onChange: handleDateRangeChange, size: "small", fullWidth: true })
}
)
}
) }) }),
/* @__PURE__ */ jsx(
Table,
{
hasRowLink: true,
durable: `__${listKey}__`,
durableKeys: ["page", "rowsPerPage"],
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("admin.creditTransactions.noTransactions")
}
),
/* @__PURE__ */ jsx(
Popover,
{
open: Boolean(sourceDataPopover.anchorEl),
anchorEl: sourceDataPopover.anchorEl,
onClose: () => setSourceDataPopover({ anchorEl: null, data: null }),
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
},
transformOrigin: {
vertical: "top",
horizontal: "left"
},
slotProps: {
paper: {
sx: {
minWidth: {
xs: 0,
md: 320
},
maxHeight: 450,
p: {
xs: 1,
md: 3
}
}
}
},
children: sourceDataPopover.data && /* @__PURE__ */ jsx(SourceDataViewer, { data: sourceDataPopover.data, showGroups: true })
}
)
] });
});
const TableRoot = 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;
}
}
`;
export default function CreditTransactionsList(rawProps) {
const props = Object.assign(
{
customer_id: "",
subscription_id: "",
credit_grant_id: "",
source: "",
pageSize: 10,
onTableDataChange: () => {
},
showAdminColumns: false,
showTimeFilter: false,
includeGrants: false,
mode: "portal"
},
rawProps
);
return /* @__PURE__ */ jsx(TransactionsTable, { ...props });
}