@blocklet/payment-react 1.24.1 → 1.24.3

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,6 +4,7 @@ import { useLocalStorageState, useRequest } from "ahooks";
4
4
  import { createContext, useContext, useEffect, useState } from "react";
5
5
  import axios from "axios";
6
6
  import { joinURL } from "ufo";
7
+ import useBus from "use-bus";
7
8
  import api from "../libs/api.js";
8
9
  import { getPrefix, PAYMENT_KIT_DID } from "../libs/util.js";
9
10
  import { CachedRequest } from "../libs/cached-request.js";
@@ -116,6 +117,12 @@ function PaymentProvider({
116
117
  } = useRequest(getSettings, {
117
118
  refreshDeps: [livemode]
118
119
  });
120
+ useBus(
121
+ // @ts-ignore
122
+ ["paymentMethod.created", "paymentMethod.updated", "paymentCurrency.added", "paymentCurrency.updated"],
123
+ () => run(true),
124
+ [run]
125
+ );
119
126
  useEffect(() => {
120
127
  const didSpace = session?.user?.didSpace;
121
128
  const userDid = session?.user?.did;
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
3
- import { Box, Typography, Grid, Stack, Link, Button, Chip } from "@mui/material";
3
+ import { Box, Typography, Grid, Stack, Link, Button } from "@mui/material";
4
4
  import { useRequest } from "ahooks";
5
5
  import { useNavigate } from "react-router-dom";
6
- import React, { useCallback, useEffect, useRef, useState } from "react";
6
+ import React, { useEffect, useRef, useState } from "react";
7
7
  import { styled } from "@mui/system";
8
8
  import { joinURL } from "ufo";
9
9
  import DateRangePicker from "../../components/date-range-picker.js";
@@ -41,16 +41,92 @@ const getInvoiceDetailLink = (invoiceId, inDashboard) => {
41
41
  connect: false
42
42
  };
43
43
  };
44
- const getTransactionDetailLink = (transactionId, inDashboard) => {
45
- let path = `/customer/credit-transaction/${transactionId}`;
44
+ const getSubscriptionDetailLink = (subscriptionId, inDashboard) => {
45
+ let path = `/customer/subscription/${subscriptionId}`;
46
46
  if (inDashboard) {
47
- path = `/admin/customers/${transactionId}`;
47
+ path = `/admin/subscriptions/${subscriptionId}`;
48
48
  }
49
49
  return {
50
50
  link: createLink(path),
51
51
  connect: false
52
52
  };
53
53
  };
54
+ const getMeterEventDetailLink = (meterEventId) => {
55
+ const path = `/admin/billing/${meterEventId}`;
56
+ return {
57
+ link: createLink(path),
58
+ connect: false
59
+ };
60
+ };
61
+ const getSubscriptionId = (item) => item.metadata?.subscription_id || item.subscription_id || item.invoice?.subscription_id;
62
+ const getInvoiceId = (item) => item.metadata?.invoice_id || item.invoice?.id;
63
+ const getMeterEventId = (item) => item.source || item.metadata?.meter_event_id;
64
+ const getCreditActivityFlags = (item) => {
65
+ const isGrant = item.activity_type === "grant";
66
+ const isScheduled = isGrant && item.metadata?.delivery_mode === "schedule";
67
+ const isDepleted = isGrant && item.status === "depleted";
68
+ const isExpired = isGrant && (item.status === "expired" || item.status === "voided");
69
+ const isInactive = isDepleted || isExpired;
70
+ return { isGrant, isScheduled, isDepleted, isExpired, isInactive };
71
+ };
72
+ const getTransactionDetailLink = (item, inDashboard) => {
73
+ if (item.activity_type === "grant") {
74
+ const invoiceId = getInvoiceId(item);
75
+ if (invoiceId) {
76
+ return getInvoiceDetailLink(invoiceId, inDashboard);
77
+ }
78
+ return getGrantDetailLink(item.id, inDashboard);
79
+ }
80
+ const meterEventId = getMeterEventId(item);
81
+ if (!meterEventId) {
82
+ return null;
83
+ }
84
+ return getMeterEventDetailLink(meterEventId);
85
+ };
86
+ const getTransactionDescription = (item, t) => {
87
+ const { isGrant, isScheduled, isInactive } = getCreditActivityFlags(item);
88
+ const isPaid = isGrant && item.category === "paid" && (!isScheduled || item.metadata?.schedule_seq === 1);
89
+ if (!isGrant) {
90
+ const secondLine = item.metadata?.is_repayment ? t("common.creditActivity.repayment") : item.description || "";
91
+ return {
92
+ isGrant,
93
+ isInactive,
94
+ activityType: t("common.creditActivity.consumption"),
95
+ secondLine
96
+ };
97
+ }
98
+ if (isPaid) {
99
+ let secondLine = item.description || "";
100
+ if (item.invoice?.total && item.invoice?.paymentCurrency) {
101
+ const invoiceCurrency = item.invoice.paymentCurrency;
102
+ const paidAmount = formatCreditAmount(
103
+ formatBNStr(item.invoice.total, invoiceCurrency.decimal || 0),
104
+ invoiceCurrency.symbol || ""
105
+ );
106
+ secondLine = t("common.creditActivity.paidAmount", { amount: paidAmount });
107
+ }
108
+ return {
109
+ isGrant,
110
+ isInactive,
111
+ activityType: t("common.creditActivity.paidGrant"),
112
+ secondLine
113
+ };
114
+ }
115
+ if (isScheduled) {
116
+ return {
117
+ isGrant,
118
+ isInactive,
119
+ activityType: t("common.creditActivity.resetGrant"),
120
+ secondLine: item.description || ""
121
+ };
122
+ }
123
+ return {
124
+ isGrant,
125
+ isInactive,
126
+ activityType: t("common.creditActivity.promotionalGrant"),
127
+ secondLine: item.description || ""
128
+ };
129
+ };
54
130
  const TransactionsTable = React.memo((props) => {
55
131
  const {
56
132
  pageSize,
@@ -68,6 +144,7 @@ const TransactionsTable = React.memo((props) => {
68
144
  const { t, locale } = useLocaleContext();
69
145
  const { session } = usePaymentContext();
70
146
  const isAdmin = ["owner", "admin"].includes(session?.user?.role || "");
147
+ const isDashboard = isAdmin && mode === "dashboard";
71
148
  const navigate = useNavigate();
72
149
  const effectiveCustomerId = customer_id || session?.user?.did;
73
150
  const [search, setSearch] = useState({
@@ -78,7 +155,7 @@ const TransactionsTable = React.memo((props) => {
78
155
  start: void 0,
79
156
  end: void 0
80
157
  });
81
- const handleDateRangeChange = useCallback((newValue) => {
158
+ const handleDateRangeChange = (newValue) => {
82
159
  setFilters(newValue);
83
160
  setSearch((prev) => ({
84
161
  ...prev,
@@ -86,7 +163,7 @@ const TransactionsTable = React.memo((props) => {
86
163
  start: newValue.start || void 0,
87
164
  end: newValue.end || void 0
88
165
  }));
89
- }, []);
166
+ };
90
167
  const { loading, data = { list: [], count: 0 } } = useRequest(
91
168
  () => fetchData({
92
169
  ...search,
@@ -102,9 +179,14 @@ const TransactionsTable = React.memo((props) => {
102
179
  );
103
180
  useEffect(() => {
104
181
  if (showTimeFilter && !search.start && !search.end) {
105
- handleDateRangeChange(filters);
182
+ setSearch((prev) => ({
183
+ ...prev,
184
+ page: 1,
185
+ start: filters.start || void 0,
186
+ end: filters.end || void 0
187
+ }));
106
188
  }
107
- }, [showTimeFilter, handleDateRangeChange, search.start, search.end, filters]);
189
+ }, [showTimeFilter, search.start, search.end, filters.start, filters.end]);
108
190
  const prevData = useRef(data);
109
191
  useEffect(() => {
110
192
  if (onTableDataChange) {
@@ -113,105 +195,94 @@ const TransactionsTable = React.memo((props) => {
113
195
  }
114
196
  }, [data]);
115
197
  const handleTransactionClick = (e, item) => {
116
- if (item.activity_type === "grant") {
117
- const { link } = getGrantDetailLink(item.id, isAdmin && mode === "dashboard");
118
- handleNavigation(e, link, navigate, { target: link.external ? "_blank" : "_self" });
119
- } else {
120
- const { link } = getTransactionDetailLink(item.id, isAdmin && mode === "dashboard");
121
- handleNavigation(e, link, navigate, { target: link.external ? "_blank" : "_self" });
198
+ const detail = getTransactionDetailLink(item, isDashboard);
199
+ if (!detail) {
200
+ return;
122
201
  }
202
+ handleNavigation(e, detail.link, navigate, { target: detail.link.external ? "_blank" : "_self" });
203
+ };
204
+ const openSubscription = (e, subscriptionId) => {
205
+ e.preventDefault();
206
+ const link = getSubscriptionDetailLink(subscriptionId, isDashboard);
207
+ handleNavigation(e, link.link, navigate);
208
+ };
209
+ const openInvoice = (e, invoiceId) => {
210
+ e.preventDefault();
211
+ const link = getInvoiceDetailLink(invoiceId, isDashboard);
212
+ handleNavigation(e, link.link, navigate);
123
213
  };
214
+ const renderActionButton = (label, onClick) => /* @__PURE__ */ jsx(Button, { variant: "text", size: "small", color: "primary", sx: { whiteSpace: "nowrap" }, onClick, children: label });
124
215
  const columns = [
125
216
  {
126
- label: t("common.amount"),
127
- name: "credit_amount",
128
- align: "right",
217
+ label: t("common.date"),
218
+ name: "created_at",
129
219
  options: {
220
+ setCellProps: () => ({ style: { width: "25%" } }),
130
221
  customBodyRenderLite: (_, index) => {
131
222
  const item = data?.list[index];
132
- const isGrant = item.activity_type === "grant";
133
- const isExpiredGrant = isGrant && item.status === "expired";
134
- const amount = isGrant ? item.amount : item.credit_amount;
135
- const currency = item.paymentCurrency || item.currency;
136
- const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
137
- const displayAmount = formatCreditAmount(formatBNStr(amount, currency?.decimal || 0), unit);
138
- if (!includeGrants) {
139
- return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsx(Typography, { children: displayAmount }) });
140
- }
141
- const amountNode = /* @__PURE__ */ jsxs(
142
- Typography,
143
- {
144
- sx: {
145
- color: isGrant ? isExpiredGrant ? "text.disabled" : "success.main" : "error.main"
146
- },
147
- children: [
148
- isGrant ? "+" : "-",
149
- " ",
150
- displayAmount
151
- ]
152
- }
153
- );
154
- return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", justifyContent: "flex-end", sx: { width: "100%" }, children: [
155
- isExpiredGrant && /* @__PURE__ */ jsx(
156
- Chip,
157
- {
158
- label: t("admin.creditGrants.status.expired"),
159
- size: "small",
160
- variant: "outlined",
161
- sx: {
162
- mr: 2,
163
- height: 18,
164
- fontSize: "12px",
165
- color: "text.disabled",
166
- borderColor: "text.disabled"
167
- }
168
- }
169
- ),
170
- amountNode
171
- ] }) });
223
+ return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontSize: "0.875rem" }, children: formatToDate(item.created_at, locale, "YYYY-MM-DD HH:mm") }) });
172
224
  }
173
225
  }
174
226
  },
175
227
  {
176
- label: t("common.creditGrant"),
177
- name: "credit_grant",
228
+ label: t("common.description"),
229
+ name: "description",
178
230
  options: {
231
+ setCellProps: () => ({ style: { width: "25%" } }),
179
232
  customBodyRenderLite: (_, index) => {
180
233
  const item = data?.list[index];
181
- const isGrant = item.activity_type === "grant";
182
- const isExpiredGrant = isGrant && item.status === "expired";
183
- const grantName = isGrant ? item.name : item.creditGrant.name;
184
- const grantId = isGrant ? item.id : item.credit_grant_id;
185
- const nameNode = /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { cursor: "pointer", color: isExpiredGrant ? "text.disabled" : void 0 }, children: grantName || `Grant ${grantId.slice(-6)}` });
186
- return /* @__PURE__ */ jsx(
187
- Stack,
188
- {
189
- direction: "row",
190
- spacing: 1,
191
- onClick: (e) => {
192
- const link = getGrantDetailLink(grantId, isAdmin && mode === "dashboard");
193
- handleNavigation(e, link.link, navigate);
194
- },
195
- sx: {
196
- alignItems: "center"
197
- },
198
- children: nameNode
199
- }
200
- );
234
+ const { activityType, secondLine, isInactive, isGrant } = getTransactionDescription(item, t);
235
+ return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { cursor: "pointer" }, children: /* @__PURE__ */ jsxs(Stack, { direction: "column", spacing: 0.25, children: [
236
+ /* @__PURE__ */ jsx(
237
+ Typography,
238
+ {
239
+ variant: "body2",
240
+ sx: {
241
+ color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main"
242
+ },
243
+ children: activityType
244
+ }
245
+ ),
246
+ secondLine && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: secondLine })
247
+ ] }) });
201
248
  }
202
249
  }
203
250
  },
