payment-kit 1.14.21 → 1.14.23
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.
- package/blocklet.yml +1 -1
- package/package.json +19 -19
- package/src/app.tsx +13 -12
- package/src/components/balance-list.tsx +12 -2
- package/src/components/copyable.tsx +3 -2
- package/src/components/customer/edit.tsx +25 -21
- package/src/components/customer/form.tsx +18 -28
- package/src/components/customer/link.tsx +1 -2
- package/src/components/date-range-picker.tsx +21 -0
- package/src/components/drawer-form.tsx +27 -4
- package/src/components/event/list.tsx +3 -2
- package/src/components/filter-toolbar.tsx +11 -4
- package/src/components/info-card.tsx +4 -2
- package/src/components/info-metric.tsx +2 -2
- package/src/components/info-row.tsx +33 -26
- package/src/components/invoice/list.tsx +2 -2
- package/src/components/invoice/table.tsx +148 -85
- package/src/components/invoice-pdf/pdf.tsx +5 -1
- package/src/components/layout/admin.tsx +8 -2
- package/src/components/metadata/editor.tsx +25 -18
- package/src/components/metadata/form.tsx +83 -25
- package/src/components/metadata/list.tsx +22 -6
- package/src/components/payment-intent/list.tsx +3 -3
- package/src/components/payment-link/preview.tsx +42 -24
- package/src/components/payouts/list.tsx +2 -3
- package/src/components/price/form.tsx +28 -15
- package/src/components/price/upsell.tsx +1 -4
- package/src/components/pricing-table/preview.tsx +42 -23
- package/src/components/product/cross-sell-select.tsx +1 -1
- package/src/components/product/cross-sell.tsx +3 -4
- package/src/components/product/edit-price.tsx +0 -1
- package/src/components/refund/list.tsx +9 -4
- package/src/components/section/header.tsx +11 -4
- package/src/components/subscription/description.tsx +10 -6
- package/src/components/subscription/items/index.tsx +28 -6
- package/src/components/subscription/list.tsx +2 -2
- package/src/components/subscription/metrics.tsx +10 -8
- package/src/components/subscription/portal/actions.tsx +37 -11
- package/src/components/subscription/portal/list.tsx +131 -70
- package/src/components/subscription/status.tsx +9 -3
- package/src/global.css +1 -1
- package/src/hooks/mobile.ts +3 -3
- package/src/libs/util.ts +6 -2
- package/src/locales/en.tsx +37 -1
- package/src/locales/zh.tsx +37 -1
- package/src/pages/admin/billing/index.tsx +24 -4
- package/src/pages/admin/billing/invoices/detail.tsx +302 -147
- package/src/pages/admin/billing/subscriptions/detail.tsx +259 -134
- package/src/pages/admin/customers/customers/detail.tsx +358 -175
- package/src/pages/admin/customers/customers/index.tsx +8 -5
- package/src/pages/admin/customers/index.tsx +22 -5
- package/src/pages/admin/developers/webhooks/index.tsx +2 -2
- package/src/pages/admin/index.tsx +24 -10
- package/src/pages/admin/overview.tsx +1 -1
- package/src/pages/admin/payments/index.tsx +22 -7
- package/src/pages/admin/payments/intents/detail.tsx +263 -121
- package/src/pages/admin/payments/payouts/detail.tsx +235 -102
- package/src/pages/admin/payments/refunds/detail.tsx +286 -133
- package/src/pages/admin/products/index.tsx +28 -12
- package/src/pages/admin/products/links/create.tsx +16 -6
- package/src/pages/admin/products/links/detail.tsx +280 -176
- package/src/pages/admin/products/links/index.tsx +4 -7
- package/src/pages/admin/products/passports/index.tsx +6 -3
- package/src/pages/admin/products/prices/detail.tsx +260 -139
- package/src/pages/admin/products/prices/list.tsx +7 -3
- package/src/pages/admin/products/pricing-tables/create.tsx +17 -5
- package/src/pages/admin/products/pricing-tables/detail.tsx +221 -121
- package/src/pages/admin/products/pricing-tables/index.tsx +1 -2
- package/src/pages/admin/products/products/detail.tsx +262 -119
- package/src/pages/admin/products/products/index.tsx +1 -2
- package/src/pages/admin/settings/index.tsx +27 -7
- package/src/pages/customer/index.tsx +431 -143
- package/src/pages/customer/invoice/detail.tsx +138 -26
- package/src/pages/customer/refund/list.tsx +193 -4
- package/src/pages/customer/subscription/change-payment.tsx +20 -20
- package/src/pages/customer/subscription/detail.tsx +168 -34
- package/src/pages/customer/subscription/embed.tsx +22 -19
- package/src/pages/home.tsx +7 -1
- package/src/components/table.tsx +0 -93
|
@@ -4,17 +4,15 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
|
4
4
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
5
5
|
import { api, formatBNStr, formatError, formatTime, usePaymentContext } from '@blocklet/payment-react';
|
|
6
6
|
import type { GroupedBN, TCustomerExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
7
|
-
import { ArrowBackOutlined
|
|
8
|
-
import { Alert, Box, Button, CircularProgress, Stack, Typography } from '@mui/material';
|
|
7
|
+
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
8
|
+
import { Alert, Avatar, Box, Button, CircularProgress, Divider, Stack, Typography } from '@mui/material';
|
|
9
9
|
import { styled } from '@mui/system';
|
|
10
10
|
import { useRequest, useSetState } from 'ahooks';
|
|
11
|
-
import {
|
|
12
|
-
import { FlagEmoji } from 'react-international-phone';
|
|
13
|
-
import { useNavigate } from 'react-router-dom';
|
|
11
|
+
import { defaultCountries, FlagEmoji, parseCountry } from 'react-international-phone';
|
|
14
12
|
|
|
13
|
+
import { useMemo } from 'react';
|
|
15
14
|
import BalanceList from '../../../../components/balance-list';
|
|
16
15
|
import Copyable from '../../../../components/copyable';
|
|
17
|
-
import CustomerActions from '../../../../components/customer/actions';
|
|
18
16
|
import EditCustomer from '../../../../components/customer/edit';
|
|
19
17
|
import EventList from '../../../../components/event/list';
|
|
20
18
|
import InfoMetric from '../../../../components/info-metric';
|
|
@@ -27,6 +25,7 @@ import RefundList from '../../../../components/refund/list';
|
|
|
27
25
|
import SectionHeader from '../../../../components/section/header';
|
|
28
26
|
import SubscriptionList from '../../../../components/subscription/list';
|
|
29
27
|
import { goBackOrFallback } from '../../../../libs/util';
|
|
28
|
+
import MetadataList from '../../../../components/metadata/list';
|
|
30
29
|
|
|
31
30
|
const fetchData = async (
|
|
32
31
|
id: string
|
|
@@ -42,6 +41,9 @@ const fetchData = async (
|
|
|
42
41
|
};
|
|
43
42
|
};
|
|
44
43
|
|
|
44
|
+
const InfoDirection = 'column';
|
|
45
|
+
const InfoAlignItems = 'flex-start';
|
|
46
|
+
|
|
45
47
|
function getTokenBalances(customer: TCustomerExpanded, paymentMethods: TPaymentMethodExpanded[]) {
|
|
46
48
|
const tokens: { currency: string; balance: string }[] = [];
|
|
47
49
|
// @ts-ignore
|
|
@@ -74,7 +76,6 @@ function getTokenBalances(customer: TCustomerExpanded, paymentMethods: TPaymentM
|
|
|
74
76
|
|
|
75
77
|
export default function CustomerDetail(props: { id: string }) {
|
|
76
78
|
const { t } = useLocaleContext();
|
|
77
|
-
const navigate = useNavigate();
|
|
78
79
|
const { settings } = usePaymentContext();
|
|
79
80
|
const [state, setState] = useSetState({
|
|
80
81
|
adding: {
|
|
@@ -91,7 +92,10 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
const { loading, error, data, runAsync } = useRequest(() => fetchData(props.id));
|
|
94
|
-
|
|
95
|
+
const countryDetail = useMemo(() => {
|
|
96
|
+
const item = defaultCountries.find((v) => v[1] === data?.customer?.address?.country);
|
|
97
|
+
return item ? parseCountry(item) : { name: '' };
|
|
98
|
+
}, [data]);
|
|
95
99
|
if (error) {
|
|
96
100
|
return <Alert severity="error">{error.message}</Alert>;
|
|
97
101
|
}
|
|
@@ -128,15 +132,18 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
128
132
|
}
|
|
129
133
|
};
|
|
130
134
|
|
|
131
|
-
const onChange = (action: string) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
};
|
|
135
|
+
// const onChange = (action: string) => {
|
|
136
|
+
// if (action === 'remove') {
|
|
137
|
+
// navigate('/admin/customers');
|
|
138
|
+
// } else {
|
|
139
|
+
// runAsync();
|
|
140
|
+
// }
|
|
141
|
+
// };
|
|
138
142
|
|
|
139
143
|
const tokenBalances = getTokenBalances(data.customer, settings.paymentMethods);
|
|
144
|
+
const handleEditMetadata = () => {
|
|
145
|
+
setState((prev) => ({ editing: { ...prev.editing, metadata: true } }));
|
|
146
|
+
};
|
|
140
147
|
|
|
141
148
|
return (
|
|
142
149
|
<Root direction="column" spacing={4} sx={{ mb: 4 }}>
|
|
@@ -152,181 +159,357 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
152
159
|
{t('admin.customers')}
|
|
153
160
|
</Typography>
|
|
154
161
|
</Stack>
|
|
155
|
-
<
|
|
162
|
+
<Button
|
|
163
|
+
variant="outlined"
|
|
164
|
+
color="primary"
|
|
165
|
+
size="small"
|
|
166
|
+
disabled={state.editing.customer}
|
|
167
|
+
onClick={() => setState((prev) => ({ editing: { ...prev.editing, customer: true } }))}>
|
|
168
|
+
{t('common.edit')}
|
|
169
|
+
</Button>
|
|
170
|
+
{/* <CustomerActions data={data.customer} onChange={onChange} variant="normal" /> */}
|
|
156
171
|
</Stack>
|
|
157
|
-
<Box
|
|
172
|
+
<Box
|
|
173
|
+
mt={4}
|
|
174
|
+
mb={3}
|
|
175
|
+
sx={{
|
|
176
|
+
display: 'flex',
|
|
177
|
+
gap: {
|
|
178
|
+
xs: 2,
|
|
179
|
+
sm: 2,
|
|
180
|
+
md: 5,
|
|
181
|
+
},
|
|
182
|
+
flexWrap: 'wrap',
|
|
183
|
+
flexDirection: {
|
|
184
|
+
xs: 'column',
|
|
185
|
+
sm: 'column',
|
|
186
|
+
md: 'row',
|
|
187
|
+
},
|
|
188
|
+
alignItems: {
|
|
189
|
+
xs: 'flex-start',
|
|
190
|
+
sm: 'flex-start',
|
|
191
|
+
md: 'center',
|
|
192
|
+
},
|
|
193
|
+
}}>
|
|
158
194
|
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
|
159
|
-
<
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
195
|
+
<Stack direction="row" alignItems="center" spacing={1}>
|
|
196
|
+
<Avatar
|
|
197
|
+
title={data.customer.name}
|
|
198
|
+
src={`/.well-known/service/user/avatar/${data?.customer?.did}`}
|
|
199
|
+
variant="square"
|
|
200
|
+
sx={{ width: 52, height: 52, borderRadius: 'var(--radius-s, 4px)' }}
|
|
201
|
+
/>
|
|
202
|
+
<Typography variant="h2" sx={{ fontWeight: 600 }}>
|
|
203
|
+
{data.customer.name}
|
|
204
|
+
</Typography>
|
|
205
|
+
</Stack>
|
|
163
206
|
</Stack>
|
|
164
207
|
<Stack
|
|
165
208
|
className="section-body"
|
|
166
|
-
direction="row"
|
|
167
|
-
spacing={3}
|
|
168
209
|
justifyContent="flex-start"
|
|
169
210
|
flexWrap="wrap"
|
|
170
|
-
sx={{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
211
|
+
sx={{
|
|
212
|
+
'hr.MuiDivider-root:last-child': {
|
|
213
|
+
display: 'none',
|
|
214
|
+
},
|
|
215
|
+
flexDirection: {
|
|
216
|
+
xs: 'column',
|
|
217
|
+
sm: 'column',
|
|
218
|
+
md: 'row',
|
|
219
|
+
},
|
|
220
|
+
alignItems: {
|
|
221
|
+
xs: 'flex-start',
|
|
222
|
+
sm: 'flex-start',
|
|
223
|
+
md: 'center',
|
|
224
|
+
},
|
|
225
|
+
gap: {
|
|
226
|
+
xs: 1,
|
|
227
|
+
sm: 1,
|
|
228
|
+
md: 3,
|
|
229
|
+
},
|
|
230
|
+
}}>
|
|
231
|
+
<InfoMetric label={t('admin.customer.email')} value={data.customer.email} divider />
|
|
232
|
+
<InfoMetric label={t('admin.customer.phone')} value={data.customer.phone} divider />
|
|
233
|
+
<InfoMetric label={t('common.id')} value={<Copyable text={props.id} style={{ marginLeft: 4 }} />} divider />
|
|
190
234
|
</Stack>
|
|
191
235
|
</Box>
|
|
236
|
+
<Divider />
|
|
192
237
|
</Box>
|
|
238
|
+
<Stack
|
|
239
|
+
sx={{
|
|
240
|
+
flexDirection: {
|
|
241
|
+
xs: 'column',
|
|
242
|
+
lg: 'row',
|
|
243
|
+
},
|
|
244
|
+
gap: 4,
|
|
245
|
+
'.payment-link-column-1': {
|
|
246
|
+
minWidth: {
|
|
247
|
+
xs: '100%',
|
|
248
|
+
lg: '600px',
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
'.payment-link-column-2': {
|
|
252
|
+
width: {
|
|
253
|
+
xs: '100%',
|
|
254
|
+
md: '100%',
|
|
255
|
+
lg: '580px',
|
|
256
|
+
},
|
|
257
|
+
maxWidth: {
|
|
258
|
+
xs: '100%',
|
|
259
|
+
md: '33%',
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
}}>
|
|
263
|
+
<Box flex={1} className="payment-link-column-1" sx={{ gap: 4, display: 'flex', flexDirection: 'column' }}>
|
|
264
|
+
<Box className="section">
|
|
265
|
+
<SectionHeader title={t('admin.details')} />
|
|
266
|
+
<Stack>
|
|
267
|
+
<InfoRow
|
|
268
|
+
label={t('common.did')}
|
|
269
|
+
value={<DidAddress did={data.customer.did} showQrcode />}
|
|
270
|
+
direction={InfoDirection}
|
|
271
|
+
alignItems={InfoAlignItems}
|
|
272
|
+
/>
|
|
273
|
+
<Box
|
|
274
|
+
sx={{
|
|
275
|
+
display: 'grid',
|
|
276
|
+
gridTemplateColumns: {
|
|
277
|
+
xs: 'repeat(1, 1fr)',
|
|
278
|
+
sm: 'repeat(1, 1fr)',
|
|
279
|
+
md: 'repeat(2, 1fr)',
|
|
280
|
+
lg: 'repeat(3, 1fr)',
|
|
281
|
+
},
|
|
282
|
+
}}>
|
|
283
|
+
<InfoRow
|
|
284
|
+
label={t('admin.customer.name')}
|
|
285
|
+
value={data.customer.name}
|
|
286
|
+
direction={InfoDirection}
|
|
287
|
+
alignItems={InfoAlignItems}
|
|
288
|
+
/>
|
|
289
|
+
<InfoRow
|
|
290
|
+
label={t('admin.customer.phone')}
|
|
291
|
+
value={data.customer.phone}
|
|
292
|
+
direction={InfoDirection}
|
|
293
|
+
alignItems={InfoAlignItems}
|
|
294
|
+
/>
|
|
295
|
+
<InfoRow
|
|
296
|
+
label={t('admin.customer.email')}
|
|
297
|
+
value={data.customer.email}
|
|
298
|
+
direction={InfoDirection}
|
|
299
|
+
alignItems={InfoAlignItems}
|
|
300
|
+
/>
|
|
301
|
+
<InfoRow
|
|
302
|
+
label={t('admin.customer.invoicePrefix')}
|
|
303
|
+
value={data.customer.invoice_prefix}
|
|
304
|
+
direction={InfoDirection}
|
|
305
|
+
alignItems={InfoAlignItems}
|
|
306
|
+
/>
|
|
307
|
+
<InfoRow
|
|
308
|
+
label={t('common.createdAt')}
|
|
309
|
+
value={formatTime(data.customer.created_at)}
|
|
310
|
+
direction={InfoDirection}
|
|
311
|
+
alignItems={InfoAlignItems}
|
|
312
|
+
/>
|
|
313
|
+
<InfoRow
|
|
314
|
+
label={t('common.updatedAt')}
|
|
315
|
+
value={formatTime(data.customer.updated_at)}
|
|
316
|
+
direction={InfoDirection}
|
|
317
|
+
alignItems={InfoAlignItems}
|
|
318
|
+
/>
|
|
193
319
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
</
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
320
|
+
{state.editing.customer && (
|
|
321
|
+
<EditCustomer
|
|
322
|
+
data={data.customer}
|
|
323
|
+
loading={state.loading.customer}
|
|
324
|
+
onSave={onUpdateInfo}
|
|
325
|
+
onCancel={() => setState((prev) => ({ editing: { ...prev.editing, customer: false } }))}
|
|
326
|
+
/>
|
|
327
|
+
)}
|
|
328
|
+
</Box>
|
|
329
|
+
</Stack>
|
|
330
|
+
</Box>
|
|
331
|
+
<Divider />
|
|
332
|
+
<Box className="section">
|
|
333
|
+
<SectionHeader title={t('admin.subscriptions')} mb={0} />
|
|
334
|
+
<Box className="section-body">
|
|
335
|
+
<SubscriptionList
|
|
336
|
+
features={{ customer: false, toolbar: false }}
|
|
337
|
+
customer_id={data.customer.id}
|
|
338
|
+
status={['active', 'trialing', 'paused', 'past_due', 'canceled'].join(',')}
|
|
339
|
+
/>
|
|
340
|
+
</Box>
|
|
341
|
+
</Box>
|
|
342
|
+
<Divider />
|
|
343
|
+
<Box className="section">
|
|
344
|
+
<SectionHeader title={t('admin.invoices')} mb={0} />
|
|
345
|
+
<Box className="section-body">
|
|
346
|
+
<InvoiceList
|
|
347
|
+
features={{ customer: false, toolbar: false }}
|
|
348
|
+
customer_id={data.customer.id}
|
|
349
|
+
status={['open', 'paid', 'uncollectible', 'draft', 'void'].join(',')}
|
|
350
|
+
/>
|
|
351
|
+
</Box>
|
|
352
|
+
</Box>
|
|
353
|
+
<Divider />
|
|
354
|
+
<Box className="section">
|
|
355
|
+
<SectionHeader title={t('admin.payments')} mb={0} />
|
|
356
|
+
<Box className="section-body">
|
|
357
|
+
<PaymentList features={{ customer: false, toolbar: false }} customer_id={data.customer.id} />
|
|
358
|
+
</Box>
|
|
359
|
+
</Box>
|
|
360
|
+
<Divider />
|
|
361
|
+
<Box className="section">
|
|
362
|
+
<SectionHeader title={t('admin.payouts')} mb={0} />
|
|
363
|
+
<Box className="section-body">
|
|
364
|
+
<PayoutList
|
|
365
|
+
features={{ customer: false, toolbar: false }}
|
|
366
|
+
customer_id={data.customer.id}
|
|
367
|
+
status={['paid', 'pending', 'failed', 'canceled'].join(',')}
|
|
368
|
+
/>
|
|
369
|
+
</Box>
|
|
370
|
+
</Box>
|
|
371
|
+
<Divider />
|
|
372
|
+
<Box className="section">
|
|
373
|
+
<SectionHeader title={t('admin.refunds')} mb={0} />
|
|
374
|
+
<Box className="section-body">
|
|
375
|
+
<RefundList
|
|
376
|
+
features={{ customer: false, toolbar: false }}
|
|
377
|
+
customer_id={data.customer.id}
|
|
378
|
+
status={['succeeded', 'canceled', 'failed', 'requires_action', 'pending'].join(',')}
|
|
379
|
+
/>
|
|
380
|
+
</Box>
|
|
381
|
+
</Box>
|
|
382
|
+
<Divider />
|
|
383
|
+
<Box className="section">
|
|
384
|
+
<SectionHeader title={t('admin.events')} />
|
|
385
|
+
<Box className="section-body">
|
|
386
|
+
<EventList features={{ toolbar: false }} object_id={data.customer.id} />
|
|
387
|
+
</Box>
|
|
388
|
+
</Box>
|
|
389
|
+
</Box>
|
|
390
|
+
<Box className="payment-link-column-2" sx={{ gap: 3, display: 'flex', flexDirection: 'column' }}>
|
|
391
|
+
<Box className="section">
|
|
392
|
+
<SectionHeader title={t('admin.metrics')} />
|
|
393
|
+
<Box className="section-body">
|
|
394
|
+
<InfoRow
|
|
395
|
+
label={t('admin.customer.spent')}
|
|
396
|
+
value={<BalanceList data={data.summary?.paid} showLogo />}
|
|
397
|
+
direction={InfoDirection}
|
|
398
|
+
alignItems={InfoAlignItems}
|
|
399
|
+
/>
|
|
400
|
+
<InfoRow
|
|
401
|
+
label={t('admin.customer.stake')}
|
|
402
|
+
value={<BalanceList data={data.summary?.stake} showLogo />}
|
|
403
|
+
direction={InfoDirection}
|
|
404
|
+
alignItems={InfoAlignItems}
|
|
405
|
+
/>
|
|
406
|
+
<InfoRow
|
|
407
|
+
label={t('admin.customer.token')}
|
|
408
|
+
value={<BalanceList data={data.summary?.token} showLogo />}
|
|
409
|
+
direction={InfoDirection}
|
|
410
|
+
alignItems={InfoAlignItems}
|
|
411
|
+
/>
|
|
412
|
+
<InfoRow
|
|
413
|
+
label={t('admin.customer.due')}
|
|
414
|
+
value={<BalanceList data={data.summary?.due} showLogo />}
|
|
415
|
+
direction={InfoDirection}
|
|
416
|
+
alignItems={InfoAlignItems}
|
|
417
|
+
/>
|
|
418
|
+
<InfoRow
|
|
419
|
+
label={t('admin.customer.refund')}
|
|
420
|
+
value={<BalanceList data={data.summary?.refunded} showLogo />}
|
|
421
|
+
direction={InfoDirection}
|
|
422
|
+
alignItems={InfoAlignItems}
|
|
423
|
+
/>
|
|
424
|
+
{tokenBalances.map((x) => (
|
|
219
425
|
<InfoRow
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
426
|
+
key={x.currency}
|
|
427
|
+
label={t('admin.customer.balance', { currency: x.currency })}
|
|
428
|
+
value={x.balance}
|
|
429
|
+
direction={InfoDirection}
|
|
430
|
+
alignItems={InfoAlignItems}
|
|
431
|
+
/>
|
|
432
|
+
))}
|
|
433
|
+
</Box>
|
|
434
|
+
</Box>
|
|
435
|
+
<Divider />
|
|
436
|
+
<Box className="section">
|
|
437
|
+
<SectionHeader title={t('admin.customer.address.label')} />
|
|
438
|
+
<Box className="section-body">
|
|
439
|
+
<InfoRow
|
|
440
|
+
label={t('admin.customer.address.country')}
|
|
441
|
+
value={
|
|
442
|
+
data.customer.address?.country ? (
|
|
443
|
+
<Box display="flex" alignItems="center" flexWrap="nowrap" gap={0.5} sx={{ cursor: 'pointer' }}>
|
|
223
444
|
<FlagEmoji iso2={data.customer.address?.country} style={{ display: 'flex', width: 24 }} />
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
445
|
+
<Typography>{countryDetail?.name}</Typography>
|
|
446
|
+
</Box>
|
|
447
|
+
) : (
|
|
448
|
+
''
|
|
449
|
+
)
|
|
450
|
+
}
|
|
451
|
+
direction={InfoDirection}
|
|
452
|
+
alignItems={InfoAlignItems}
|
|
453
|
+
/>
|
|
454
|
+
<InfoRow
|
|
455
|
+
label={t('admin.customer.address.state')}
|
|
456
|
+
value={data.customer.address?.state}
|
|
457
|
+
direction={InfoDirection}
|
|
458
|
+
alignItems={InfoAlignItems}
|
|
459
|
+
/>
|
|
460
|
+
<InfoRow
|
|
461
|
+
label={t('admin.customer.address.city')}
|
|
462
|
+
value={data.customer.address?.city}
|
|
463
|
+
direction={InfoDirection}
|
|
464
|
+
alignItems={InfoAlignItems}
|
|
465
|
+
/>
|
|
466
|
+
<InfoRow
|
|
467
|
+
label={t('admin.customer.address.line1')}
|
|
468
|
+
value={data.customer.address?.line1}
|
|
469
|
+
direction={InfoDirection}
|
|
470
|
+
alignItems={InfoAlignItems}
|
|
471
|
+
/>
|
|
472
|
+
<InfoRow
|
|
473
|
+
label={t('admin.customer.address.line2')}
|
|
474
|
+
value={data.customer.address?.line2}
|
|
475
|
+
direction={InfoDirection}
|
|
476
|
+
alignItems={InfoAlignItems}
|
|
477
|
+
/>
|
|
478
|
+
<InfoRow
|
|
479
|
+
label={t('admin.customer.address.postal_code')}
|
|
480
|
+
value={data.customer.address?.postal_code}
|
|
481
|
+
direction={InfoDirection}
|
|
482
|
+
alignItems={InfoAlignItems}
|
|
483
|
+
/>
|
|
484
|
+
</Box>
|
|
485
|
+
</Box>
|
|
486
|
+
<Divider />
|
|
487
|
+
<Box className="section">
|
|
488
|
+
<SectionHeader title={t('common.metadata.label')}>
|
|
489
|
+
<Button
|
|
490
|
+
variant="text"
|
|
491
|
+
color="inherit"
|
|
492
|
+
size="small"
|
|
493
|
+
sx={{ color: 'text.link' }}
|
|
494
|
+
disabled={state.editing.metadata}
|
|
495
|
+
onClick={handleEditMetadata}>
|
|
496
|
+
{t('common.edit')}
|
|
497
|
+
</Button>
|
|
498
|
+
</SectionHeader>
|
|
499
|
+
<Box className="section-body">
|
|
500
|
+
<MetadataList data={data.customer.metadata} handleEditMetadata={handleEditMetadata} />
|
|
501
|
+
{state.editing.metadata && (
|
|
502
|
+
<MetadataEditor
|
|
503
|
+
data={data.customer}
|
|
504
|
+
loading={state.loading.metadata}
|
|
505
|
+
onSave={onUpdateMetadata}
|
|
506
|
+
onCancel={() => setState((prev) => ({ editing: { ...prev.editing, metadata: false } }))}
|
|
228
507
|
/>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
<InfoRow label={t('admin.customer.address.line2')} value={data.customer.address?.line2} />
|
|
233
|
-
<InfoRow label={t('admin.customer.address.postal_code')} value={data.customer.address?.postal_code} />
|
|
234
|
-
</Stack>
|
|
235
|
-
}
|
|
236
|
-
/>
|
|
237
|
-
{state.editing.customer && (
|
|
238
|
-
<EditCustomer
|
|
239
|
-
data={data.customer}
|
|
240
|
-
loading={state.loading.customer}
|
|
241
|
-
onSave={onUpdateInfo}
|
|
242
|
-
onCancel={() => setState((prev) => ({ editing: { ...prev.editing, customer: false } }))}
|
|
243
|
-
/>
|
|
244
|
-
)}
|
|
245
|
-
</Stack>
|
|
246
|
-
</Box>
|
|
247
|
-
<Box className="section">
|
|
248
|
-
<SectionHeader title={t('admin.subscriptions')} mb={0} />
|
|
249
|
-
<Box className="section-body">
|
|
250
|
-
<SubscriptionList
|
|
251
|
-
features={{ customer: false, toolbar: false }}
|
|
252
|
-
customer_id={data.customer.id}
|
|
253
|
-
status={['active', 'trialing', 'paused', 'past_due', 'canceled'].join(',')}
|
|
254
|
-
/>
|
|
255
|
-
</Box>
|
|
256
|
-
</Box>
|
|
257
|
-
<Box className="section">
|
|
258
|
-
<SectionHeader title={t('admin.invoices')} mb={0} />
|
|
259
|
-
<Box className="section-body">
|
|
260
|
-
<InvoiceList
|
|
261
|
-
features={{ customer: false, toolbar: false }}
|
|
262
|
-
customer_id={data.customer.id}
|
|
263
|
-
status={['open', 'paid', 'uncollectible', 'draft', 'void'].join(',')}
|
|
264
|
-
/>
|
|
265
|
-
</Box>
|
|
266
|
-
</Box>
|
|
267
|
-
<Box className="section">
|
|
268
|
-
<SectionHeader title={t('admin.payments')} mb={0} />
|
|
269
|
-
<Box className="section-body">
|
|
270
|
-
<PaymentList features={{ customer: false, toolbar: false }} customer_id={data.customer.id} />
|
|
271
|
-
</Box>
|
|
272
|
-
</Box>
|
|
273
|
-
<Box className="section">
|
|
274
|
-
<SectionHeader title={t('admin.payouts')} mb={0} />
|
|
275
|
-
<Box className="section-body">
|
|
276
|
-
<PayoutList
|
|
277
|
-
features={{ customer: false, toolbar: false }}
|
|
278
|
-
customer_id={data.customer.id}
|
|
279
|
-
status={['paid', 'pending', 'failed', 'canceled'].join(',')}
|
|
280
|
-
/>
|
|
281
|
-
</Box>
|
|
282
|
-
</Box>
|
|
283
|
-
<Box className="section">
|
|
284
|
-
<SectionHeader title={t('admin.refunds')} mb={0} />
|
|
285
|
-
<Box className="section-body">
|
|
286
|
-
<RefundList
|
|
287
|
-
features={{ customer: false, toolbar: false }}
|
|
288
|
-
customer_id={data.customer.id}
|
|
289
|
-
status={['succeeded', 'canceled', 'failed', 'requires_action', 'pending'].join(',')}
|
|
290
|
-
/>
|
|
508
|
+
)}
|
|
509
|
+
</Box>
|
|
510
|
+
</Box>
|
|
291
511
|
</Box>
|
|
292
|
-
</
|
|
293
|
-
<Box className="section">
|
|
294
|
-
<SectionHeader title={t('common.metadata.label')}>
|
|
295
|
-
<Button
|
|
296
|
-
variant="outlined"
|
|
297
|
-
color="inherit"
|
|
298
|
-
size="small"
|
|
299
|
-
disabled={state.editing.metadata}
|
|
300
|
-
onClick={() => setState((prev) => ({ editing: { ...prev.editing, metadata: true } }))}>
|
|
301
|
-
<Edit fontSize="small" sx={{ mr: 0.5 }} />
|
|
302
|
-
{t('common.metadata.edit')}
|
|
303
|
-
</Button>
|
|
304
|
-
</SectionHeader>
|
|
305
|
-
<Box className="section-body">
|
|
306
|
-
{!state.editing.metadata &&
|
|
307
|
-
(isEmpty(data.customer.metadata) ? (
|
|
308
|
-
<Typography color="text.secondary">{t('common.metadata.empty')}</Typography>
|
|
309
|
-
) : (
|
|
310
|
-
Object.keys(data.customer.metadata || {}).map((key) => (
|
|
311
|
-
<InfoRow key={key} label={key} value={data.customer.metadata[key]} />
|
|
312
|
-
))
|
|
313
|
-
))}
|
|
314
|
-
{state.editing.metadata && (
|
|
315
|
-
<MetadataEditor
|
|
316
|
-
data={data.customer}
|
|
317
|
-
loading={state.loading.metadata}
|
|
318
|
-
onSave={onUpdateMetadata}
|
|
319
|
-
onCancel={() => setState((prev) => ({ editing: { ...prev.editing, metadata: false } }))}
|
|
320
|
-
/>
|
|
321
|
-
)}
|
|
322
|
-
</Box>
|
|
323
|
-
</Box>
|
|
324
|
-
<Box className="section">
|
|
325
|
-
<SectionHeader title={t('admin.events')} />
|
|
326
|
-
<Box className="section-body">
|
|
327
|
-
<EventList features={{ toolbar: false }} object_id={data.customer.id} />
|
|
328
|
-
</Box>
|
|
329
|
-
</Box>
|
|
512
|
+
</Stack>
|
|
330
513
|
</Root>
|
|
331
514
|
);
|
|
332
515
|
}
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
import { getDurableData } from '@arcblock/ux/lib/Datatable';
|
|
3
3
|
import DidAddress from '@arcblock/ux/lib/DID';
|
|
4
4
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
5
|
-
import { api, formatTime } from '@blocklet/payment-react';
|
|
5
|
+
import { api, formatTime, Table } from '@blocklet/payment-react';
|
|
6
6
|
import type { TCustomer } from '@blocklet/payment-types';
|
|
7
7
|
import { Avatar, CircularProgress, Stack, Typography } from '@mui/material';
|
|
8
8
|
import { useEffect, useState } from 'react';
|
|
9
9
|
import { Link } from 'react-router-dom';
|
|
10
10
|
|
|
11
|
-
import Table from '../../../../components/table';
|
|
12
|
-
|
|
13
11
|
const fetchData = (params: Record<string, any> = {}): Promise<{ list: TCustomer[]; count: number }> => {
|
|
14
12
|
const search = new URLSearchParams();
|
|
15
13
|
Object.keys(params).forEach((key) => {
|
|
@@ -60,8 +58,12 @@ export default function CustomersList() {
|
|
|
60
58
|
const item = data.list[index] as TCustomer;
|
|
61
59
|
return (
|
|
62
60
|
<Link to={`/admin/customers/${item.id}`}>
|
|
63
|
-
<Stack direction="row" alignItems="center" spacing={
|
|
64
|
-
<Avatar
|
|
61
|
+
<Stack direction="row" alignItems="center" spacing={1}>
|
|
62
|
+
<Avatar
|
|
63
|
+
src={`/.well-known/service/user/avatar/${item.did}?imageFilter=resize&w=48&h=48`}
|
|
64
|
+
variant="square"
|
|
65
|
+
sx={{ borderRadius: 'var(--radius-m, 8px)' }}
|
|
66
|
+
/>
|
|
65
67
|
<Typography>{item.name}</Typography>
|
|
66
68
|
</Stack>
|
|
67
69
|
</Link>
|
|
@@ -167,6 +169,7 @@ export default function CustomersList() {
|
|
|
167
169
|
}}
|
|
168
170
|
loading={!data.list}
|
|
169
171
|
onChange={onTableChange}
|
|
172
|
+
emptyNodeText={t('empty.customers')}
|
|
170
173
|
/>
|
|
171
174
|
);
|
|
172
175
|
}
|