@blocklet/payment-react 1.24.2 → 1.24.4

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,102 @@ 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/billing/${subscriptionId}`;
48
48
  }
49
49
  return {
50
50
  link: createLink(path),
51
51
  connect: false
52
52
  };
53
53
  };
54
+ const getCreditTransactionDetailLink = (transactionId) => {
55
+ const path = `/customer/credit-transaction/${transactionId}`;
56
+ return {
57
+ link: createLink(path),
58
+ connect: false
59
+ };
60
+ };
61
+ const getMeterEventDetailLink = (meterEventId) => {
62
+ const path = `/admin/billing/${meterEventId}`;
63
+ return {
64
+ link: createLink(path),
65
+ connect: false
66
+ };
67
+ };
68
+ const getSubscriptionId = (item) => item.metadata?.subscription_id || item.subscription_id || item.invoice?.subscription_id;
69
+ const getInvoiceId = (item) => item.metadata?.invoice_id || item.invoice?.id;
70
+ const getMeterEventId = (item) => item.source || item.metadata?.meter_event_id;
71
+ const getCreditActivityFlags = (item) => {
72
+ const isGrant = item.activity_type === "grant";
73
+ const isScheduled = isGrant && item.metadata?.delivery_mode === "schedule";
74
+ const isDepleted = isGrant && item.status === "depleted";
75
+ const isExpired = isGrant && (item.status === "expired" || item.status === "voided");
76
+ const isInactive = isDepleted || isExpired;
77
+ return { isGrant, isScheduled, isDepleted, isExpired, isInactive };
78
+ };
79
+ const getTransactionDetailLink = (item, inDashboard) => {
80
+ if (item.activity_type === "grant") {
81
+ const invoiceId = getInvoiceId(item);
82
+ if (invoiceId) {
83
+ return getInvoiceDetailLink(invoiceId, inDashboard);
84
+ }
85
+ return getGrantDetailLink(item.id, inDashboard);
86
+ }
87
+ if (!inDashboard) {
88
+ return getCreditTransactionDetailLink(item.id);
89
+ }
90
+ const meterEventId = getMeterEventId(item);
91
+ if (!meterEventId) {
92
+ return null;
93
+ }
94
+ return getMeterEventDetailLink(meterEventId);
95
+ };
96
+ const getTransactionDescription = (item, t) => {
97
+ const { isGrant, isScheduled, isInactive } = getCreditActivityFlags(item);
98
+ const isPaid = isGrant && item.category === "paid" && (!isScheduled || item.metadata?.schedule_seq === 1);
99
+ if (!isGrant) {
100
+ const secondLine = item.metadata?.is_repayment ? t("common.creditActivity.repayment") : item.description || "";
101
+ return {
102
+ isGrant,
103
+ isInactive,
104
+ activityType: t("common.creditActivity.consumption"),
105
+ secondLine
106
+ };
107
+ }
108
+ if (isPaid) {
109
+ let secondLine = item.description || "";
110
+ if (item.invoice?.total && item.invoice?.paymentCurrency) {
111
+ const invoiceCurrency = item.invoice.paymentCurrency;
112
+ const paidAmount = formatCreditAmount(
113
+ formatBNStr(item.invoice.total, invoiceCurrency.decimal || 0),
114
+ invoiceCurrency.symbol || ""
115
+ );
116
+ secondLine = t("common.creditActivity.paidAmount", { amount: paidAmount });
117
+ }
118
+ return {
119
+ isGrant,
120
+ isInactive,
121
+ activityType: t("common.creditActivity.paidGrant"),
122
+ secondLine
123
+ };
124
+ }
125
+ if (isScheduled) {
126
+ return {
127
+ isGrant,
128
+ isInactive,
129
+ activityType: t("common.creditActivity.resetGrant"),
130
+ secondLine: item.description || ""
131
+ };
132
+ }
133
+ return {
134
+ isGrant,
135
+ isInactive,
136
+ activityType: t("common.creditActivity.promotionalGrant"),
137
+ secondLine: item.description || ""
138
+ };
139
+ };
54
140
  const TransactionsTable = React.memo((props) => {
55
141
  const {
56
142
  pageSize,
@@ -68,6 +154,7 @@ const TransactionsTable = React.memo((props) => {
68
154
  const { t, locale } = useLocaleContext();
69
155
  const { session } = usePaymentContext();
70
156
  const isAdmin = ["owner", "admin"].includes(session?.user?.role || "");
157
+ const isDashboard = isAdmin && mode === "dashboard";
71
158
  const navigate = useNavigate();
72
159
  const effectiveCustomerId = customer_id || session?.user?.did;
73
160
  const [search, setSearch] = useState({
@@ -78,7 +165,7 @@ const TransactionsTable = React.memo((props) => {
78
165
  start: void 0,
79
166
  end: void 0
80
167
  });
81
- const handleDateRangeChange = useCallback((newValue) => {
168
+ const handleDateRangeChange = (newValue) => {
82
169
  setFilters(newValue);
83
170
  setSearch((prev) => ({
84
171
  ...prev,
@@ -86,7 +173,7 @@ const TransactionsTable = React.memo((props) => {
86
173
  start: newValue.start || void 0,
87
174
  end: newValue.end || void 0
88
175
  }));
89
- }, []);
176
+ };
90
177
  const { loading, data = { list: [], count: 0 } } = useRequest(
91
178
  () => fetchData({
92
179
  ...search,
@@ -102,9 +189,14 @@ const TransactionsTable = React.memo((props) => {
102
189
  );
103
190
  useEffect(() => {
104
191
  if (showTimeFilter && !search.start && !search.end) {
105
- handleDateRangeChange(filters);
192
+ setSearch((prev) => ({
193
+ ...prev,
194
+ page: 1,
195
+ start: filters.start || void 0,
196
+ end: filters.end || void 0
197
+ }));
106
198
  }
107
- }, [showTimeFilter, handleDateRangeChange, search.start, search.end, filters]);
199
+ }, [showTimeFilter, search.start, search.end, filters.start, filters.end]);
108
200
  const prevData = useRef(data);
109
201
  useEffect(() => {
110
202
  if (onTableDataChange) {
@@ -113,105 +205,94 @@ const TransactionsTable = React.memo((props) => {
113
205
  }
114
206
  }, [data]);
115
207
  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" });
208
+ const detail = getTransactionDetailLink(item, isDashboard);
209
+ if (!detail) {
210
+ return;
122
211
  }
212
+ handleNavigation(e, detail.link, navigate, { target: detail.link.external ? "_blank" : "_self" });
213
+ };
214
+ const openSubscription = (e, subscriptionId) => {
215
+ e.preventDefault();
216
+ const link = getSubscriptionDetailLink(subscriptionId, isDashboard);
217
+ handleNavigation(e, link.link, navigate);
123
218
  };
219
+ const openInvoice = (e, invoiceId) => {
220
+ e.preventDefault();
221
+ const link = getInvoiceDetailLink(invoiceId, isDashboard);
222
+ handleNavigation(e, link.link, navigate);
223
+ };
224
+ const renderActionButton = (label, onClick) => /* @__PURE__ */ jsx(Button, { variant: "text", size: "small", color: "primary", sx: { whiteSpace: "nowrap" }, onClick, children: label });
124
225
  const columns = [
125
226
  {
126
- label: t("common.amount"),
127
- name: "credit_amount",
128
- align: "right",
227
+ label: t("common.date"),
228
+ name: "created_at",
129
229
  options: {
230
+ setCellProps: () => ({ style: { width: "25%" } }),
130
231
  customBodyRenderLite: (_, index) => {
131
232
  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
- ] }) });
233
+ 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
234
  }
173
235
  }
174
236
  },
175
237
  {
176
- label: t("common.creditGrant"),
177
- name: "credit_grant",
238
+ label: t("common.description"),
239
+ name: "description",
178
240
  options: {
241
+ setCellProps: () => ({ style: { width: "25%" } }),
179
242
  customBodyRenderLite: (_, index) => {
180
243
  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
- );
244
+ const { activityType, secondLine, isInactive, isGrant } = getTransactionDescription(item, t);
245
+ return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { cursor: "pointer" }, children: /* @__PURE__ */ jsxs(Stack, { direction: "column", spacing: 0.25, children: [
246
+ /* @__PURE__ */ jsx(
247
+ Typography,
248
+ {
249
+ variant: "body2",
250
+ sx: {
251
+ color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main"
252
+ },
253
+ children: activityType
254
+ }
255
+ ),
256
+ secondLine && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: secondLine })
257
+ ] }) });
201
258
  }
202
259
  }
203
260
  },
204
261
  {
205
- label: t("common.description"),
206
- name: "description",
262
+ label: t("common.amount"),
263
+ name: "credit_amount",
264
+ align: "right",
207
265
  options: {
266
+ setCellProps: () => ({ style: { width: "20%" } }),
267
+ customHeadLabelRender: () => /* @__PURE__ */ jsx(Box, { sx: { pr: 5 }, children: t("common.amount") }),
208
268
  customBodyRenderLite: (_, index) => {
209
269
  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 });
270
+ const { isGrant, isDepleted, isExpired, isInactive } = getCreditActivityFlags(item);
271
+ const amount = isGrant ? item.amount : item.credit_amount;
272
+ const currency = item.paymentCurrency || item.currency;
273
+ const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
274
+ const displayAmount = formatCreditAmount(formatBNStr(amount, currency?.decimal || 0), unit);
275
+ if (!includeGrants) {
276
+ return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), sx: { pr: 5 }, children: /* @__PURE__ */ jsx(Typography, { children: displayAmount }) });
277
+ }
278
+ 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: [
279
+ /* @__PURE__ */ jsxs(
280
+ Typography,
281
+ {
282
+ sx: {
283
+ fontWeight: 500,
284
+ color: isInactive ? "text.secondary" : isGrant ? "success.main" : "error.main",
285
+ whiteSpace: "nowrap"
286
+ },
287
+ children: [
288
+ isGrant ? "+" : "-",
289
+ " ",
290
+ displayAmount
291
+ ]
292
+ }
293
+ ),
294
+ 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
295
+ ] }) });
215
296
  }
216
297
  }
217
298
  },
@@ -230,63 +311,21 @@ const TransactionsTable = React.memo((props) => {
230
311
  }
231
312
  }
232
313
  ] : [],
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
314
  {
254
315
  label: t("common.actions"),
255
316
  name: "actions",
256
317
  options: {
318
+ setCellProps: () => ({ style: { width: "25%" } }),
257
319
  customBodyRenderLite: (_, index) => {
258
320
  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
- )
321
+ const { isGrant, isScheduled } = getCreditActivityFlags(item);
322
+ const isPaid = isGrant && item.category === "paid" && !isScheduled;
323
+ const subscriptionId = getSubscriptionId(item);
324
+ const invoiceId = isGrant ? getInvoiceId(item) : null;
325
+ const shouldShowSubscription = Boolean(subscriptionId) && (!isGrant || isScheduled || isPaid);
326
+ return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 1, alignItems: "center", flexWrap: "nowrap" }, children: [
327
+ shouldShowSubscription && subscriptionId ? renderActionButton(t("common.viewSubscription"), (e) => openSubscription(e, subscriptionId)) : null,
328
+ isPaid && invoiceId ? renderActionButton(t("common.viewInvoice"), (e) => openInvoice(e, invoiceId)) : null
290
329
  ] });
291
330
  }
292
331
  }
@@ -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.",