@blocklet/payment-react 1.19.0 → 1.19.1

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.
Files changed (73) hide show
  1. package/es/components/blockchain/tx.d.ts +1 -1
  2. package/es/components/blockchain/tx.js +9 -11
  3. package/es/components/country-select.d.ts +1 -1
  4. package/es/components/date-range-picker.d.ts +13 -0
  5. package/es/components/date-range-picker.js +279 -0
  6. package/es/components/input.d.ts +5 -2
  7. package/es/components/input.js +6 -2
  8. package/es/components/label.d.ts +7 -0
  9. package/es/components/label.js +49 -0
  10. package/es/components/loading-button.d.ts +1 -1
  11. package/es/history/credit/grants-list.d.ts +14 -0
  12. package/es/history/credit/grants-list.js +215 -0
  13. package/es/history/credit/transactions-list.d.ts +13 -0
  14. package/es/history/credit/transactions-list.js +255 -0
  15. package/es/history/invoice/list.js +21 -1
  16. package/es/index.d.ts +5 -1
  17. package/es/index.js +10 -1
  18. package/es/libs/util.d.ts +2 -0
  19. package/es/libs/util.js +12 -0
  20. package/es/locales/en.js +20 -2
  21. package/es/locales/zh.js +20 -2
  22. package/es/payment/form/index.js +44 -6
  23. package/es/payment/index.js +18 -3
  24. package/es/payment/product-item.d.ts +8 -1
  25. package/es/payment/product-item.js +137 -5
  26. package/es/payment/summary.d.ts +3 -1
  27. package/es/payment/summary.js +9 -0
  28. package/lib/components/blockchain/tx.d.ts +1 -1
  29. package/lib/components/blockchain/tx.js +9 -8
  30. package/lib/components/country-select.d.ts +1 -1
  31. package/lib/components/date-range-picker.d.ts +13 -0
  32. package/lib/components/date-range-picker.js +329 -0
  33. package/lib/components/input.d.ts +5 -2
  34. package/lib/components/input.js +8 -4
  35. package/lib/components/label.d.ts +7 -0
  36. package/lib/components/label.js +60 -0
  37. package/lib/components/loading-button.d.ts +1 -1
  38. package/lib/history/credit/grants-list.d.ts +14 -0
  39. package/lib/history/credit/grants-list.js +277 -0
  40. package/lib/history/credit/transactions-list.d.ts +13 -0
  41. package/lib/history/credit/transactions-list.js +301 -0
  42. package/lib/history/invoice/list.js +24 -0
  43. package/lib/index.d.ts +5 -1
  44. package/lib/index.js +39 -0
  45. package/lib/libs/util.d.ts +2 -0
  46. package/lib/libs/util.js +14 -0
  47. package/lib/locales/en.js +20 -2
  48. package/lib/locales/zh.js +20 -2
  49. package/lib/payment/form/index.js +45 -6
  50. package/lib/payment/index.js +20 -2
  51. package/lib/payment/product-item.d.ts +8 -1
  52. package/lib/payment/product-item.js +144 -4
  53. package/lib/payment/summary.d.ts +3 -1
  54. package/lib/payment/summary.js +9 -0
  55. package/package.json +3 -3
  56. package/src/components/blockchain/tx.tsx +9 -15
  57. package/src/components/country-select.tsx +2 -2
  58. package/src/components/date-range-picker.tsx +310 -0
  59. package/src/components/input.tsx +14 -3
  60. package/src/components/label.tsx +58 -0
  61. package/src/components/loading-button.tsx +1 -1
  62. package/src/history/credit/grants-list.tsx +276 -0
  63. package/src/history/credit/transactions-list.tsx +317 -0
  64. package/src/history/invoice/list.tsx +18 -1
  65. package/src/index.ts +9 -0
  66. package/src/libs/util.ts +14 -0
  67. package/src/locales/en.tsx +20 -0
  68. package/src/locales/zh.tsx +19 -0
  69. package/src/payment/form/address.tsx +2 -2
  70. package/src/payment/form/index.tsx +110 -52
  71. package/src/payment/index.tsx +17 -1
  72. package/src/payment/product-item.tsx +152 -4
  73. package/src/payment/summary.tsx +13 -2
