@blocklet/payment-react 1.18.56 → 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 (251) hide show
  1. package/es/checkout/donate.d.ts +1 -15
  2. package/es/checkout/donate.js +301 -189
  3. package/es/checkout/form.d.ts +1 -15
  4. package/es/checkout/form.js +5 -13
  5. package/es/checkout/table.js +3 -3
  6. package/es/components/blockchain/gas.d.ts +1 -5
  7. package/es/components/blockchain/gas.js +10 -2
  8. package/es/components/blockchain/tx.d.ts +1 -8
  9. package/es/components/blockchain/tx.js +29 -10
  10. package/es/components/confirm.d.ts +1 -10
  11. package/es/components/confirm.js +4 -10
  12. package/es/components/country-select.d.ts +3 -2
  13. package/es/components/country-select.js +375 -352
  14. package/es/components/date-range-picker.d.ts +13 -0
  15. package/es/components/date-range-picker.js +279 -0
  16. package/es/components/input.d.ts +14 -20
  17. package/es/components/input.js +51 -44
  18. package/es/components/label.d.ts +7 -0
  19. package/es/components/label.js +49 -0
  20. package/es/components/lazy-loader.js +1 -2
  21. package/es/components/link.d.ts +2 -9
  22. package/es/components/link.js +9 -6
  23. package/es/components/livemode.d.ts +2 -8
  24. package/es/components/livemode.js +1 -5
  25. package/es/components/loading-button.d.ts +6 -1
  26. package/es/components/loading-button.js +56 -66
  27. package/es/components/over-due-invoice-payment.d.ts +0 -18
  28. package/es/components/over-due-invoice-payment.js +138 -95
  29. package/es/components/payment-beneficiaries.d.ts +2 -7
  30. package/es/components/payment-beneficiaries.js +86 -40
  31. package/es/components/pricing-item.d.ts +0 -5
  32. package/es/components/pricing-item.js +1 -4
  33. package/es/components/pricing-table.d.ts +2 -10
  34. package/es/components/pricing-table.js +8 -7
  35. package/es/components/resume-subscription.d.ts +0 -10
  36. package/es/components/resume-subscription.js +42 -21
  37. package/es/components/truncated-text.d.ts +2 -9
  38. package/es/components/truncated-text.js +0 -5
  39. package/es/contexts/donate.d.ts +0 -7
  40. package/es/contexts/donate.js +10 -8
  41. package/es/contexts/payment.d.ts +1 -4
  42. package/es/contexts/payment.js +7 -2
  43. package/es/history/credit/grants-list.d.ts +14 -0
  44. package/es/history/credit/grants-list.js +215 -0
  45. package/es/history/credit/transactions-list.d.ts +13 -0
  46. package/es/history/credit/transactions-list.js +255 -0
  47. package/es/history/invoice/list.d.ts +2 -18
  48. package/es/history/invoice/list.js +172 -74
  49. package/es/history/payment/list.js +115 -38
  50. package/es/hooks/keyboard.d.ts +1 -1
  51. package/es/hooks/keyboard.js +2 -4
  52. package/es/index.d.ts +5 -1
  53. package/es/index.js +10 -1
  54. package/es/libs/cached-request.js +2 -4
  55. package/es/libs/phone-validator.js +1 -2
  56. package/es/libs/util.d.ts +2 -0
  57. package/es/libs/util.js +14 -4
  58. package/es/libs/validator.js +2 -4
  59. package/es/locales/en.js +20 -2
  60. package/es/locales/zh.js +20 -2
  61. package/es/payment/amount.d.ts +2 -7
  62. package/es/payment/amount.js +1 -5
  63. package/es/payment/donation-form.d.ts +2 -10
  64. package/es/payment/donation-form.js +196 -160
  65. package/es/payment/error.d.ts +2 -8
  66. package/es/payment/error.js +40 -20
  67. package/es/payment/footer.d.ts +2 -3
  68. package/es/payment/footer.js +19 -6
  69. package/es/payment/form/addon.js +14 -4
  70. package/es/payment/form/address.d.ts +2 -9
  71. package/es/payment/form/address.js +3 -6
  72. package/es/payment/form/currency.js +45 -25
  73. package/es/payment/form/index.d.ts +2 -8
  74. package/es/payment/form/index.js +151 -71
  75. package/es/payment/form/phone.js +2 -4
  76. package/es/payment/form/stripe/form.d.ts +2 -8
  77. package/es/payment/form/stripe/form.js +1 -3
  78. package/es/payment/header.js +38 -16
  79. package/es/payment/index.d.ts +2 -9
  80. package/es/payment/index.js +23 -17
  81. package/es/payment/product-card.d.ts +2 -11
  82. package/es/payment/product-card.js +84 -50
  83. package/es/payment/product-donation.js +175 -114
  84. package/es/payment/product-item.d.ts +9 -9
  85. package/es/payment/product-item.js +320 -145
  86. package/es/payment/product-skeleton.js +2 -2
  87. package/es/payment/skeleton/donation.js +27 -7
  88. package/es/payment/skeleton/overview.js +22 -2
  89. package/es/payment/skeleton/payment.js +33 -5
  90. package/es/payment/success.d.ts +2 -9
  91. package/es/payment/success.js +41 -14
  92. package/es/payment/summary.d.ts +4 -17
  93. package/es/payment/summary.js +193 -111
  94. package/es/theme/index.d.ts +0 -5
  95. package/es/theme/index.js +2 -5
  96. package/es/theme/typography.d.ts +2 -2
  97. package/lib/checkout/donate.d.ts +1 -15
  98. package/lib/checkout/donate.js +75 -54
  99. package/lib/checkout/form.d.ts +1 -15
  100. package/lib/checkout/form.js +7 -15
  101. package/lib/checkout/table.js +4 -4
  102. package/lib/components/blockchain/gas.d.ts +1 -5
  103. package/lib/components/blockchain/gas.js +3 -2
  104. package/lib/components/blockchain/tx.d.ts +1 -8
  105. package/lib/components/blockchain/tx.js +15 -10
  106. package/lib/components/confirm.d.ts +1 -10
  107. package/lib/components/confirm.js +5 -11
  108. package/lib/components/country-select.d.ts +3 -2
  109. package/lib/components/country-select.js +23 -22
  110. package/lib/components/date-range-picker.d.ts +13 -0
  111. package/lib/components/date-range-picker.js +329 -0
  112. package/lib/components/input.d.ts +14 -20
  113. package/lib/components/input.js +28 -27
  114. package/lib/components/label.d.ts +7 -0
  115. package/lib/components/label.js +60 -0
  116. package/lib/components/lazy-loader.js +1 -1
  117. package/lib/components/link.d.ts +2 -9
  118. package/lib/components/link.js +3 -8
  119. package/lib/components/livemode.d.ts +2 -8
  120. package/lib/components/livemode.js +3 -7
  121. package/lib/components/loading-button.d.ts +6 -1
  122. package/lib/components/loading-button.js +9 -17
  123. package/lib/components/over-due-invoice-payment.d.ts +0 -18
  124. package/lib/components/over-due-invoice-payment.js +31 -33
  125. package/lib/components/payment-beneficiaries.d.ts +2 -7
  126. package/lib/components/payment-beneficiaries.js +12 -11
  127. package/lib/components/pricing-item.d.ts +0 -5
  128. package/lib/components/pricing-item.js +2 -5
  129. package/lib/components/pricing-table.d.ts +2 -10
  130. package/lib/components/pricing-table.js +5 -11
  131. package/lib/components/resume-subscription.d.ts +0 -10
  132. package/lib/components/resume-subscription.js +16 -16
  133. package/lib/components/table.js +1 -1
  134. package/lib/components/truncated-text.d.ts +2 -9
  135. package/lib/components/truncated-text.js +1 -6
  136. package/lib/contexts/donate.d.ts +0 -7
  137. package/lib/contexts/donate.js +4 -7
  138. package/lib/contexts/payment.d.ts +1 -4
  139. package/lib/contexts/payment.js +4 -7
  140. package/lib/history/credit/grants-list.d.ts +14 -0
  141. package/lib/history/credit/grants-list.js +277 -0
  142. package/lib/history/credit/transactions-list.d.ts +13 -0
  143. package/lib/history/credit/transactions-list.js +301 -0
  144. package/lib/history/invoice/list.d.ts +2 -18
  145. package/lib/history/invoice/list.js +73 -37
  146. package/lib/history/payment/list.js +30 -16
  147. package/lib/hooks/keyboard.d.ts +1 -1
  148. package/lib/hooks/mobile.js +1 -1
  149. package/lib/hooks/subscription.js +1 -1
  150. package/lib/index.d.ts +5 -1
  151. package/lib/index.js +41 -2
  152. package/lib/libs/api.js +1 -1
  153. package/lib/libs/dayjs.js +1 -1
  154. package/lib/libs/phone-validator.js +0 -2
  155. package/lib/libs/theme.js +1 -1
  156. package/lib/libs/util.d.ts +2 -0
  157. package/lib/libs/util.js +15 -1
  158. package/lib/libs/validator.js +1 -1
  159. package/lib/locales/en.js +21 -3
  160. package/lib/locales/index.js +1 -1
  161. package/lib/locales/zh.js +21 -3
  162. package/lib/payment/amount.d.ts +2 -7
  163. package/lib/payment/amount.js +2 -6
  164. package/lib/payment/donation-form.d.ts +2 -10
  165. package/lib/payment/donation-form.js +33 -38
  166. package/lib/payment/error.d.ts +2 -8
  167. package/lib/payment/error.js +11 -13
  168. package/lib/payment/footer.d.ts +2 -3
  169. package/lib/payment/footer.js +5 -5
  170. package/lib/payment/form/addon.js +5 -3
  171. package/lib/payment/form/address.d.ts +2 -9
  172. package/lib/payment/form/address.js +5 -8
  173. package/lib/payment/form/currency.js +3 -3
  174. package/lib/payment/form/index.d.ts +2 -8
  175. package/lib/payment/form/index.js +64 -21
  176. package/lib/payment/form/phone.js +1 -1
  177. package/lib/payment/form/stripe/form.d.ts +2 -8
  178. package/lib/payment/form/stripe/form.js +3 -6
  179. package/lib/payment/header.js +8 -4
  180. package/lib/payment/index.d.ts +2 -9
  181. package/lib/payment/index.js +27 -18
  182. package/lib/payment/product-card.d.ts +2 -11
  183. package/lib/payment/product-card.js +13 -20
  184. package/lib/payment/product-donation.js +71 -66
  185. package/lib/payment/product-item.d.ts +9 -9
  186. package/lib/payment/product-item.js +168 -29
  187. package/lib/payment/product-skeleton.js +2 -2
  188. package/lib/payment/skeleton/donation.js +8 -4
  189. package/lib/payment/skeleton/overview.js +6 -2
  190. package/lib/payment/skeleton/payment.js +9 -3
  191. package/lib/payment/success.d.ts +2 -9
  192. package/lib/payment/success.js +12 -15
  193. package/lib/payment/summary.d.ts +4 -17
  194. package/lib/payment/summary.js +53 -45
  195. package/lib/theme/index.d.ts +0 -5
  196. package/lib/theme/index.js +2 -5
  197. package/lib/theme/typography.d.ts +2 -2
  198. package/package.json +40 -40
  199. package/src/checkout/donate.tsx +103 -35
  200. package/src/checkout/form.tsx +5 -14
  201. package/src/checkout/table.tsx +3 -3
  202. package/src/components/blockchain/gas.tsx +5 -3
  203. package/src/components/blockchain/tx.tsx +19 -11
  204. package/src/components/confirm.tsx +4 -11
  205. package/src/components/country-select.tsx +391 -378
  206. package/src/components/date-range-picker.tsx +310 -0
  207. package/src/components/input.tsx +61 -46
  208. package/src/components/label.tsx +58 -0
  209. package/src/components/link.tsx +9 -7
  210. package/src/components/livemode.tsx +2 -6
  211. package/src/components/loading-button.tsx +63 -76
  212. package/src/components/over-due-invoice-payment.tsx +43 -28
  213. package/src/components/payment-beneficiaries.tsx +33 -14
  214. package/src/components/pricing-item.tsx +1 -4
  215. package/src/components/pricing-table.tsx +8 -8
  216. package/src/components/resume-subscription.tsx +20 -14
  217. package/src/components/table.tsx +2 -2
  218. package/src/components/truncated-text.tsx +0 -6
  219. package/src/contexts/donate.tsx +6 -7
  220. package/src/contexts/payment.tsx +7 -3
  221. package/src/history/credit/grants-list.tsx +276 -0
  222. package/src/history/credit/transactions-list.tsx +317 -0
  223. package/src/history/invoice/list.tsx +92 -36
  224. package/src/history/payment/list.tsx +53 -16
  225. package/src/hooks/keyboard.ts +1 -1
  226. package/src/index.ts +9 -0
  227. package/src/libs/util.ts +14 -0
  228. package/src/locales/en.tsx +20 -0
  229. package/src/locales/zh.tsx +19 -0
  230. package/src/payment/amount.tsx +1 -6
  231. package/src/payment/donation-form.tsx +47 -29
  232. package/src/payment/error.tsx +16 -8
  233. package/src/payment/footer.tsx +11 -3
  234. package/src/payment/form/addon.tsx +6 -1
  235. package/src/payment/form/address.tsx +3 -7
  236. package/src/payment/form/currency.tsx +12 -2
  237. package/src/payment/form/index.tsx +121 -45
  238. package/src/payment/form/stripe/form.tsx +1 -5
  239. package/src/payment/header.tsx +14 -2
  240. package/src/payment/index.tsx +27 -22
  241. package/src/payment/product-card.tsx +41 -18
  242. package/src/payment/product-donation.tsx +85 -47
  243. package/src/payment/product-item.tsx +198 -28
  244. package/src/payment/product-skeleton.tsx +3 -2
  245. package/src/payment/skeleton/donation.tsx +12 -2
  246. package/src/payment/skeleton/overview.tsx +12 -2
  247. package/src/payment/skeleton/payment.tsx +16 -3
  248. package/src/payment/success.tsx +26 -15
  249. package/src/payment/summary.tsx +87 -44
  250. package/src/theme/index.tsx +5 -8
  251. package/src/theme/typography.ts +2 -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
+ }