204
251
  {
205
- label: t("common.description"),
206
- name: "description",
252
+ label: t("common.amount"),
253
+ name: "credit_amount",
254
+ align: "right",
207
255
  options: {
256
+ setCellProps: () => ({ style: { width: "20%" } }),
257
+ customHeadLabelRender: () => /* @__PURE__ */ jsx(Box, { sx: { pr: 5 }, children: t("common.amount") }),
208
258
  customBodyRenderLite: (_, index) => {
209
259
  const item = data?.list[index];
210
- const isGrant = item.activity_type === "grant";
211
- const isExpiredGrant = isGrant && item.status === "expired";
212
- const description = isGrant ? item.name || item.description || "Credit Granted" : item.subscription?.description || item.description || `${item.meter_event_name} usage`;
213
- const descriptionNode = /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 400, color: isExpiredGrant ? "text.disabled" : void 0 }, children: description });
214
- return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: descriptionNode });
260
+ const { isGrant, isDepleted, isExpired, isInactive } = getCreditActivityFlags(item);
261
+ const amount = isGrant ? item.amount : item.credit_amount;
262
+ const currency = item.paymentCurrency || item.currency;
263
+ const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
264
+ const displayAmount = formatCreditAmount(formatBNStr(amount, currency?.decimal || 0), unit);
265
+ if (!includeGrants) {
266
+ return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { pr: 5 }, children: /* @__PURE__ */ jsx(Typography, { children: displayAmount }) });
267
+ }
268
+ return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { pr: 5 }, children: /* @__PURE__ */ jsxs(Stack, { direction: "column", spacing: 0.25, alignItems: "flex-end", children: [
269
+ /* @__PURE__ */ jsxs(
270
+ Typography,
271
+ {
272
+ sx: {
273
+ fontWeight: 500,
274
+ color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main",
275
+ whiteSpace: "nowrap"
276
+ },
277
+ children: [
278
+ isGrant ? "+" : "-",
279
+ " ",
280
+ displayAmount
281
+ ]
282
+ }
283
+ ),
284
+ isDepleted ? /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: t("common.consumed") }) : isExpired ? /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: t("common.expired") }) : null
285
+ ] }) });
215
286
  }