@@ -0,0 +1,276 @@
1
+ /* eslint-disable @typescript-eslint/indent */
2
+ /* eslint-disable react/require-default-props */
3
+ /* eslint-disable react/no-unused-prop-types */
4
+ /* eslint-disable @typescript-eslint/naming-convention */
5
+ /* eslint-disable no-nested-ternary */
6
+ /* eslint-disable react/no-unstable-nested-components */
7
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
8
+ import type { Paginated, TCreditGrantExpanded } from '@blocklet/payment-types';
9
+ import { Box, Typography, Chip } from '@mui/material';
10
+ import { useRequest } from 'ahooks';
11
+ import React, { useEffect, useRef, useState } from 'react';
12
+ // eslint-disable-next-line import/no-extraneous-dependencies
13
+ import { useNavigate } from 'react-router-dom';
14
+
15
+ import { styled } from '@mui/system';
16
+ import { formatBNStr, formatToDate } from '../../libs/util';
17
+ import { usePaymentContext } from '../../contexts/payment';
18
+ import api from '../../libs/api';
19
+ import Table from '../../components/table';
20
+ import { createLink, handleNavigation } from '../../libs/navigation';
21
+
22
+ type Result = Paginated<TCreditGrantExpanded>;
23
+
24
+ const fetchData = (params: Record<string, any> = {}): Promise<Result> => {
25
+ const search = new URLSearchParams();
26
+ Object.keys(params).forEach((key) => {
27
+ if (params[key]) {
28
+ search.set(key, String(params[key]));
29
+ }
30
+ });
31
+
32
+ return api.get(`/api/credit-grants?${search.toString()}`).then((res: any) => res.data);
33
+ };
34
+
35
+ type Props = {
36
+ customer_id?: string;
37
+ subscription_id?: string;
38
+ status?: string;
39
+ pageSize?: number;
40
+ onTableDataChange?: Function;
41
+ mode?: 'dashboard' | 'portal';
42
+ };
43
+
44
+ export function StatusChip({ status, label }: { status: string; label?: string }) {
45
+ const getStatusColor = (statusValue: string) => {
46
+ switch (statusValue) {
47
+ case 'granted':
48
+ return 'success';
49
+ case 'pending':
50
+ return 'warning';
51
+ case 'expired':
52
+ return 'default';
53
+ case 'depleted':
54
+ return 'default';
55
+ case 'voided':
56
+ return 'default';
57
+ default:
58
+ return 'default';
59
+ }
60
+ };
61
+
62
+ return <Chip label={label || status} size="small" color={getStatusColor(status) as any} />;
63
+ }
64
+
65
+ const getLink = (grant: TCreditGrantExpanded, inDashboard: boolean) => {
66
+ let path = `/customer/credit-grant/${grant.id}`;
67
+ if (inDashboard) {
68
+ path = `/admin/customers/${grant.id}`;
69
+ }
70
+
71
+ return {
72
+ link: createLink(path),
73
+ connect: false,
74
+ };
75
+ };
76
+
77
+ const GrantsTable = React.memo((props: Props) => {
78
+ const { pageSize, status = '', customer_id, subscription_id, onTableDataChange } = props;
79
+ const listKey = 'credit-grants-table';
80
+ const { t, locale } = useLocaleContext();
81
+ const { session } = usePaymentContext();
82
+ const navigate = useNavigate();
83
+
84
+ const isAdmin = ['owner', 'admin'].includes(session?.user?.role || '');
85
+
86
+ const inDashboard = props.mode === 'dashboard' && isAdmin;
87
+
88
+ // 如果没有传入 customer_id,使用当前登录用户的 DID
89
+ const effectiveCustomerId = customer_id || session?.user?.did;
90
+
91
+ const [search, setSearch] = useState<{ pageSize: number; page: number }>({
92
+ pageSize: pageSize || 10,
93
+ page: 1,
94
+ });
95
+
96
+ const { loading, data = { list: [], count: 0 } } = useRequest(
97
+ () =>
98
+ fetchData({
99
+ ...search,
100
+ status,
101
+ customer_id: effectiveCustomerId,
102
+ subscription_id,
103
+ }),
104
+ {
105
+ refreshDeps: [search, status, effectiveCustomerId, subscription_id],
106
+ }
107
+ );
108
+
109
+ const prevData = useRef(data);
110
+
111
+ const handleLinkClick = (e: React.MouseEvent, grant: TCreditGrantExpanded) => {
112
+ const { link } = getLink(grant, inDashboard);
113
+ handleNavigation(e, link, navigate, { target: link.external ? '_blank' : '_self' });
114
+ };
115
+
116
+ useEffect(() => {
117
+ if (onTableDataChange) {
118
+ onTableDataChange(data, prevData.current);
119
+ prevData.current = data;
120
+ }
121
+ // eslint-disable-next-line react-hooks/exhaustive-deps
122
+ }, [data]);
123
+
124
+ const columns = [
125
+ {
126
+ label: t('common.name'),
127
+ name: 'name',
128
+ options: {
129
+ customBodyRenderLite: (_: string, index: number) => {
130
+ const grant = data?.list[index] as TCreditGrantExpanded;
131
+ return <Box onClick={(e) => handleLinkClick(e, grant)}>{grant.name || grant.id}</Box>;
132
+ },
133
+ },
134
+ },
135
+ {
136
+ label: t('common.status'),
137
+ name: 'status',
138
+ options: {
139
+ customBodyRenderLite: (_: string, index: number) => {
140
+ const grant = data?.list[index] as TCreditGrantExpanded;
141
+ return (
142
+ <Box onClick={(e) => handleLinkClick(e, grant)}>
143
+ <StatusChip status={grant.status} label={t(`admin.customer.creditGrants.status.${grant.status}`)} />
144
+ </Box>
145
+ );
146
+ },
147
+ },
148
+ },
149
+ {
150
+ label: t('common.remainingCredit'),
151
+ name: 'remaining_amount',
152
+ align: 'right',
153
+ options: {
154
+ customBodyRenderLite: (_: string, index: number) => {
155
+ const grant = data?.list[index] as TCreditGrantExpanded;
156
+ return (
157
+ <Box onClick={(e) => handleLinkClick(e, grant)}>
158
+ <Typography variant="body2">
159
+ {formatBNStr(grant.remaining_amount, grant.paymentCurrency.decimal)} {grant.paymentCurrency.symbol}
160
+ </Typography>
161
+ </Box>
162
+ );
163
+ },
164
+ },
165
+ },
166
+ {
167
+ label: t('common.scope'),
168
+ name: 'scope',
169
+ options: {
170
+ customBodyRenderLite: (_: string, index: number) => {
171
+ const grant = data?.list[index] as TCreditGrantExpanded;
172
+ let scope = 'general';
173
+ if (grant.applicability_config?.scope?.prices) {
174
+ scope = 'specific';
175
+ }
176
+ return (
177
+ <Box onClick={(e) => handleLinkClick(e, grant)}>
178
+ {scope === 'specific' ? t('common.specific') : t('common.general')}
179
+ </Box>
180
+ );
181
+ },
182
+ },
183
+ },
184
+ {
185
+ label: t('common.effectiveDate'),
186
+ name: 'effective_at',
187
+ options: {
188
+ customBodyRenderLite: (_: string, index: number) => {
189
+ const grant = data?.list[index] as TCreditGrantExpanded;
190
+ const effectiveAt = grant.effective_at ? grant.effective_at * 1000 : grant.created_at;
191
+ return (
192
+ <Box onClick={(e) => handleLinkClick(e, grant)}>
193
+ {formatToDate(effectiveAt, locale, 'YYYY-MM-DD HH:mm')}
194
+ </Box>
195
+ );
196
+ },
197
+ },
198
+ },
199
+ {
200
+ label: t('common.expirationDate'),
201
+ name: 'expires_at',
202
+ options: {
203
+ customBodyRenderLite: (_: string, index: number) => {
204
+ const grant = data?.list[index] as TCreditGrantExpanded;
205
+ return (
206
+ <Box onClick={(e) => handleLinkClick(e, grant)}>
207
+ <Typography variant="body2">
208
+ {grant.expires_at ? formatToDate(grant.expires_at * 1000, locale, 'YYYY-MM-DD HH:mm') : '-'}
209
+ </Typography>
210
+ </Box>
211
+ );
212
+ },
213
+ },
214
+ },
215
+ ];
216
+
217
+ const onTableChange = ({ page, rowsPerPage }: any) => {
218
+ if (search.pageSize !== rowsPerPage) {
219
+ setSearch((x) => ({ ...x, pageSize: rowsPerPage, page: 1 }));
220
+ } else if (search.page !== page + 1) {
221
+ setSearch((x) => ({ ...x, page: page + 1 }));
222
+ }
223
+ };
224
+
225
+ return (
226
+ <TableRoot>
227
+ <Table
228
+ hasRowLink
229
+ durable={`__${listKey}__`}
230
+ durableKeys={['page', 'rowsPerPage', 'searchText']}
231
+ data={data.list}
232
+ columns={columns}
233
+ options={{
234
+ count: data.count,
235
+ page: search.page - 1,
236
+ rowsPerPage: search.pageSize,
237
+ }}
238
+ loading={loading}
239
+ onChange={onTableChange}
240
+ toolbar={false}
241
+ sx={{ mt: 2 }}
242
+ showMobile={false}
243
+ mobileTDFlexDirection="row"
244
+ emptyNodeText={t('admin.creditGrants.noGrants')}
245
+ />
246
+ </TableRoot>
247
+ );
248
+ });
249
+
250
+ const TableRoot = styled(Box)`
251
+ @media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
252
+ .MuiTable-root > .MuiTableBody-root > .MuiTableRow-root > td.MuiTableCell-root {
253
+ > div {
254
+ width: fit-content;
255
+ flex: inherit;
256
+ font-size: 14px;
257
+ }
258
+ }
259
+ .invoice-summary {
260
+ padding-right: 20px;
261
+ }
262
+ }
263
+ `;
264
+ export default function CreditGrantsList(rawProps: Props) {
265
+ const props = Object.assign(
266
+ {
267
+ customer_id: '',
268
+ subscription_id: '',
269
+ status: 'granted,pending,depleted,expired',
270
+ pageSize: 10,
271
+ onTableDataChange: () => {},
272
+ },
273
+ rawProps
274
+ );
275
+ return <GrantsTable {...props} />;
276
+ }
@@ -0,0 +1,317 @@
1
+ /* eslint-disable @typescript-eslint/indent */
2
+ /* eslint-disable react/require-default-props */
3
+ /* eslint-disable react/no-unused-prop-types */
4
+ /* eslint-disable @typescript-eslint/naming-convention */
5
+ /* eslint-disable no-nested-ternary */
6
+ /* eslint-disable react/no-unstable-nested-components */
7
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
8
+ import type { Paginated, TCreditTransactionExpanded } from '@blocklet/payment-types';
9
+ import { Box, Typography, Stack, Link, Grid } from '@mui/material';
10
+ import { useRequest } from 'ahooks';
11
+ // eslint-disable-next-line import/no-extraneous-dependencies
12
+ import { useNavigate } from 'react-router-dom';
13
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
14
+ import { joinURL } from 'ufo';
15
+ import { styled } from '@mui/system';
16
+ import DateRangePicker, { type DateRangeValue } from '../../components/date-range-picker';
17
+ // eslint-disable-next-line import/no-extraneous-dependencies
18
+
19
+ import { formatBNStr, formatToDate, getPrefix } from '../../libs/util';
20
+ import { usePaymentContext } from '../../contexts/payment';
21
+ import api from '../../libs/api';
22
+ import Table from '../../components/table';
23
+ import { createLink, handleNavigation } from '../../libs/navigation';
24
+
25
+ type Result = Paginated<TCreditTransactionExpanded>;
26
+
27
+ const fetchData = (params: Record<string, any> = {}): Promise<Result> => {
28
+ const search = new URLSearchParams();
29
+ Object.keys(params).forEach((key) => {
30
+ if (params[key]) {
31
+ search.set(key, String(params[key]));
32
+ }
33
+ });
34
+
35
+ return api.get(`/api/credit-transactions?${search.toString()}`).then((res: any) => res.data);
36
+ };
37
+
38
+ type Props = {
39
+ customer_id?: string;
40
+ subscription_id?: string;
41
+ credit_grant_id?: string;
42
+ pageSize?: number;
43
+ onTableDataChange?: Function;
44
+ showAdminColumns?: boolean;
45
+ showTimeFilter?: boolean;
46
+ source?: string;
47
+ mode?: 'dashboard' | 'portal';
48
+ };
49
+
50
+ const getGrantDetailLink = (grantId: string, inDashboard: boolean) => {
51
+ let path = `/customer/credit-grant/${grantId}`;
52
+ if (inDashboard) {
53
+ path = `/admin/customers/${grantId}`;
54
+ }
55
+
56
+ return {
57
+ link: createLink(path),
58
+ connect: false,
59
+ };
60
+ };
61
+
62
+ const TransactionsTable = React.memo((props: Props) => {
63
+ const {
64
+ pageSize,
65
+ customer_id,
66
+ subscription_id,
67
+ credit_grant_id,
68
+ onTableDataChange,
69
+ showAdminColumns = false,
70
+ showTimeFilter = false,
71
+ source,
72
+ mode = 'portal',
73
+ } = props;
74
+ const listKey = 'credit-transactions-table';
75
+ const { t, locale } = useLocaleContext();
76
+ const { session } = usePaymentContext();
77
+ const isAdmin = ['owner', 'admin'].includes(session?.user?.role || '');
78
+ const navigate = useNavigate();
79
+
80
+ // 如果没有传入 customer_id,使用当前登录用户的 DID
81
+ const effectiveCustomerId = customer_id || session?.user?.did;
82
+
83
+ const [search, setSearch] = useState<{
84
+ pageSize: number;
85
+ page: number;
86
+ start?: number;
87
+ end?: number;
88
+ }>({
89
+ pageSize: pageSize || 10,
90
+ page: 1,
91
+ });
92
+
93
+ const [filters, setFilters] = useState<DateRangeValue>({
94
+ start: undefined,
95
+ end: undefined,
96
+ });
97
+
98
+ const handleDateRangeChange = useCallback((newValue: DateRangeValue) => {
99
+ setFilters(newValue);
100
+ setSearch((prev) => ({
101
+ ...prev,
102
+ page: 1,
103
+ start: newValue.start || undefined,
104
+ end: newValue.end || undefined,
105
+ }));
106
+ }, []);
107
+
108
+ const { loading, data = { list: [], count: 0 } } = useRequest(
109
+ () =>
110
+ fetchData({
111
+ ...search,
112
+ customer_id: effectiveCustomerId,
113
+ subscription_id,
114
+ credit_grant_id,
115
+ source,
116
+ }),
117
+ {
118
+ refreshDeps: [search, effectiveCustomerId, subscription_id, credit_grant_id, source],
119
+ }
120
+ );
121
+
122
+ // 初始化时应用默认日期筛选
123
+ useEffect(() => {
124
+ if (showTimeFilter && !search.start && !search.end) {
125
+ handleDateRangeChange(filters);
126
+ }
127
+ }, [showTimeFilter, handleDateRangeChange, search.start, search.end, filters]);
128
+
129
+ const prevData = useRef(data);
130
+
131
+ useEffect(() => {
132
+ if (onTableDataChange) {
133
+ onTableDataChange(data, prevData.current);
134
+ prevData.current = data;
135
+ }
136
+ // eslint-disable-next-line react-hooks/exhaustive-deps
137
+ }, [data]);
138
+
139
+ const columns = [
140
+ {
141
+ label: t('common.creditAmount'),
142
+ name: 'credit_amount',
143
+ align: 'right',
144
+ width: 120,
145
+ options: {
146
+ customBodyRenderLite: (_: string, index: number) => {
147
+ const transaction = data?.list[index] as TCreditTransactionExpanded;
148
+ const unit = transaction.meter?.unit || transaction.paymentCurrency.symbol;
149
+ return (
150
+ <Typography>
151
+ {formatBNStr(transaction.credit_amount, transaction.paymentCurrency.decimal)} {unit}
152
+ </Typography>
153
+ );
154
+ },
155
+ },
156
+ },
157
+ !credit_grant_id && {
158
+ label: t('common.creditGrant'),
159
+ name: 'credit_grant',
160
+ options: {
161
+ customBodyRenderLite: (_: string, index: number) => {
162
+ const transaction = data?.list[index] as TCreditTransactionExpanded;
163
+ return (
164
+ <Stack
165
+ direction="row"
166
+ spacing={1}
167
+ onClick={(e) => {
168
+ const link = getGrantDetailLink(transaction.credit_grant_id, isAdmin && mode === 'dashboard');
169
+ handleNavigation(e, link.link, navigate);
170
+ }}
171
+ sx={{
172
+ alignItems: 'center',
173
+ }}>
174
+ <Typography variant="body2" sx={{ color: 'text.link', cursor: 'pointer' }}>
175
+ {transaction.creditGrant.name || `Grant ${transaction.credit_grant_id.slice(-6)}`}
176
+ </Typography>
177
+ </Stack>
178
+ );
179
+ },
180
+ },
181
+ },
182
+ {
183
+ label: t('common.description'),
184
+ name: 'subscription',
185
+ options: {
186
+ customBodyRenderLite: (_: string, index: number) => {
187
+ const transaction = data?.list[index] as TCreditTransactionExpanded;
188
+ return (
189
+ <Typography variant="body2">{transaction.subscription?.description || transaction.description}</Typography>
190
+ );
191
+ },
192
+ },
193
+ },
194
+ ...(showAdminColumns && isAdmin
195
+ ? [
196
+ {
197
+ label: t('common.meterEvent'),
198
+ name: 'meter_event',
199
+ options: {
200
+ customBodyRenderLite: (_: string, index: number) => {
201
+ const transaction = data?.list[index] as TCreditTransactionExpanded;
202
+ if (!transaction.meter) {
203
+ return <Typography variant="body2">-</Typography>;
204
+ }
205
+ return (
206
+ <Link href={joinURL(getPrefix(), `/admin/billing/${transaction.meter.id}`)}>
207
+ <Typography variant="body2" sx={{ color: 'text.link' }}>
208
+ {transaction.meter.event_name}
209
+ </Typography>
210
+ </Link>
211
+ );
212
+ },
213
+ },
214
+ },
215
+ ]
216
+ : []),
217
+ {
218
+ label: t('admin.creditTransactions.transactionDate'),
219
+ name: 'created_at',
220
+ options: {
221
+ customBodyRenderLite: (_: string, index: number) => {
222
+ const transaction = data?.list[index] as TCreditTransactionExpanded;
223
+ return (
224
+ <Typography variant="body2">
225
+ {formatToDate(transaction.created_at, locale, 'YYYY-MM-DD HH:mm:ss')}
226
+ </Typography>
227
+ );
228
+ },
229
+ },
230
+ },
231
+ ].filter(Boolean);
232
+
233
+ const onTableChange = ({ page, rowsPerPage }: any) => {
234
+ if (search.pageSize !== rowsPerPage) {
235
+ setSearch((x) => ({ ...x, pageSize: rowsPerPage, page: 1 }));
236
+ } else if (search.page !== page + 1) {
237
+ setSearch((x) => ({ ...x, page: page + 1 }));
238
+ }
239
+ };
240
+
241
+ return (
242
+ <TableRoot>
243
+ {showTimeFilter && (
244
+ <Box sx={{ my: 2 }}>
245
+ <Box sx={{ mt: 2 }}>
246
+ <Grid
247
+ container
248
+ spacing={2}
249
+ sx={{
250
+ alignItems: 'center',
251
+ }}>
252
+ <Grid
253
+ size={{
254
+ xs: 12,
255
+ sm: 6,
256
+ md: 4,
257
+ }}>
258
+ <DateRangePicker value={filters} onChange={handleDateRangeChange} size="small" fullWidth />
259
+ </Grid>
260
+ </Grid>
261
+ </Box>
262
+ </Box>
263
+ )}
264
+ <Table
265
+ hasRowLink
266
+ durable={`__${listKey}__`}
267
+ durableKeys={['page', 'rowsPerPage']}
268
+ data={data.list}
269
+ columns={columns}
270
+ options={{
271
+ count: data.count,
272
+ page: search.page - 1,
273
+ rowsPerPage: search.pageSize,
274
+ }}
275
+ loading={loading}
276
+ onChange={onTableChange}
277
+ toolbar={false}
278
+ sx={{ mt: 2 }}
279
+ showMobile={false}
280
+ mobileTDFlexDirection="row"
281
+ emptyNodeText={t('admin.creditTransactions.noTransactions')}
282
+ />
283
+ </TableRoot>
284
+ );
285
+ });
286
+
287
+ const TableRoot = styled(Box)`
288
+ @media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
289
+ .MuiTable-root > .MuiTableBody-root > .MuiTableRow-root > td.MuiTableCell-root {
290
+ > div {
291
+ width: fit-content;
292
+ flex: inherit;
293
+ font-size: 14px;
294
+ }
295
+ }
296
+ .invoice-summary {
297
+ padding-right: 20px;
298
+ }
299
+ }
300
+ `;
301
+ export default function CreditTransactionsList(rawProps: Props) {
302
+ const props = Object.assign(
303
+ {
304
+ customer_id: '',
305
+ subscription_id: '',
306
+ credit_grant_id: '',
307
+ source: '',
308
+ pageSize: 10,
309
+ onTableDataChange: () => {},
310
+ showAdminColumns: false,
311
+ showTimeFilter: false,
312
+ mode: 'portal',
313
+ },
314
+ rawProps
315
+ );
316
+ return <TransactionsTable {...props} />;
317
+ }
@@ -8,7 +8,7 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
8
8
  import Toast from '@arcblock/ux/lib/Toast';
