@blocklet/payment-react
Version:
Reusable react components for payment kit v2
556 lines (555 loc) • 18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
module.exports = CreditTransactionsList;
var _jsxRuntime = require("react/jsx-runtime");
var _context = require("@arcblock/ux/lib/Locale/context");
var _material = require("@mui/material");
var _ahooks = require("ahooks");
var _reactRouterDom = require("react-router-dom");
var _react = _interopRequireWildcard(require("react"));
var _system = require("@mui/system");
var _ufo = require("ufo");
var _dateRangePicker = _interopRequireDefault(require("../../components/date-range-picker"));
var _util = require("../../libs/util");
var _payment = require("../../contexts/payment");
var _api = _interopRequireDefault(require("../../libs/api"));
var _table = _interopRequireDefault(require("../../components/table"));
var _navigation = require("../../libs/navigation");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const fetchData = (params = {}) => {
const search = new URLSearchParams();
Object.keys(params).forEach(key => {
if (params[key]) {
search.set(key, String(params[key]));
}
});
return _api.default.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: (0, _navigation.createLink)(path),
connect: false
};
};
const getInvoiceDetailLink = (invoiceId, inDashboard) => {
let path = `/customer/invoice/${invoiceId}`;
if (inDashboard) {
path = `/admin/billing/${invoiceId}`;
}
return {
link: (0, _navigation.createLink)(path),
connect: false
};
};
const getSubscriptionDetailLink = (subscriptionId, inDashboard) => {
let path = `/customer/subscription/${subscriptionId}`;
if (inDashboard) {
path = `/admin/billing/${subscriptionId}`;
}
return {
link: (0, _navigation.createLink)(path),
connect: false
};
};
const getCreditTransactionDetailLink = transactionId => {
const path = `/customer/credit-transaction/${transactionId}`;
return {
link: (0, _navigation.createLink)(path),
connect: false
};
};
const getMeterEventDetailLink = meterEventId => {
const path = `/admin/billing/${meterEventId}`;
return {
link: (0, _navigation.createLink)(path),
connect: false
};
};
const getSubscriptionId = item => item.metadata?.subscription_id || item.subscription_id || item.invoice?.subscription_id;
const getInvoiceId = item => item.metadata?.invoice_id || item.invoice?.id;
const getMeterEventId = item => item.source || item.metadata?.meter_event_id;
const getCreditActivityFlags = item => {
const isGrant = item.activity_type === "grant";
const isScheduled = isGrant && item.metadata?.delivery_mode === "schedule";
const isDepleted = isGrant && item.status === "depleted";
const isExpired = isGrant && (item.status === "expired" || item.status === "voided");
const isInactive = isDepleted || isExpired;
return {
isGrant,
isScheduled,
isDepleted,
isExpired,
isInactive
};
};
const getTransactionDetailLink = (item, inDashboard) => {
if (item.activity_type === "grant") {
const invoiceId = getInvoiceId(item);
if (invoiceId) {
return getInvoiceDetailLink(invoiceId, inDashboard);
}
return getGrantDetailLink(item.id, inDashboard);
}
if (!inDashboard) {
return getCreditTransactionDetailLink(item.id);
}
const meterEventId = getMeterEventId(item);
if (!meterEventId) {
return null;
}
return getMeterEventDetailLink(meterEventId);
};
const getTransactionDescription = (item, t) => {
const {
isGrant,
isScheduled,
isInactive
} = getCreditActivityFlags(item);
const isPaid = isGrant && item.category === "paid" && (!isScheduled || item.metadata?.schedule_seq === 1);
if (!isGrant) {
const secondLine = item.metadata?.is_repayment ? t("common.creditActivity.repayment") : item.description || "";
return {
isGrant,
isInactive,
activityType: t("common.creditActivity.consumption"),
secondLine
};
}
if (isPaid) {
let secondLine = item.description || "";
if (item.invoice?.total && item.invoice?.paymentCurrency) {
const invoiceCurrency = item.invoice.paymentCurrency;
const paidAmount = (0, _util.formatCreditAmount)((0, _util.formatBNStr)(item.invoice.total, invoiceCurrency.decimal || 0), invoiceCurrency.symbol || "");
secondLine = t("common.creditActivity.paidAmount", {
amount: paidAmount
});
}
return {
isGrant,
isInactive,
activityType: t("common.creditActivity.paidGrant"),
secondLine
};
}
if (isScheduled) {
return {
isGrant,
isInactive,
activityType: t("common.creditActivity.resetGrant"),
secondLine: item.description || ""
};
}
return {
isGrant,
isInactive,
activityType: t("common.creditActivity.promotionalGrant"),
secondLine: item.description || ""
};
};
const TransactionsTable = _react.default.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
} = (0, _context.useLocaleContext)();
const {
session
} = (0, _payment.usePaymentContext)();
const isAdmin = ["owner", "admin"].includes(session?.user?.role || "");
const isDashboard = isAdmin && mode === "dashboard";
const navigate = (0, _reactRouterDom.useNavigate)();
const effectiveCustomerId = customer_id || session?.user?.did;
const [search, setSearch] = (0, _react.useState)({
pageSize: pageSize || 10,
page: 1
});
const [filters, setFilters] = (0, _react.useState)({
start: void 0,
end: void 0
});
const handleDateRangeChange = newValue => {
setFilters(newValue);
setSearch(prev => ({
...prev,
page: 1,
start: newValue.start || void 0,
end: newValue.end || void 0
}));
};
const {
loading,
data = {
list: [],
count: 0
}
} = (0, _ahooks.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]
});
(0, _react.useEffect)(() => {
if (showTimeFilter && !search.start && !search.end) {
setSearch(prev => ({
...prev,
page: 1,
start: filters.start || void 0,
end: filters.end || void 0
}));
}
}, [showTimeFilter, search.start, search.end, filters.start, filters.end]);
const prevData = (0, _react.useRef)(data);
(0, _react.useEffect)(() => {
if (onTableDataChange) {
onTableDataChange(data, prevData.current);
prevData.current = data;
}
}, [data]);
const handleTransactionClick = (e, item) => {
const detail = getTransactionDetailLink(item, isDashboard);
if (!detail) {
return;
}
(0, _navigation.handleNavigation)(e, detail.link, navigate, {
target: detail.link.external ? "_blank" : "_self"
});
};
const openSubscription = (e, subscriptionId) => {
e.preventDefault();
const link = getSubscriptionDetailLink(subscriptionId, isDashboard);
(0, _navigation.handleNavigation)(e, link.link, navigate);
};
const openInvoice = (e, invoiceId) => {
e.preventDefault();
const link = getInvoiceDetailLink(invoiceId, isDashboard);
(0, _navigation.handleNavigation)(e, link.link, navigate);
};
const renderActionButton = (label, onClick) => /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
variant: "text",
size: "small",
color: "primary",
sx: {
whiteSpace: "nowrap"
},
onClick,
children: label
});
const columns = [{
label: t("common.date"),
name: "created_at",
options: {
setCellProps: () => ({
style: {
width: "25%"
}
}),
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
onClick: e => handleTransactionClick(e, item),
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "body2",
sx: {
fontSize: "0.875rem"
},
children: (0, _util.formatToDate)(item.created_at, locale, "YYYY-MM-DD HH:mm")
})
});
}
}
}, {
label: t("common.description"),
name: "description",
options: {
setCellProps: () => ({
style: {
width: "25%"
}
}),
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
const {
activityType,
secondLine,
isInactive,
isGrant
} = getTransactionDescription(item, t);
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
onClick: e => handleTransactionClick(e, item),
sx: {
cursor: "pointer"
},
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "column",
spacing: 0.25,
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "body2",
sx: {
color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main"
},
children: activityType
}), secondLine && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "caption",
sx: {
color: "text.secondary"
},
children: secondLine
})]
})
});
}
}
}, {
label: t("common.amount"),
name: "credit_amount",
align: "right",
options: {
setCellProps: () => ({
style: {
width: "20%"
}
}),
customHeadLabelRender: () => /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
sx: {
pr: 5
},
children: t("common.amount")
}),
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
const {
isGrant,
isDepleted,
isExpired,
isInactive
} = getCreditActivityFlags(item);
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 = (0, _util.formatCreditAmount)((0, _util.formatBNStr)(amount, currency?.decimal || 0), unit);
if (!includeGrants) {
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
onClick: e => handleTransactionClick(e, item),
sx: {
pr: 5
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
children: displayAmount
})
});
}
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
onClick: e => handleTransactionClick(e, item),
sx: {
pr: 5
},
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "column",
spacing: 0.25,
alignItems: "flex-end",
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
sx: {
fontWeight: 500,
color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main",
whiteSpace: "nowrap"
},
children: [isGrant ? "+" : "-", " ", displayAmount]
}), isDepleted ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "caption",
sx: {
color: "text.secondary"
},
children: t("common.consumed")
}) : isExpired ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "caption",
sx: {
color: "text.secondary"
},
children: t("common.expired")
}) : null]
})
});
}
}
}, ...(showAdminColumns && isAdmin ? [{
label: t("common.meterEvent"),
name: "meter_event",
options: {
customBodyRenderLite: (_, index) => {
const transaction = data?.list[index];
if (!transaction.meter) {
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "body2",
children: "-"
});
}
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Link, {
href: (0, _ufo.joinURL)((0, _util.getPrefix)(), `/admin/billing/${transaction.meter.id}`),
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "body2",
sx: {
color: "text.link"
},
children: transaction.meter.event_name
})
});
}
}
}] : []), {
label: t("common.actions"),
name: "actions",
options: {
setCellProps: () => ({
style: {
width: "25%"
}
}),
customBodyRenderLite: (_, index) => {
const item = data?.list[index];
const {
isGrant,
isScheduled
} = getCreditActivityFlags(item);
const isPaid = isGrant && item.category === "paid" && !isScheduled;
const subscriptionId = getSubscriptionId(item);
const invoiceId = isGrant ? getInvoiceId(item) : null;
const shouldShowSubscription = Boolean(subscriptionId) && (!isGrant || isScheduled || isPaid);
return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
sx: {
display: "flex",
gap: 1,
alignItems: "center",
flexWrap: "nowrap"
},
children: [shouldShowSubscription && subscriptionId ? renderActionButton(t("common.viewSubscription"), e => openSubscription(e, subscriptionId)) : null, isPaid && invoiceId ? renderActionButton(t("common.viewInvoice"), e => openInvoice(e, invoiceId)) : null]
});
}
}
}].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__ */(0, _jsxRuntime.jsxs)(TableRoot, {
children: [showTimeFilter && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
sx: {
my: 2
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
sx: {
mt: 2
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Grid, {
container: true,
spacing: 2,
sx: {
alignItems: "center"
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Grid, {
size: {
xs: 12,
sm: 6,
md: 4
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_dateRangePicker.default, {
value: filters,
onChange: handleDateRangeChange,
size: "small",
fullWidth: true
})
})
})
})
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_table.default, {
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")
})]
});
});
const TableRoot = (0, _system.styled)(_material.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;
}
}
`;
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__ */(0, _jsxRuntime.jsx)(TransactionsTable, {
...props
});
}