216
287
  }
217
288
  },
@@ -230,63 +301,21 @@ const TransactionsTable = React.memo((props) => {
230
301
  }
231
302
  }
232
303
  ] : [],
233
- {
234
- label: t("common.date"),
235
- name: "created_at",
236
- options: {
237
- customBodyRenderLite: (_, index) => {
238
- const item = data?.list[index];
239
- return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsx(
240
- Typography,
241
- {
242
- variant: "body2",
243
- sx: {
244
- color: "text.secondary",
245
- fontSize: "0.875rem"
246
- },
247
- children: formatToDate(item.created_at, locale, "YYYY-MM-DD HH:mm")
248
- }
249
- ) });
250
- }
251
- }
252
- },
253
304
  {
254
305
  label: t("common.actions"),
255
306
  name: "actions",
256
307
  options: {
308
+ setCellProps: () => ({ style: { width: "25%" } }),
257
309
  customBodyRenderLite: (_, index) => {
258
310
  const item = data?.list[index];
259
- const isGrant = item.activity_type === "grant";
260
- const invoiceId = isGrant ? item.metadata?.invoice_id : null;
261
- return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 1, alignItems: "center" }, children: [
262
- isGrant && invoiceId && /* @__PURE__ */ jsx(
263
- Button,
264
- {
265
- variant: "text",
266
- size: "small",
267
- color: "primary",
268
- onClick: (e) => {
269
- e.preventDefault();
270
- const link = getInvoiceDetailLink(invoiceId, isAdmin && mode === "dashboard");
271
- handleNavigation(e, link.link, navigate);
272
- },
273
- children: t("common.viewInvoice")
274
- }
275
- ),
276
- !isGrant && /* @__PURE__ */ jsx(
277
- Button,
278
- {
279
- variant: "text",
280
- size: "small",
281
- color: "primary",
282
- onClick: (e) => {
283
- e.preventDefault();
284
- const link = getTransactionDetailLink(item.id, isAdmin && mode === "dashboard");
285
- handleNavigation(e, link.link, navigate);
286
- },
287
- children: t("common.viewDetail")
288
- }
289
- )
311
+ const { isGrant, isScheduled } = getCreditActivityFlags(item);
312
+ const isPaid = isGrant && item.category === "paid" && !isScheduled;
313
+ const subscriptionId = getSubscriptionId(item);
314
+ const invoiceId = isGrant ? getInvoiceId(item) : null;
315
+ const shouldShowSubscription = Boolean(subscriptionId) && (!isGrant || isScheduled || isPaid);
316
+ return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 1, alignItems: "center", flexWrap: "nowrap" }, children: [
317
+ shouldShowSubscription && subscriptionId ? renderActionButton(t("common.viewSubscription"), (e) => openSubscription(e, subscriptionId)) : null,
318
+ isPaid && invoiceId ? renderActionButton(t("common.viewInvoice"), (e) => openInvoice(e, invoiceId)) : null
290
319
  ] });