9
9
  import type { Paginated, TInvoiceExpanded, TSubscription } from '@blocklet/payment-types';
10
10
  import { OpenInNewOutlined } from '@mui/icons-material';
11
- import { Box, Button, CircularProgress, Stack, Typography, Tooltip } from '@mui/material';
11
+ import { Box, Button, CircularProgress, Stack, Typography, Tooltip, Avatar } from '@mui/material';
12
12
  import { styled } from '@mui/system';
13
13
  import { useInfiniteScroll, useRequest, useSetState } from 'ahooks';
14
14
  import React, { useEffect, useRef, useState } from 'react';
@@ -207,6 +207,23 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
207
207
  },
208
208
  },
209
209
  },
210
+ {
211
+ label: t('common.paymentMethod'),
212
+ name: 'paymentMethod',
213
+ options: {
214
+ customBodyRenderLite: (_: string, index: number) => {
215
+ const invoice = data?.list[index] as TInvoiceExpanded;
216
+ return (
217
+ <Typography
218
+ sx={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap' }}
219
+ onClick={(e) => handleLinkClick(e, invoice)}>
220
+ <Avatar src={invoice.paymentMethod.logo} sx={{ width: 18, height: 18, mr: 1 }} />
221
+ {invoice.paymentMethod.name}
222
+ </Typography>
223
+ );
224
+ },
225
+ },
226
+ },
210
227
  {
211
228
  label: t('common.type'),
212
229
  name: 'billing_reason',
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ import TxGas from './components/blockchain/gas';
5
5
  import TxLink from './components/blockchain/tx';
6
6
  import ConfirmDialog from './components/confirm';
7
7
  import FormInput from './components/input';
8
+ import FormLabel from './components/label';
8
9
  import Livemode from './components/livemode';
9
10
  import PricingTable from './components/pricing-table';
10
11
  import Table from './components/table';
@@ -13,6 +14,8 @@ import Status from './components/status';
13
14
  import Switch from './components/switch-button';
14
15
  import CustomerInvoiceList from './history/invoice/list';
15
16
  import CustomerPaymentList from './history/payment/list';
17
+ import CreditGrantsList, { StatusChip as CreditStatusChip } from './history/credit/grants-list';
18
+ import CreditTransactionsList from './history/credit/transactions-list';
16
19
  import api from './libs/api';
17
20
  import dayjs from './libs/dayjs';
18
21
  import Amount from './payment/amount';
@@ -32,6 +35,7 @@ import OverdueInvoicePayment from './components/over-due-invoice-payment';
32
35
  import PaymentBeneficiaries from './components/payment-beneficiaries';
33
36
  import LoadingButton from './components/loading-button';
34
37
  import ResumeSubscription from './components/resume-subscription';
38
+ import DateRangePicker from './components/date-range-picker';
35
39
 
36
40
  export { PaymentThemeProvider } from './theme';
37
41
 
@@ -55,6 +59,7 @@ export {
55
59
  api,
56
60
  dayjs,
57
61
  FormInput,
62
+ FormLabel,
58
63
  PhoneInput,
59
64
  AddressForm,
60
65
  StripeForm,
@@ -86,4 +91,8 @@ export {
86
91
  LoadingButton,
87
92
  DonateDetails,
88
93
  ResumeSubscription,
94
+ CreditGrantsList,
95
+ CreditTransactionsList,
96
+ DateRangePicker,
97
+ CreditStatusChip,
89
98
  };
package/src/libs/util.ts CHANGED
@@ -1283,3 +1283,17 @@ export function getTokenBalanceLink(method: TPaymentMethod, address: string) {
1283
1283
  }
1284
1284
  return '';
1285
1285
  }
1286
+
1287
+ export function isCreditMetered(price: TPrice): boolean {
1288
+ return !!(price.type === 'recurring' && price.recurring?.usage_type === 'metered' && price.recurring?.meter_id);
1289
+ }
1290
+
1291
+ export function showStaking(method: TPaymentMethod, currency: TPaymentCurrency, noStake: boolean) {
1292
+ if (noStake) {
1293
+ return false;
1294
+ }
1295
+ if (method.type === 'arcblock') {
1296
+ return currency.type !== 'credit';
1297
+ }
1298
+ return true;
1299
+ }