payment-kit 1.13.150 → 1.13.152
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/api/src/libs/notification/template/subscription-refund-succeeded.ts +1 -1
- package/api/src/queues/invoice.ts +14 -0
- package/api/src/queues/refund.ts +1 -0
- package/api/src/routes/checkout-sessions.ts +9 -1
- package/api/src/routes/connect/shared.ts +1 -0
- package/api/src/routes/customers.ts +22 -1
- package/api/src/routes/refunds.ts +14 -0
- package/api/src/routes/subscriptions.ts +2 -1
- package/api/src/store/migrations/20240225-refund-ext.ts +22 -0
- package/api/src/store/models/customer.ts +21 -0
- package/api/src/store/models/index.ts +1 -1
- package/api/src/store/models/invoice.ts +37 -1
- package/api/src/store/models/payment-intent.ts +24 -2
- package/api/src/store/models/refund.ts +53 -16
- package/api/src/store/models/types.ts +2 -0
- package/blocklet.yml +1 -1
- package/package.json +4 -4
- package/src/app.tsx +10 -0
- package/src/components/balance-list.tsx +43 -0
- package/src/components/info-metric.tsx +2 -2
- package/src/components/invoice/table.tsx +1 -1
- package/src/components/payment-intent/list.tsx +1 -1
- package/src/components/payment-link/after-pay.tsx +0 -19
- package/src/components/refund/list.tsx +6 -2
- package/src/components/section/header.tsx +3 -1
- package/src/components/subscription/items/usage-records.tsx +1 -1
- package/src/global.css +0 -1
- package/src/locales/en.tsx +2 -71
- package/src/locales/zh.tsx +2 -71
- package/src/pages/admin/billing/invoices/detail.tsx +7 -0
- package/src/pages/admin/billing/subscriptions/detail.tsx +7 -0
- package/src/pages/admin/customers/customers/detail.tsx +49 -32
- package/src/pages/admin/index.tsx +4 -2
- package/src/pages/admin/payments/intents/detail.tsx +17 -13
- package/src/pages/admin/payments/links/create.tsx +1 -1
- package/src/pages/customer/index.tsx +55 -9
- package/src/pages/customer/invoice/past-due.tsx +77 -0
- package/src/pages/customer/invoice.tsx +58 -56
- package/src/pages/customer/refund/list.tsx +125 -0
|
@@ -27,6 +27,7 @@ type SearchProps = {
|
|
|
27
27
|
page: number;
|
|
28
28
|
customer_id?: string;
|
|
29
29
|
invoice_id?: string;
|
|
30
|
+
subscription_id?: string;
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
type ListProps = {
|
|
@@ -37,6 +38,7 @@ type ListProps = {
|
|
|
37
38
|
};
|
|
38
39
|
customer_id?: string;
|
|
39
40
|
invoice_id?: string;
|
|
41
|
+
subscription_id?: string;
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
const getListKey = (props: ListProps) => {
|
|
@@ -50,16 +52,17 @@ const getListKey = (props: ListProps) => {
|
|
|
50
52
|
return 'refunds';
|
|
51
53
|
};
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
RefundList.defaultProps = {
|
|
54
56
|
features: {
|
|
55
57
|
customer: true,
|
|
56
58
|
filter: true,
|
|
57
59
|
},
|
|
58
60
|
customer_id: '',
|
|
59
61
|
invoice_id: '',
|
|
62
|
+
subscription_id: '',
|
|
60
63
|
};
|
|
61
64
|
|
|
62
|
-
export default function
|
|
65
|
+
export default function RefundList({ customer_id, invoice_id, subscription_id, features }: ListProps) {
|
|
63
66
|
const { t } = useLocaleContext();
|
|
64
67
|
const navigate = useNavigate();
|
|
65
68
|
|
|
@@ -70,6 +73,7 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
70
73
|
status: '',
|
|
71
74
|
customer_id,
|
|
72
75
|
invoice_id,
|
|
76
|
+
subscription_id,
|
|
73
77
|
pageSize: persisted.rowsPerPage || 20,
|
|
74
78
|
page: persisted.page ? persisted.page + 1 : 1,
|
|
75
79
|
});
|
|
@@ -5,6 +5,7 @@ type Props = {
|
|
|
5
5
|
title: string;
|
|
6
6
|
children?: ReactNode;
|
|
7
7
|
mb?: number;
|
|
8
|
+
mt?: number;
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
export default function SectionHeader(props: Props) {
|
|
@@ -16,7 +17,7 @@ export default function SectionHeader(props: Props) {
|
|
|
16
17
|
alignItems="center"
|
|
17
18
|
flexWrap="wrap"
|
|
18
19
|
gap={1}
|
|
19
|
-
sx={{ mb: props.mb, pb: 1, width: 1, borderBottom: '1px solid #eee' }}>
|
|
20
|
+
sx={{ mb: props.mb, mt: props.mt, pb: 1, width: 1, borderBottom: '1px solid #eee' }}>
|
|
20
21
|
<Typography variant="h6" sx={{ fontWeight: 600 }}>
|
|
21
22
|
{props.title}
|
|
22
23
|
</Typography>
|
|
@@ -28,4 +29,5 @@ export default function SectionHeader(props: Props) {
|
|
|
28
29
|
SectionHeader.defaultProps = {
|
|
29
30
|
children: null,
|
|
30
31
|
mb: 1,
|
|
32
|
+
mt: 1,
|
|
31
33
|
};
|
|
@@ -48,7 +48,7 @@ export function UsageRecordDialog(props: { subscriptionId: string; id: string; o
|
|
|
48
48
|
options: {
|
|
49
49
|
customBodyRenderLite: (_: string, index: number) => {
|
|
50
50
|
const item = data.list[index] as TUsageRecord;
|
|
51
|
-
return formatToDatetime(item.timestamp);
|
|
51
|
+
return formatToDatetime(item.timestamp * 1000);
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
54
|
},
|
package/src/global.css
CHANGED
package/src/locales/en.tsx
CHANGED
|
@@ -2,78 +2,7 @@ import flat from 'flat';
|
|
|
2
2
|
|
|
3
3
|
export default flat({
|
|
4
4
|
common: {
|
|
5
|
-
id: 'ID',
|
|
6
|
-
url: 'URL',
|
|
7
|
-
createdAt: 'Created At',
|
|
8
|
-
updatedAt: 'Updated At',
|
|
9
|
-
resumesAt: 'Resume At',
|
|
10
|
-
actions: 'Actions',
|
|
11
|
-
options: 'Options',
|
|
12
|
-
advanced: 'Advanced options',
|
|
13
|
-
login: 'Login to access this page',
|
|
14
|
-
settings: 'Settings',
|
|
15
|
-
preview: 'Preview',
|
|
16
|
-
required: 'Required',
|
|
17
|
-
setup: 'Setup',
|
|
18
|
-
name: 'Name',
|
|
19
|
-
amount: 'Amount',
|
|
20
|
-
total: 'Total',
|
|
21
|
-
subtotal: 'Subtotal',
|
|
22
|
-
status: 'Status',
|
|
23
|
-
livemode: 'Test mode',
|
|
24
|
-
afterTime: 'After {time}',
|
|
25
|
-
timeAgo: '{time} ago',
|
|
26
|
-
save: 'Save',
|
|
27
|
-
saved: 'Changes saved',
|
|
28
|
-
remove: 'Remove',
|
|
29
|
-
removed: 'Resource removed',
|
|
30
|
-
confirm: 'Confirm',
|
|
31
|
-
cancel: 'Cancel',
|
|
32
|
-
every: 'every',
|
|
33
|
-
per: 'per {interval}',
|
|
34
|
-
slash: '/ {interval}',
|
|
35
|
-
unit: 'units',
|
|
36
|
-
edit: 'Edit',
|
|
37
|
-
quantity: 'Quantity',
|
|
38
|
-
yes: 'Yes',
|
|
39
|
-
no: 'No',
|
|
40
|
-
email: 'Email',
|
|
41
|
-
did: 'DID',
|
|
42
|
-
txHash: 'Transaction',
|
|
43
|
-
customer: 'Customer',
|
|
44
|
-
custom: 'Custom',
|
|
45
|
-
description: 'Description',
|
|
46
|
-
statementDescriptor: 'Statement descriptor',
|
|
47
|
-
loadMore: 'View more {resource}',
|
|
48
|
-
loadingMore: 'Loading more {resource}...',
|
|
49
|
-
noMore: 'No more {resource}',
|
|
50
|
-
copied: 'Copied',
|
|
51
|
-
previous: 'Back',
|
|
52
|
-
continue: 'Continue',
|
|
53
|
-
qty: 'Qty {count}',
|
|
54
|
-
each: '{unit} each',
|
|
55
|
-
trial: 'Free for {count} days',
|
|
56
|
-
billed: 'billed {rule}',
|
|
57
|
-
metered: 'based on usage',
|
|
58
|
-
hour: 'hour',
|
|
59
|
-
day: 'day',
|
|
60
|
-
week: 'week',
|
|
61
|
-
month: 'month',
|
|
62
|
-
year: 'year',
|
|
63
|
-
hourly: 'hourly',
|
|
64
|
-
daily: 'daily',
|
|
65
|
-
weekly: 'weekly',
|
|
66
|
-
monthly: 'monthly',
|
|
67
|
-
yearly: 'yearly',
|
|
68
|
-
month3: 'every 3 months',
|
|
69
|
-
month6: 'every 6 months',
|
|
70
|
-
recurring: 'every {count} {interval}',
|
|
71
5
|
redirecting: 'Redirecting...',
|
|
72
|
-
hours: 'hours',
|
|
73
|
-
days: 'days',
|
|
74
|
-
weeks: 'weeks',
|
|
75
|
-
months: 'months',
|
|
76
|
-
years: 'years',
|
|
77
6
|
metadata: {
|
|
78
7
|
label: 'Metadata',
|
|
79
8
|
add: 'Add more metadata',
|
|
@@ -306,6 +235,7 @@ export default flat({
|
|
|
306
235
|
view: 'View payment detail',
|
|
307
236
|
empty: 'No payment intent',
|
|
308
237
|
refund: 'Refund payment',
|
|
238
|
+
received: 'Received',
|
|
309
239
|
},
|
|
310
240
|
paymentMethod: {
|
|
311
241
|
_name: 'Payment Method',
|
|
@@ -462,6 +392,7 @@ export default flat({
|
|
|
462
392
|
spent: 'Spent Amount',
|
|
463
393
|
refund: 'Refund Amount',
|
|
464
394
|
dispute: 'Dispute Amount',
|
|
395
|
+
due: 'Due Amount',
|
|
465
396
|
name: 'Name',
|
|
466
397
|
email: 'Email',
|
|
467
398
|
phone: 'Phone',
|
package/src/locales/zh.tsx
CHANGED
|
@@ -2,78 +2,7 @@ import flat from 'flat';
|
|
|
2
2
|
|
|
3
3
|
export default flat({
|
|
4
4
|
common: {
|
|
5
|
-
id: 'ID',
|
|
6
|
-
url: 'URL',
|
|
7
|
-
createdAt: '创建时间',
|
|
8
|
-
updatedAt: '更新时间',
|
|
9
|
-
resumesAt: '恢复时间',
|
|
10
|
-
actions: '操作',
|
|
11
|
-
options: '选项',
|
|
12
|
-
advanced: '高级选项',
|
|
13
|
-
settings: '设置',
|
|
14
|
-
preview: '预览',
|
|
15
|
-
required: '必填',
|
|
16
|
-
setup: '设置',
|
|
17
|
-
name: '姓名',
|
|
18
|
-
login: '登录以访问此页面',
|
|
19
|
-
amount: '金额',
|
|
20
|
-
total: '总计',
|
|
21
|
-
subtotal: '小计',
|
|
22
|
-
status: '状态',
|
|
23
|
-
livemode: '测试模式',
|
|
24
|
-
afterTime: '在{time}后',
|
|
25
|
-
timeAgo: '{time}前',
|
|
26
|
-
save: '保存',
|
|
27
|
-
saved: '更改已保存',
|
|
28
|
-
remove: '删除',
|
|
29
|
-
removed: '资源已删除',
|
|
30
|
-
confirm: '确认',
|
|
31
|
-
cancel: '取消',
|
|
32
|
-
every: '每',
|
|
33
|
-
per: '每{interval}',
|
|
34
|
-
slash: '每{interval}',
|
|
35
|
-
unit: '件',
|
|
36
|
-
edit: '编辑',
|
|
37
|
-
quantity: '数量',
|
|
38
|
-
yes: '是',
|
|
39
|
-
no: '否',
|
|
40
|
-
email: '邮箱',
|
|
41
|
-
did: 'DID',
|
|
42
|
-
txHash: '交易哈希',
|
|
43
|
-
customer: '客户',
|
|
44
|
-
custom: '自定义',
|
|
45
|
-
description: '描述',
|
|
46
|
-
statementDescriptor: '声明描述',
|
|
47
|
-
loadMore: '查看更多{resource}',
|
|
48
|
-
loadingMore: '正在加载更多{resource}...',
|
|
49
|
-
noMore: '没有更多{resource}',
|
|
50
|
-
copied: '已复制',
|
|
51
|
-
previous: '返回',
|
|
52
|
-
continue: '继续',
|
|
53
|
-
qty: '{count} 件',
|
|
54
|
-
each: '每件 {unit}',
|
|
55
|
-
trial: '免费试用 {count} 天',
|
|
56
|
-
billed: '{rule}收费',
|
|
57
|
-
metered: '按用量',
|
|
58
|
-
hour: '小时',
|
|
59
|
-
day: '天',
|
|
60
|
-
week: '周',
|
|
61
|
-
month: '月',
|
|
62
|
-
year: '年',
|
|
63
|
-
hourly: '按小时',
|
|
64
|
-
daily: '按天',
|
|
65
|
-
weekly: '按周',
|
|
66
|
-
monthly: '按月',
|
|
67
|
-
yearly: '按年',
|
|
68
|
-
month3: '按季度',
|
|
69
|
-
month6: '按半年',
|
|
70
|
-
recurring: '每{count}{interval}',
|
|
71
5
|
redirecting: '跳转中...',
|
|
72
|
-
hours: '小时',
|
|
73
|
-
days: '天',
|
|
74
|
-
weeks: '周',
|
|
75
|
-
months: '月',
|
|
76
|
-
years: '年',
|
|
77
6
|
metadata: {
|
|
78
7
|
label: '元数据',
|
|
79
8
|
add: '添加更多元数据',
|
|
@@ -298,6 +227,7 @@ export default flat({
|
|
|
298
227
|
view: '查看支付详情',
|
|
299
228
|
empty: '没有支付意向',
|
|
300
229
|
refund: '退款支付',
|
|
230
|
+
received: '实收金额',
|
|
301
231
|
},
|
|
302
232
|
paymentMethod: {
|
|
303
233
|
_name: '支付方式',
|
|
@@ -453,6 +383,7 @@ export default flat({
|
|
|
453
383
|
spent: '花费金额',
|
|
454
384
|
refund: '退款金额',
|
|
455
385
|
dispute: '争议金额',
|
|
386
|
+
due: '欠款金额',
|
|
456
387
|
name: '名称',
|
|
457
388
|
email: '电子邮件',
|
|
458
389
|
phone: '电话',
|
|
@@ -20,6 +20,7 @@ import InvoiceActions from '../../../../components/invoice/action';
|
|
|
20
20
|
import InvoiceTable from '../../../../components/invoice/table';
|
|
21
21
|
import MetadataEditor from '../../../../components/metadata/editor';
|
|
22
22
|
import PaymentList from '../../../../components/payment-intent/list';
|
|
23
|
+
import RefundList from '../../../../components/refund/list';
|
|
23
24
|
import SectionHeader from '../../../../components/section/header';
|
|
24
25
|
|
|
25
26
|
const fetchData = (id: string): Promise<TInvoiceExpanded> => {
|
|
@@ -145,6 +146,12 @@ export default function InvoiceDetail(props: { id: string }) {
|
|
|
145
146
|
<PaymentList features={{ customer: false, toolbar: false }} invoice_id={data.id} />
|
|
146
147
|
</Box>
|
|
147
148
|
</Box>
|
|
149
|
+
<Box className="section">
|
|
150
|
+
<SectionHeader title={t('admin.refunds')} mb={0} />
|
|
151
|
+
<Box className="section-body">
|
|
152
|
+
<RefundList features={{ customer: false, toolbar: false }} invoice_id={data.id} />
|
|
153
|
+
</Box>
|
|
154
|
+
</Box>
|
|
148
155
|
<Box className="section">
|
|
149
156
|
<SectionHeader title={t('admin.connections')} />
|
|
150
157
|
<Stack>
|
|
@@ -16,6 +16,7 @@ import EventList from '../../../../components/event/list';
|
|
|
16
16
|
import InfoRow from '../../../../components/info-row';
|
|
17
17
|
import InvoiceList from '../../../../components/invoice/list';
|
|
18
18
|
import MetadataEditor from '../../../../components/metadata/editor';
|
|
19
|
+
import RefundList from '../../../../components/refund/list';
|
|
19
20
|
import SectionHeader from '../../../../components/section/header';
|
|
20
21
|
import SubscriptionActions from '../../../../components/subscription/actions';
|
|
21
22
|
import SubscriptionItemList from '../../../../components/subscription/items';
|
|
@@ -205,6 +206,12 @@ export default function SubscriptionDetail(props: { id: string }) {
|
|
|
205
206
|
<InvoiceList features={{ customer: true, toolbar: false }} subscription_id={data.id} />
|
|
206
207
|
</Box>
|
|
207
208
|
</Box>
|
|
209
|
+
<Box className="section">
|
|
210
|
+
<SectionHeader title={t('admin.refunds')} mb={0} />
|
|
211
|
+
<Box className="section-body">
|
|
212
|
+
<RefundList features={{ customer: true, toolbar: false }} subscription_id={data.id} />
|
|
213
|
+
</Box>
|
|
214
|
+
</Box>
|
|
208
215
|
<Box className="section">
|
|
209
216
|
<SectionHeader title={t('admin.events')} />
|
|
210
217
|
<Box className="section-body">
|
|
@@ -3,7 +3,7 @@ import DidAddress from '@arcblock/ux/lib/DID';
|
|
|
3
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
4
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
5
5
|
import { api, formatError, formatTime, usePaymentContext } from '@blocklet/payment-react';
|
|
6
|
-
import type { TCustomerExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
6
|
+
import type { GroupedBN, TCustomerExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
7
7
|
import { ArrowBackOutlined, Edit } from '@mui/icons-material';
|
|
8
8
|
import { Alert, Box, Button, CircularProgress, Stack, Typography } from '@mui/material';
|
|
9
9
|
import { styled } from '@mui/system';
|
|
@@ -13,6 +13,7 @@ import { isEmpty } from 'lodash';
|
|
|
13
13
|
import { FlagEmoji } from 'react-international-phone';
|
|
14
14
|
import { Link, useNavigate } from 'react-router-dom';
|
|
15
15
|
|
|
16
|
+
import BalanceList from '../../../../components/balance-list';
|
|
16
17
|
import Copyable from '../../../../components/copyable';
|
|
17
18
|
import CustomerActions from '../../../../components/customer/actions';
|
|
18
19
|
import EditCustomer from '../../../../components/customer/edit';
|
|
@@ -25,8 +26,18 @@ import PaymentList from '../../../../components/payment-intent/list';
|
|
|
25
26
|
import SectionHeader from '../../../../components/section/header';
|
|
26
27
|
import SubscriptionList from '../../../../components/subscription/list';
|
|
27
28
|
|
|
28
|
-
const fetchData = (
|
|
29
|
-
|
|
29
|
+
const fetchData = async (
|
|
30
|
+
id: string
|
|
31
|
+
): Promise<{ customer: TCustomerExpanded; summary: { [key: string]: GroupedBN } }> => {
|
|
32
|
+
const results = await Promise.all([
|
|
33
|
+
api.get(`/api/customers/${id}`).then((res) => res.data),
|
|
34
|
+
api.get(`/api/customers/${id}/summary`).then((res) => res.data),
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
customer: results[0],
|
|
39
|
+
summary: results[1],
|
|
40
|
+
};
|
|
30
41
|
};
|
|
31
42
|
|
|
32
43
|
function getTokenBalances(customer: TCustomerExpanded, paymentMethods: TPaymentMethodExpanded[]) {
|
|
@@ -123,7 +134,7 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
123
134
|
}
|
|
124
135
|
};
|
|
125
136
|
|
|
126
|
-
const tokenBalances = getTokenBalances(data, settings.paymentMethods);
|
|
137
|
+
const tokenBalances = getTokenBalances(data.customer, settings.paymentMethods);
|
|
127
138
|
|
|
128
139
|
return (
|
|
129
140
|
<Root direction="column" spacing={4} sx={{ mb: 4 }}>
|
|
@@ -142,9 +153,9 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
142
153
|
<Box mt={2}>
|
|
143
154
|
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
|
144
155
|
<Typography variant="h5" sx={{ fontWeight: 600 }}>
|
|
145
|
-
{data.name}
|
|
156
|
+
{data.customer.name}
|
|
146
157
|
</Typography>
|
|
147
|
-
<CustomerActions data={data} onChange={onChange} variant="normal" />
|
|
158
|
+
<CustomerActions data={data.customer} onChange={onChange} variant="normal" />
|
|
148
159
|
</Stack>
|
|
149
160
|
<Stack
|
|
150
161
|
className="section-body"
|
|
@@ -153,9 +164,15 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
153
164
|
justifyContent="flex-start"
|
|
154
165
|
flexWrap="wrap"
|
|
155
166
|
sx={{ pt: 2, mt: 2, borderTop: '1px solid #eee' }}>
|
|
156
|
-
<InfoMetric label={t('common.createdAt')} value={formatTime(data.created_at)} divider />
|
|
157
|
-
<InfoMetric label={t('common.updatedAt')} value={formatTime(data.updated_at)} divider />
|
|
158
|
-
<InfoMetric label={t('admin.customer.spent')} value={
|
|
167
|
+
<InfoMetric label={t('common.createdAt')} value={formatTime(data.customer.created_at)} divider />
|
|
168
|
+
<InfoMetric label={t('common.updatedAt')} value={formatTime(data.customer.updated_at)} divider />
|
|
169
|
+
<InfoMetric label={t('admin.customer.spent')} value={<BalanceList data={data.summary.paid} />} divider />
|
|
170
|
+
<InfoMetric
|
|
171
|
+
label={t('admin.customer.refund')}
|
|
172
|
+
value={<BalanceList data={data.summary.refunded} />}
|
|
173
|
+
divider
|
|
174
|
+
/>
|
|
175
|
+
<InfoMetric label={t('admin.customer.due')} value={<BalanceList data={data.summary.due} />} divider />
|
|
159
176
|
{tokenBalances.map((x) => (
|
|
160
177
|
<InfoMetric
|
|
161
178
|
key={x.currency}
|
|
@@ -181,13 +198,13 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
181
198
|
</Button>
|
|
182
199
|
</SectionHeader>
|
|
183
200
|
<Stack>
|
|
184
|
-
<InfoRow label={t('common.did')} value={<DidAddress did={data.did} />} />
|
|
185
|
-
<InfoRow label={t('admin.customer.name')} value={data.name} />
|
|
186
|
-
<InfoRow label={t('admin.customer.phone')} value={data.phone} />
|
|
187
|
-
<InfoRow label={t('admin.customer.email')} value={data.email} />
|
|
188
|
-
<InfoRow label={t('admin.customer.invoicePrefix')} value={data.invoice_prefix} />
|
|
189
|
-
<InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
|
|
190
|
-
<InfoRow label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
|
|
201
|
+
<InfoRow label={t('common.did')} value={<DidAddress did={data.customer.did} />} />
|
|
202
|
+
<InfoRow label={t('admin.customer.name')} value={data.customer.name} />
|
|
203
|
+
<InfoRow label={t('admin.customer.phone')} value={data.customer.phone} />
|
|
204
|
+
<InfoRow label={t('admin.customer.email')} value={data.customer.email} />
|
|
205
|
+
<InfoRow label={t('admin.customer.invoicePrefix')} value={data.customer.invoice_prefix} />
|
|
206
|
+
<InfoRow label={t('common.createdAt')} value={formatTime(data.customer.created_at)} />
|
|
207
|
+
<InfoRow label={t('common.updatedAt')} value={formatTime(data.customer.updated_at)} />
|
|
191
208
|
<InfoRow
|
|
192
209
|
alignItems="flex-start"
|
|
193
210
|
label={t('admin.customer.address.label')}
|
|
@@ -196,24 +213,24 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
196
213
|
<InfoRow
|
|
197
214
|
label={t('admin.customer.address.country')}
|
|
198
215
|
value={
|
|
199
|
-
data.address?.country ? (
|
|
200
|
-
<FlagEmoji iso2={data.address?.country} style={{ display: 'flex', width: 24 }} />
|
|
216
|
+
data.customer.address?.country ? (
|
|
217
|
+
<FlagEmoji iso2={data.customer.address?.country} style={{ display: 'flex', width: 24 }} />
|
|
201
218
|
) : (
|
|
202
219
|
''
|
|
203
220
|
)
|
|
204
221
|
}
|
|
205
222
|
/>
|
|
206
|
-
<InfoRow label={t('admin.customer.address.state')} value={data.address?.state} />
|
|
207
|
-
<InfoRow label={t('admin.customer.address.city')} value={data.address?.city} />
|
|
208
|
-
<InfoRow label={t('admin.customer.address.line1')} value={data.address?.line1} />
|
|
209
|
-
<InfoRow label={t('admin.customer.address.line2')} value={data.address?.line2} />
|
|
210
|
-
<InfoRow label={t('admin.customer.address.postal_code')} value={data.address?.postal_code} />
|
|
223
|
+
<InfoRow label={t('admin.customer.address.state')} value={data.customer.address?.state} />
|
|
224
|
+
<InfoRow label={t('admin.customer.address.city')} value={data.customer.address?.city} />
|
|
225
|
+
<InfoRow label={t('admin.customer.address.line1')} value={data.customer.address?.line1} />
|
|
226
|
+
<InfoRow label={t('admin.customer.address.line2')} value={data.customer.address?.line2} />
|
|
227
|
+
<InfoRow label={t('admin.customer.address.postal_code')} value={data.customer.address?.postal_code} />
|
|
211
228
|
</Stack>
|
|
212
229
|
}
|
|
213
230
|
/>
|
|
214
231
|
{state.editing.customer && (
|
|
215
232
|
<EditCustomer
|
|
216
|
-
data={data}
|
|
233
|
+
data={data.customer}
|
|
217
234
|
loading={state.loading.customer}
|
|
218
235
|
onSave={onUpdateInfo}
|
|
219
236
|
onCancel={() => setState((prev) => ({ editing: { ...prev.editing, customer: false } }))}
|
|
@@ -224,19 +241,19 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
224
241
|
<Box className="section">
|
|
225
242
|
<SectionHeader title={t('admin.subscriptions')} mb={0} />
|
|
226
243
|
<Box className="section-body">
|
|
227
|
-
<SubscriptionList features={{ customer: false, toolbar: false }} customer_id={data.id} />
|
|
244
|
+
<SubscriptionList features={{ customer: false, toolbar: false }} customer_id={data.customer.id} />
|
|
228
245
|
</Box>
|
|
229
246
|
</Box>
|
|
230
247
|
<Box className="section">
|
|
231
248
|
<SectionHeader title={t('admin.payments')} mb={0} />
|
|
232
249
|
<Box className="section-body">
|
|
233
|
-
<PaymentList features={{ customer: false, toolbar: false }} customer_id={data.id} />
|
|
250
|
+
<PaymentList features={{ customer: false, toolbar: false }} customer_id={data.customer.id} />
|
|
234
251
|
</Box>
|
|
235
252
|
</Box>
|
|
236
253
|
<Box className="section">
|
|
237
254
|
<SectionHeader title={t('admin.invoices')} mb={0} />
|
|
238
255
|
<Box className="section-body">
|
|
239
|
-
<InvoiceList features={{ customer: false, toolbar: false }} customer_id={data.id} />
|
|
256
|
+
<InvoiceList features={{ customer: false, toolbar: false }} customer_id={data.customer.id} />
|
|
240
257
|
</Box>
|
|
241
258
|
</Box>
|
|
242
259
|
<Box className="section">
|
|
@@ -253,16 +270,16 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
253
270
|
</SectionHeader>
|
|
254
271
|
<Box className="section-body">
|
|
255
272
|
{!state.editing.metadata &&
|
|
256
|
-
(isEmpty(data.metadata) ? (
|
|
273
|
+
(isEmpty(data.customer.metadata) ? (
|
|
257
274
|
<Typography color="text.secondary">{t('common.metadata.empty')}</Typography>
|
|
258
275
|
) : (
|
|
259
|
-
Object.keys(data.metadata || {}).map((key) => (
|
|
260
|
-
<InfoRow key={key} label={key} value={data.metadata[key]} />
|
|
276
|
+
Object.keys(data.customer.metadata || {}).map((key) => (
|
|
277
|
+
<InfoRow key={key} label={key} value={data.customer.metadata[key]} />
|
|
261
278
|
))
|
|
262
279
|
))}
|
|
263
280
|
{state.editing.metadata && (
|
|
264
281
|
<MetadataEditor
|
|
265
|
-
data={data}
|
|
282
|
+
data={data.customer}
|
|
266
283
|
loading={state.loading.metadata}
|
|
267
284
|
onSave={onUpdateMetadata}
|
|
268
285
|
onCancel={() => setState((prev) => ({ editing: { ...prev.editing, metadata: false } }))}
|
|
@@ -273,7 +290,7 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
273
290
|
<Box className="section">
|
|
274
291
|
<SectionHeader title={t('admin.events')} />
|
|
275
292
|
<Box className="section-body">
|
|
276
|
-
<EventList features={{ toolbar: false }} object_id={data.id} />
|
|
293
|
+
<EventList features={{ toolbar: false }} object_id={data.customer.id} />
|
|
277
294
|
</Box>
|
|
278
295
|
</Box>
|
|
279
296
|
</Root>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
2
|
import { PaymentProvider, Switch, usePaymentContext } from '@blocklet/payment-react';
|
|
3
3
|
import { Box, Chip, Stack } from '@mui/material';
|
|
4
|
-
import React, { isValidElement, startTransition } from 'react';
|
|
4
|
+
import React, { Suspense, isValidElement, startTransition } from 'react';
|
|
5
5
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
6
6
|
|
|
7
7
|
import Layout from '../../components/layout/admin';
|
|
@@ -99,7 +99,9 @@ function Admin() {
|
|
|
99
99
|
</label>
|
|
100
100
|
</Stack>
|
|
101
101
|
</Stack>
|
|
102
|
-
<
|
|
102
|
+
<Suspense fallback={<div />}>
|
|
103
|
+
<div className="page-content">{isValidElement(TabComponent) ? TabComponent : <TabComponent />}</div>
|
|
104
|
+
</Suspense>
|
|
103
105
|
</Layout>
|
|
104
106
|
);
|
|
105
107
|
}
|
|
@@ -77,7 +77,8 @@ export default function PaymentIntentDetail(props: { id: string }) {
|
|
|
77
77
|
const onUpdateMetadata = createUpdater('metadata');
|
|
78
78
|
|
|
79
79
|
const currency = data.paymentCurrency;
|
|
80
|
-
const
|
|
80
|
+
const received = [fromUnitToToken(data?.amount_received, currency.decimal), currency.symbol].join(' ');
|
|
81
|
+
const total = [fromUnitToToken(data?.amount, currency.decimal), currency.symbol].join(' ');
|
|
81
82
|
|
|
82
83
|
return (
|
|
83
84
|
<Root direction="column" spacing={4} mb={4}>
|
|
@@ -96,7 +97,7 @@ export default function PaymentIntentDetail(props: { id: string }) {
|
|
|
96
97
|
<Box mt={2}>
|
|
97
98
|
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
|
98
99
|
<Stack direction="row" alignItems="center">
|
|
99
|
-
<Amount amount={
|
|
100
|
+
<Amount amount={received} sx={{ my: 0, fontSize: '2rem', lineHeight: '1rem' }} />
|
|
100
101
|
<Status label={data.status} color={getPaymentIntentStatusColor(data.status)} sx={{ ml: 2 }} />
|
|
101
102
|
</Stack>
|
|
102
103
|
<PaymentIntentActions data={data} variant="normal" />
|
|
@@ -117,7 +118,8 @@ export default function PaymentIntentDetail(props: { id: string }) {
|
|
|
117
118
|
<Box className="section">
|
|
118
119
|
<SectionHeader title={t('admin.details')} />
|
|
119
120
|
<Stack>
|
|
120
|
-
<InfoRow label={t('common.amount')} value={
|
|
121
|
+
<InfoRow label={t('common.amount')} value={total} />
|
|
122
|
+
<InfoRow label={t('admin.paymentIntent.received')} value={received} />
|
|
121
123
|
<InfoRow
|
|
122
124
|
label={t('common.status')}
|
|
123
125
|
value={
|
|
@@ -191,16 +193,18 @@ export default function PaymentIntentDetail(props: { id: string }) {
|
|
|
191
193
|
<Box className="section">
|
|
192
194
|
<SectionHeader title={t('admin.connections')} />
|
|
193
195
|
<Stack>
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
196
|
+
{data.subscription && (
|
|
197
|
+
<InfoRow
|
|
198
|
+
label={t('admin.subscription.name')}
|
|
199
|
+
value={<Link to={`/admin/billing/${data.subscription.id}`}>{data.subscription.id}</Link>}
|
|
200
|
+
/>
|
|
201
|
+
)}
|
|
202
|
+
{data.invoice_id && (
|
|
203
|
+
<InfoRow
|
|
204
|
+
label={t('admin.invoice.name')}
|
|
205
|
+
value={<Link to={`/admin/billing/${data.invoice_id}`}>{data.invoice_id}</Link>}
|
|
206
|
+
/>
|
|
207
|
+
)}
|
|
204
208
|
</Stack>
|
|
205
209
|
</Box>
|
|
206
210
|
<Box className="section">
|