291
320
  }
292
321
  }
@@ -15,6 +15,7 @@ import api from "../../libs/api.js";
15
15
  import StripePaymentAction from "../../components/stripe-payment-action.js";
16
16
  import {
17
17
  formatBNStr,
18
+ formatCreditAmount,
18
19
  formatError,
19
20
  formatToDate,
20
21
  formatToDatetime,
@@ -80,6 +81,7 @@ const InvoiceTable = React.memo((props) => {
80
81
  const listKey = "invoice-table";
81
82
  const { t, locale } = useLocaleContext();
82
83
  const navigate = useNavigate();
84
+ const { getCurrency } = usePaymentContext();
83
85
  const [search, setSearch] = useState({
84
86
  pageSize: pageSize || 10,
85
87
  page: 1
@@ -203,19 +205,97 @@ const InvoiceTable = React.memo((props) => {
203
205
  },
204
206
  ...relatedSubscription ? [
205
207
  {
206
- label: t("common.relatedSubscription"),
207
- name: "subscription",
208
+ label: t("common.purchaseItems"),
209
+ name: "purchase_items",
208
210
  options: {
209
211
  customBodyRenderLite: (_, index) => {
210
212
  const invoice = data?.list[index];
211
- return invoice.subscription_id ? /* @__PURE__ */ jsx(
212
- Box,
213
+ const lines = invoice.lines || [];
214
+ const items = lines.map((line) => {
215
+ const name = line.price?.product?.name || line.description;
216
+ if (!name) {
217
+ return null;
218
+ }
219
+ const quantity = Number(line.quantity || 0);
220
+ const label = Number.isFinite(quantity) && quantity > 1 ? `${name} x${quantity}` : name;
221
+ const lineKey = line.id || line.price?.id || line.price?.product?.id || line.description || name;
222
+ return { key: String(lineKey), label };
223
+ }).filter(Boolean);
224
+ if (items.length === 0 && invoice.subscription?.description) {
225
+ items.push({
226
+ key: `subscription-${invoice.subscription_id || invoice.id}`,
227
+ label: invoice.subscription.description
228
+ });
229
+ }
230
+ const isSubscription = Boolean(invoice.subscription_id);
231
+ const clickableProps = isSubscription ? { onClick: (e) => handleRelatedSubscriptionClick(e, invoice) } : {};
232
+ if (items.length === 0) {
233
+ return /* @__PURE__ */ jsx(Box, { sx: { color: "text.lighter" }, children: /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14 }, children: t("common.none") }) });
234
+ }
235
+ return /* @__PURE__ */ jsx(Box, { ...clickableProps, sx: isSubscription ? { cursor: "pointer" } : void 0, children: /* @__PURE__ */ jsx(Stack, { spacing: 0.5, children: items.map((item) => /* @__PURE__ */ jsx(
236
+ Typography,
213
237
  {
214
- onClick: (e) => handleRelatedSubscriptionClick(e, invoice),
215
- sx: { color: "text.link", cursor: "pointer" },
216
- children: invoice.subscription?.description
238
+ sx: { fontSize: 14, color: isSubscription ? "text.link" : "text.primary" },
239
+ noWrap: true,
240
+ children: item.label
241
+ },
242
+ `${invoice.id}-item-${item.key}`
243
+ )) }) });
244
+ }
245
+ }
246
+ },
247
+ {
248
+ label: t("common.credits"),
249
+ name: "credits",
250
+ options: {
251
+ customBodyRenderLite: (_, index) => {
252
+ const invoice = data?.list[index];
253
+ const lines = invoice.lines || [];
254
+ const creditItems = [];
255
+ lines.forEach((line) => {
256
+ const lineKey = String(
257
+ line.id || line.price?.id || line.price?.product?.id || line.description || invoice.id
258
+ );
259
+ const pushCreditItem = (suffix, label) => {
260
+ creditItems.push({ key: `${lineKey}-${suffix}`, label });
261
+ };
262
+ const creditConfig = line.price?.metadata?.credit_config;
263
+ const creditAmount = Number(creditConfig?.credit_amount || 0);
264
+ if (creditAmount > 0) {
265
+ const quantity = Number(line.quantity || 0);
266
+ const totalAmount = creditAmount * (Number.isFinite(quantity) && quantity > 0 ? quantity : 1);
267
+ const currencySymbol = getCurrency(creditConfig?.currency_id)?.symbol || creditConfig?.currency_id || "Credits";
268
+ pushCreditItem("amount", `+${formatCreditAmount(String(totalAmount), currencySymbol)}`);
269
+ return;
270
+ }
271
+ const scheduleConfig = creditConfig?.schedule;
272
+ const scheduledAmount = Number(scheduleConfig?.amount_per_grant || 0);
273
+ if (scheduleConfig?.enabled && scheduleConfig?.delivery_mode === "schedule" && scheduledAmount > 0) {
274
+ const quantity = Number(line.quantity || 0);
275
+ const totalAmount = scheduledAmount * (Number.isFinite(quantity) && quantity > 0 ? quantity : 1);
276
+ const currencySymbol = getCurrency(creditConfig?.currency_id)?.symbol || creditConfig?.currency_id || "Credits";
277
+ pushCreditItem("schedule", `+${formatCreditAmount(String(totalAmount), currencySymbol)}`);
278
+ return;
217
279
  }
218
- ) : /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: { ...linkStyle, color: "text.lighter" }, children: t("common.none") });
280
+ const creditInfo = line.price?.credit;
281
+ const creditInfoAmount = Number(creditInfo?.amount || 0);
282
+ if (creditInfoAmount > 0) {
283
+ const currencySymbol = creditInfo.currency?.symbol || "Credits";
284
+ pushCreditItem("credit", `+${formatCreditAmount(String(creditInfoAmount), currencySymbol)}`);
285
+ }
286
+ });
287
+ if (creditItems.length === 0) {
288
+ return "-";
289
+ }
290
+ return /* @__PURE__ */ jsx(Stack, { spacing: 0.5, children: creditItems.map((creditItem) => /* @__PURE__ */ jsx(
291
+ Typography,
292
+ {
293
+ sx: { fontSize: 14, color: "success.main" },
294
+ noWrap: true,
295
+ children: creditItem.label
296
+ },
297
+ `${invoice.id}-credit-${creditItem.key}`
298
+ )) });
219
299
  }
220
300
  }
221
301
  }
@@ -234,17 +314,6 @@ const InvoiceTable = React.memo((props) => {
234
314
  }
235
315
  }
236
316
  },
237
- {
238
- label: t("common.description"),
239
- name: "",
240
- options: {
241
- sort: false,
242
- customBodyRenderLite: (val, index) => {
243
- const invoice = data?.list[index];
244
- return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, invoice), sx: linkStyle, children: getInvoiceDescriptionAndReason(invoice, locale)?.description || invoice.id });
245
- }
246
- }
247
- },
248
317
  {
249
318
  label: t("common.status"),
250
319
  name: "status",
package/es/locales/en.js CHANGED
@@ -65,14 +65,24 @@ export default flat({
65
65
  remainingBalance: "Remaining Balance",
66
66
  credits: "credits",
67
67
  ofCredits: "of credits",
68
+ creditActivity: {
69
+ consumption: "Credit Consumed",
70
+ paidGrant: "Credit Top-up",
71
+ paidAmount: "Paid {amount}",
72
+ promotionalGrant: "Bonus",
73
+ resetGrant: "Credit Reset",
74
+ repayment: "Repayment"
75
+ },
68
76
  transferStatus: "Transaction Status",
69
77
  sourceData: "Source Data",
70
78
  viewGrant: "View Grant",
71
- viewSubscription: "View Subscription",
79
+ viewSubscription: "Subscription Detail",
72
80
  view: "View",
73
81
  meterEvent: "Meter Event",
74
82
  source: "Source",
75
83
  viewDetail: "View Detail",
84
+ viewTransactionDetail: "Transaction Detail",
85
+ viewConsumptionDetail: "Consumption Detail",
76
86
  customer: "Customer",
77
87
  currency: "Currency",
78
88
  custom: "Custom",
@@ -119,6 +129,8 @@ export default flat({
119
129
  slashStakeAmount: "Slash Stake Amount",
120
130
  know: "I know",
121
131
  relatedSubscription: "Subscription",
132
+ subscriptionOrCredit: "Subscription / Credit",
133
+ purchaseItems: "Purchase Items",
122
134
  connect: {
123
135
  defaultScan: "Use the following methods to complete this action",
124
136
  scan: "Use the following methods to complete this {action}",
@@ -126,8 +138,10 @@ export default flat({
126
138
  cancel: "Cancel"
127
139
  },
128
140
  paymentMethod: "Payment Method",
129
- viewInvoice: "View Invoice",
130
- submit: "Submit"
141
+ viewInvoice: "Invoice Detail",
142
+ submit: "Submit",
143
+ expired: "Expired",
144
+ consumed: "Consumed"
131
145
  },
132
146
  payment: {
133
147
  checkout: {
@@ -488,7 +502,9 @@ export default flat({
488
502
  amount: "Amount",
489
503
  paymentConfirmTitle: "Payment Confirmation",
490
504
  paymentConfirmDescription: "After completing this payment, the payment method you use will be automatically set as the default for this subscription. Additionally, we will retry payment for any other unpaid invoices associated with this subscription.",
491
- continue: "Continue"
505
+ continue: "Continue",
506
+ credit: "Credit",
507
+ creditRefresh: "Refresh every {interval} {unit}"
492
508
  },
493
509
  overduePayment: {
494
510
  setupPaymentDescription: "Use your saved card or add a new one to complete payment via Stripe.",