payment-kit 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.
- package/.eslintrc.js +6 -0
- package/api/src/crons/index.ts +8 -0
- package/api/src/index.ts +4 -0
- package/api/src/libs/credit-grant.ts +146 -0
- package/api/src/libs/env.ts +1 -0
- package/api/src/libs/invoice.ts +4 -3
- package/api/src/libs/notification/template/base.ts +388 -2
- package/api/src/libs/notification/template/customer-credit-grant-granted.ts +149 -0
- package/api/src/libs/notification/template/customer-credit-grant-low-balance.ts +151 -0
- package/api/src/libs/notification/template/customer-credit-insufficient.ts +254 -0
- package/api/src/libs/notification/template/subscription-canceled.ts +193 -202
- package/api/src/libs/notification/template/subscription-refund-succeeded.ts +215 -237
- package/api/src/libs/notification/template/subscription-renewed.ts +130 -200
- package/api/src/libs/notification/template/subscription-succeeded.ts +100 -202
- package/api/src/libs/notification/template/subscription-trial-start.ts +142 -188
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +146 -174
- package/api/src/libs/notification/template/subscription-upgraded.ts +96 -192
- package/api/src/libs/notification/template/subscription-will-canceled.ts +94 -135
- package/api/src/libs/notification/template/subscription-will-renew.ts +220 -245
- package/api/src/libs/payment.ts +69 -0
- package/api/src/libs/queue/index.ts +3 -2
- package/api/src/libs/session.ts +8 -0
- package/api/src/libs/subscription.ts +74 -3
- package/api/src/libs/ws.ts +23 -1
- package/api/src/locales/en.ts +33 -0
- package/api/src/locales/zh.ts +31 -0
- package/api/src/queues/credit-consume.ts +715 -0
- package/api/src/queues/credit-grant.ts +572 -0
- package/api/src/queues/notification.ts +173 -128
- package/api/src/queues/payment.ts +210 -122
- package/api/src/queues/subscription.ts +179 -0
- package/api/src/routes/checkout-sessions.ts +157 -9
- package/api/src/routes/connect/shared.ts +3 -2
- package/api/src/routes/credit-grants.ts +241 -0
- package/api/src/routes/credit-transactions.ts +208 -0
- package/api/src/routes/index.ts +8 -0
- package/api/src/routes/meter-events.ts +347 -0
- package/api/src/routes/meters.ts +219 -0
- package/api/src/routes/payment-currencies.ts +14 -2
- package/api/src/routes/payment-links.ts +1 -1
- package/api/src/routes/payment-methods.ts +14 -2
- package/api/src/routes/prices.ts +43 -0
- package/api/src/routes/pricing-table.ts +13 -7
- package/api/src/routes/products.ts +63 -4
- package/api/src/routes/settings.ts +1 -1
- package/api/src/routes/subscriptions.ts +4 -0
- package/api/src/store/migrations/20250610-billing-credit.ts +43 -0
- package/api/src/store/models/credit-grant.ts +486 -0
- package/api/src/store/models/credit-transaction.ts +268 -0
- package/api/src/store/models/customer.ts +8 -0
- package/api/src/store/models/index.ts +52 -1
- package/api/src/store/models/meter-event.ts +423 -0
- package/api/src/store/models/meter.ts +176 -0
- package/api/src/store/models/payment-currency.ts +66 -14
- package/api/src/store/models/price.ts +6 -0
- package/api/src/store/models/product.ts +2 -2
- package/api/src/store/models/subscription.ts +24 -0
- package/api/src/store/models/types.ts +28 -2
- package/api/tests/libs/subscription.spec.ts +53 -0
- package/blocklet.yml +9 -1
- package/package.json +57 -58
- package/scripts/sdk.js +233 -1
- package/src/app.tsx +10 -0
- package/src/components/actions.tsx +22 -9
- package/src/components/balance-list.tsx +40 -12
- package/src/components/collapse.tsx +33 -15
- package/src/components/copyable.tsx +8 -7
- package/src/components/currency.tsx +15 -7
- package/src/components/customer/actions.tsx +1 -5
- package/src/components/customer/credit-grant-item-list.tsx +99 -0
- package/src/components/customer/credit-overview.tsx +233 -0
- package/src/components/customer/form.tsx +7 -2
- package/src/components/customer/link.tsx +4 -12
- package/src/components/customer/notification-preference.tsx +18 -9
- package/src/components/customer/overdraft-protection.tsx +112 -41
- package/src/components/drawer-form.tsx +42 -18
- package/src/components/error.tsx +1 -5
- package/src/components/event/list.tsx +9 -10
- package/src/components/filter-toolbar.tsx +20 -19
- package/src/components/info-card.tsx +32 -18
- package/src/components/info-metric.tsx +16 -6
- package/src/components/info-row-group.tsx +1 -7
- package/src/components/info-row.tsx +30 -24
- package/src/components/invoice/action.tsx +1 -7
- package/src/components/invoice/list.tsx +34 -26
- package/src/components/invoice/recharge.tsx +5 -7
- package/src/components/invoice/table.tsx +17 -12
- package/src/components/layout/user.tsx +1 -1
- package/src/components/metadata/form.tsx +290 -94
- package/src/components/metadata/list.tsx +11 -3
- package/src/components/meter/actions.tsx +101 -0
- package/src/components/meter/add-usage-dialog.tsx +239 -0
- package/src/components/meter/events-list.tsx +657 -0
- package/src/components/meter/form.tsx +245 -0
- package/src/components/meter/products.tsx +264 -0
- package/src/components/meter/usage-guide.tsx +174 -0
- package/src/components/passport/actions.tsx +9 -4
- package/src/components/payment-currency/add.tsx +16 -3
- package/src/components/payment-currency/form.tsx +14 -6
- package/src/components/payment-intent/actions.tsx +24 -16
- package/src/components/payment-intent/list.tsx +30 -9
- package/src/components/payment-link/actions.tsx +1 -5
- package/src/components/payment-link/after-pay.tsx +4 -2
- package/src/components/payment-link/before-pay.tsx +14 -4
- package/src/components/payment-link/item.tsx +27 -6
- package/src/components/payment-link/preview.tsx +9 -9
- package/src/components/payment-link/product-select.tsx +69 -15
- package/src/components/payment-method/arcblock.tsx +8 -1
- package/src/components/payment-method/base.tsx +8 -1
- package/src/components/payment-method/bitcoin.tsx +8 -1
- package/src/components/payment-method/ethereum.tsx +8 -1
- package/src/components/payment-method/evm-rpc-input.tsx +11 -7
- package/src/components/payment-method/form.tsx +2 -7
- package/src/components/payment-method/stripe.tsx +2 -0
- package/src/components/payouts/actions.tsx +1 -5
- package/src/components/payouts/list.tsx +30 -10
- package/src/components/payouts/portal/list.tsx +11 -9
- package/src/components/price/currency-select.tsx +63 -32
- package/src/components/price/form.tsx +895 -370
- package/src/components/price/upsell-select.tsx +10 -2
- package/src/components/price/upsell.tsx +7 -2
- package/src/components/pricing-table/actions.tsx +1 -5
- package/src/components/pricing-table/customer-settings.tsx +5 -1
- package/src/components/pricing-table/payment-settings.tsx +14 -4
- package/src/components/pricing-table/preview.tsx +9 -9
- package/src/components/pricing-table/price-item.tsx +6 -1
- package/src/components/pricing-table/product-item.tsx +6 -1
- package/src/components/pricing-table/product-settings.tsx +17 -4
- package/src/components/product/actions.tsx +1 -5
- package/src/components/product/add-price.tsx +9 -7
- package/src/components/product/create.tsx +8 -9
- package/src/components/product/cross-sell-select.tsx +5 -1
- package/src/components/product/cross-sell.tsx +7 -2
- package/src/components/product/edit-price.tsx +21 -12
- package/src/components/product/features.tsx +26 -6
- package/src/components/product/form.tsx +115 -72
- package/src/components/progress-bar.tsx +1 -1
- package/src/components/refund/actions.tsx +1 -7
- package/src/components/refund/list.tsx +31 -18
- package/src/components/section/header.tsx +12 -14
- package/src/components/subscription/actions/cancel.tsx +22 -5
- package/src/components/subscription/actions/index.tsx +9 -10
- package/src/components/subscription/actions/pause.tsx +32 -6
- package/src/components/subscription/actions/slash-stake.tsx +5 -3
- package/src/components/subscription/description.tsx +12 -8
- package/src/components/subscription/items/index.tsx +31 -16
- package/src/components/subscription/items/usage-records.tsx +19 -5
- package/src/components/subscription/list.tsx +5 -7
- package/src/components/subscription/metrics.tsx +62 -15
- package/src/components/subscription/portal/actions.tsx +78 -71
- package/src/components/subscription/portal/cancel.tsx +10 -3
- package/src/components/subscription/portal/list.tsx +48 -26
- package/src/components/uploader.tsx +5 -13
- package/src/components/webhook/attempts.tsx +51 -16
- package/src/components/webhook/request-info.tsx +8 -6
- package/src/contexts/products.tsx +27 -10
- package/src/hooks/subscription.ts +34 -0
- package/src/libs/meter-utils.ts +196 -0
- package/src/libs/util.ts +4 -0
- package/src/locales/en.tsx +385 -4
- package/src/locales/zh.tsx +364 -0
- package/src/pages/admin/billing/index.tsx +61 -33
- package/src/pages/admin/billing/invoices/detail.tsx +49 -13
- package/src/pages/admin/billing/meters/create.tsx +60 -0
- package/src/pages/admin/billing/meters/detail.tsx +435 -0
- package/src/pages/admin/billing/meters/index.tsx +210 -0
- package/src/pages/admin/billing/meters/meter-event.tsx +346 -0
- package/src/pages/admin/billing/subscriptions/detail.tsx +90 -25
- package/src/pages/admin/customers/customers/credit-grant/detail.tsx +391 -0
- package/src/pages/admin/customers/customers/detail.tsx +67 -14
- package/src/pages/admin/customers/customers/index.tsx +6 -1
- package/src/pages/admin/customers/index.tsx +5 -0
- package/src/pages/admin/developers/events/detail.tsx +37 -11
- package/src/pages/admin/developers/index.tsx +1 -1
- package/src/pages/admin/developers/webhooks/detail.tsx +41 -11
- package/src/pages/admin/index.tsx +15 -2
- package/src/pages/admin/overview.tsx +107 -19
- package/src/pages/admin/payments/intents/detail.tsx +58 -14
- package/src/pages/admin/payments/payouts/detail.tsx +63 -15
- package/src/pages/admin/payments/refunds/detail.tsx +58 -14
- package/src/pages/admin/products/index.tsx +11 -4
- package/src/pages/admin/products/links/create.tsx +22 -4
- package/src/pages/admin/products/links/detail.tsx +43 -14
- package/src/pages/admin/products/passports/index.tsx +23 -4
- package/src/pages/admin/products/prices/actions.tsx +16 -9
- package/src/pages/admin/products/prices/detail.tsx +73 -14
- package/src/pages/admin/products/prices/list.tsx +15 -3
- package/src/pages/admin/products/pricing-tables/create.tsx +45 -12
- package/src/pages/admin/products/pricing-tables/detail.tsx +45 -14
- package/src/pages/admin/products/products/create.tsx +233 -54
- package/src/pages/admin/products/products/detail.tsx +74 -18
- package/src/pages/admin/settings/index.tsx +8 -1
- package/src/pages/admin/settings/payment-methods/index.tsx +87 -19
- package/src/pages/admin/settings/vault-config/edit-form.tsx +42 -28
- package/src/pages/admin/settings/vault-config/index.tsx +57 -10
- package/src/pages/customer/credit-grant/detail.tsx +308 -0
- package/src/pages/customer/index.tsx +76 -17
- package/src/pages/customer/invoice/detail.tsx +63 -14
- package/src/pages/customer/invoice/past-due.tsx +11 -3
- package/src/pages/customer/payout/detail.tsx +56 -13
- package/src/pages/customer/recharge/account.tsx +78 -18
- package/src/pages/customer/recharge/subscription.tsx +86 -25
- package/src/pages/customer/refund/list.tsx +60 -24
- package/src/pages/customer/subscription/change-payment.tsx +17 -6
- package/src/pages/customer/subscription/change-plan.tsx +34 -7
- package/src/pages/customer/subscription/detail.tsx +134 -34
- package/src/pages/customer/subscription/embed.tsx +25 -5
- package/src/pages/home.tsx +26 -4
- package/src/pages/integrations/donations/edit-form.tsx +25 -9
- package/src/pages/integrations/donations/index.tsx +26 -9
- package/src/pages/integrations/donations/preview.tsx +59 -15
- package/src/pages/integrations/index.tsx +10 -1
- package/src/pages/integrations/overview.tsx +78 -17
- package/vite.config.ts +60 -30
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/* eslint-disable react/no-unstable-nested-components */
|
|
2
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
+
import {
|
|
4
|
+
api,
|
|
5
|
+
formatBNStr,
|
|
6
|
+
formatTime,
|
|
7
|
+
CreditTransactionsList,
|
|
8
|
+
CreditStatusChip,
|
|
9
|
+
getCustomerAvatar,
|
|
10
|
+
} from '@blocklet/payment-react';
|
|
11
|
+
import type { TCreditGrantExpanded } from '@blocklet/payment-types';
|
|
12
|
+
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
13
|
+
import { Alert, Avatar, Box, CircularProgress, Divider, Stack, Typography } from '@mui/material';
|
|
14
|
+
import { useRequest } from 'ahooks';
|
|
15
|
+
import { useNavigate, useParams } from 'react-router-dom';
|
|
16
|
+
import { styled } from '@mui/system';
|
|
17
|
+
import { useCallback } from 'react';
|
|
18
|
+
import InfoMetric from '../../../components/info-metric';
|
|
19
|
+
import { useSessionContext } from '../../../contexts/session';
|
|
20
|
+
import SectionHeader from '../../../components/section/header';
|
|
21
|
+
import CreditGrantItemList from '../../../components/customer/credit-grant-item-list';
|
|
22
|
+
import InfoRow from '../../../components/info-row';
|
|
23
|
+
import InfoRowGroup from '../../../components/info-row-group';
|
|
24
|
+
|
|
25
|
+
const fetchData = (id: string | undefined): Promise<TCreditGrantExpanded> => {
|
|
26
|
+
return api.get(`/api/credit-grants/${id}`).then((res: any) => res.data);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default function CustomerCreditGrantDetail() {
|
|
30
|
+
const { id } = useParams() as { id: string };
|
|
31
|
+
const navigate = useNavigate();
|
|
32
|
+
const { t } = useLocaleContext();
|
|
33
|
+
const { session } = useSessionContext();
|
|
34
|
+
const { loading, error, data } = useRequest(() => fetchData(id));
|
|
35
|
+
|
|
36
|
+
const handleBack = useCallback(() => {
|
|
37
|
+
navigate('/customer', { replace: true });
|
|
38
|
+
}, [navigate]);
|
|
39
|
+
|
|
40
|
+
if (data?.customer?.did && session?.user?.did && data.customer.did !== session.user.did) {
|
|
41
|
+
return <Alert severity="error">You do not have permission to access other customer data</Alert>;
|
|
42
|
+
}
|
|
43
|
+
if (error) {
|
|
44
|
+
return <Alert severity="error">{error.message}</Alert>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (loading || !data) {
|
|
48
|
+
return <CircularProgress />;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const isDepleted = data?.status === 'depleted';
|
|
52
|
+
|
|
53
|
+
const getStatusText = (status: string) => {
|
|
54
|
+
switch (status) {
|
|
55
|
+
case 'granted':
|
|
56
|
+
return t('admin.customer.creditGrants.status.granted');
|
|
57
|
+
case 'pending':
|
|
58
|
+
return t('admin.customer.creditGrants.status.pending');
|
|
59
|
+
case 'depleted':
|
|
60
|
+
return t('admin.customer.creditGrants.status.depleted');
|
|
61
|
+
case 'expired':
|
|
62
|
+
return t('admin.customer.creditGrants.status.expired');
|
|
63
|
+
case 'voided':
|
|
64
|
+
return t('admin.customer.creditGrants.status.voided');
|
|
65
|
+
default:
|
|
66
|
+
return status;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getUsagePercentage = () => {
|
|
71
|
+
if (!data.amount || !data.remaining_amount) return 0;
|
|
72
|
+
const total = parseFloat(data.amount);
|
|
73
|
+
const remaining = parseFloat(data.remaining_amount);
|
|
74
|
+
return ((total - remaining) / total) * 100;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Root>
|
|
79
|
+
<Box>
|
|
80
|
+
<Stack
|
|
81
|
+
className="page-header"
|
|
82
|
+
direction="row"
|
|
83
|
+
justifyContent="space-between"
|
|
84
|
+
alignItems="center"
|
|
85
|
+
sx={{ position: 'relative' }}>
|
|
86
|
+
<Stack
|
|
87
|
+
direction="row"
|
|
88
|
+
onClick={handleBack}
|
|
89
|
+
alignItems="center"
|
|
90
|
+
sx={{ fontWeight: 'normal', cursor: 'pointer' }}>
|
|
91
|
+
<ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
|
|
92
|
+
<Typography variant="h6" sx={{ color: 'text.secondary', fontWeight: 'normal' }}>
|
|
93
|
+
{t('admin.customer.creditGrants.title')}
|
|
94
|
+
</Typography>
|
|
95
|
+
</Stack>
|
|
96
|
+
</Stack>
|
|
97
|
+
<Box
|
|
98
|
+
mt={4}
|
|
99
|
+
sx={{
|
|
100
|
+
display: 'flex',
|
|
101
|
+
gap: {
|
|
102
|
+
xs: 2,
|
|
103
|
+
sm: 2,
|
|
104
|
+
md: 5,
|
|
105
|
+
},
|
|
106
|
+
flexWrap: 'wrap',
|
|
107
|
+
flexDirection: {
|
|
108
|
+
xs: 'column',
|
|
109
|
+
sm: 'column',
|
|
110
|
+
md: 'row',
|
|
111
|
+
},
|
|
112
|
+
alignItems: {
|
|
113
|
+
xs: 'flex-start',
|
|
114
|
+
sm: 'flex-start',
|
|
115
|
+
md: 'center',
|
|
116
|
+
},
|
|
117
|
+
}}>
|
|
118
|
+
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
|
119
|
+
<Stack direction="row" alignItems="center" flexWrap="wrap" gap={1}>
|
|
120
|
+
<Stack direction="column" alignItems="flex-start" justifyContent="space-around">
|
|
121
|
+
<Typography variant="h2" color="text.primary">
|
|
122
|
+
{data.name || data.id}
|
|
123
|
+
</Typography>
|
|
124
|
+
</Stack>
|
|
125
|
+
</Stack>
|
|
126
|
+
</Stack>
|
|
127
|
+
<Stack
|
|
128
|
+
className="section-body"
|
|
129
|
+
justifyContent="flex-start"
|
|
130
|
+
flexWrap="wrap"
|
|
131
|
+
sx={{
|
|
132
|
+
'hr.MuiDivider-root:last-child': {
|
|
133
|
+
display: 'none',
|
|
134
|
+
},
|
|
135
|
+
flexDirection: {
|
|
136
|
+
xs: 'column',
|
|
137
|
+
sm: 'column',
|
|
138
|
+
md: 'row',
|
|
139
|
+
},
|
|
140
|
+
alignItems: 'flex-start',
|
|
141
|
+
gap: {
|
|
142
|
+
xs: 1,
|
|
143
|
+
sm: 1,
|
|
144
|
+
md: 3,
|
|
145
|
+
},
|
|
146
|
+
}}>
|
|
147
|
+
<InfoMetric
|
|
148
|
+
label={t('common.status')}
|
|
149
|
+
value={<CreditStatusChip status={data.status} label={getStatusText(data.status)} />}
|
|
150
|
+
/>
|
|
151
|
+
<InfoMetric
|
|
152
|
+
label={t('admin.customer.creditGrants.originalAmount')}
|
|
153
|
+
value={
|
|
154
|
+
<Stack direction="row" alignItems="center" spacing={0.5}>
|
|
155
|
+
<Avatar
|
|
156
|
+
src={data.paymentCurrency?.logo}
|
|
157
|
+
sx={{ width: 16, height: 16 }}
|
|
158
|
+
alt={data.paymentCurrency?.symbol}
|
|
159
|
+
/>
|
|
160
|
+
<Typography variant="body2">
|
|
161
|
+
{formatBNStr(data.amount, data.paymentCurrency.decimal)} {data.paymentCurrency.symbol}
|
|
162
|
+
</Typography>
|
|
163
|
+
</Stack>
|
|
164
|
+
}
|
|
165
|
+
divider
|
|
166
|
+
/>
|
|
167
|
+
<InfoMetric
|
|
168
|
+
label={t('common.remainingCredit')}
|
|
169
|
+
value={
|
|
170
|
+
<Stack direction="row" alignItems="center" spacing={0.5}>
|
|
171
|
+
<Avatar
|
|
172
|
+
src={data.paymentCurrency?.logo}
|
|
173
|
+
sx={{ width: 16, height: 16 }}
|
|
174
|
+
alt={data.paymentCurrency?.symbol}
|
|
175
|
+
/>
|
|
176
|
+
<Typography variant="body2">
|
|
177
|
+
{formatBNStr(data.remaining_amount, data.paymentCurrency.decimal)} {data.paymentCurrency.symbol}
|
|
178
|
+
</Typography>
|
|
179
|
+
</Stack>
|
|
180
|
+
}
|
|
181
|
+
divider
|
|
182
|
+
/>
|
|
183
|
+
<InfoMetric
|
|
184
|
+
label={t('admin.customer.creditGrants.usage')}
|
|
185
|
+
value={
|
|
186
|
+
<Typography variant="body2" color={isDepleted ? 'error.main' : 'text.primary'}>
|
|
187
|
+
{getUsagePercentage().toFixed(1)}%
|
|
188
|
+
</Typography>
|
|
189
|
+
}
|
|
190
|
+
divider
|
|
191
|
+
/>
|
|
192
|
+
<InfoMetric
|
|
193
|
+
label={t('common.effectiveDate')}
|
|
194
|
+
value={formatTime(data.effective_at ? data.effective_at * 1000 : data.created_at, 'YYYY-MM-DD HH:mm:ss')}
|
|
195
|
+
/>
|
|
196
|
+
{data.expires_at && (
|
|
197
|
+
<InfoMetric
|
|
198
|
+
label={t('common.expirationDate')}
|
|
199
|
+
value={formatTime(data.expires_at * 1000, 'YYYY-MM-DD HH:mm:ss')}
|
|
200
|
+
/>
|
|
201
|
+
)}
|
|
202
|
+
</Stack>
|
|
203
|
+
</Box>
|
|
204
|
+
</Box>
|
|
205
|
+
<Box className="section" sx={{ containerType: 'inline-size' }}>
|
|
206
|
+
<SectionHeader title={t('admin.details')} />
|
|
207
|
+
<InfoRowGroup
|
|
208
|
+
sx={{
|
|
209
|
+
display: 'grid',
|
|
210
|
+
gridTemplateColumns: {
|
|
211
|
+
xs: 'repeat(1, 1fr)',
|
|
212
|
+
xl: 'repeat(2, 1fr)',
|
|
213
|
+
},
|
|
214
|
+
'@container (min-width: 1000px)': {
|
|
215
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
216
|
+
},
|
|
217
|
+
'.info-row-wrapper': {
|
|
218
|
+
gap: 1,
|
|
219
|
+
flexDirection: {
|
|
220
|
+
xs: 'column',
|
|
221
|
+
xl: 'row',
|
|
222
|
+
},
|
|
223
|
+
alignItems: {
|
|
224
|
+
xs: 'flex-start',
|
|
225
|
+
xl: 'center',
|
|
226
|
+
},
|
|
227
|
+
'@container (min-width: 1000px)': {
|
|
228
|
+
flexDirection: 'row',
|
|
229
|
+
alignItems: 'center',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
}}>
|
|
233
|
+
<InfoRow
|
|
234
|
+
label={t('common.customer')}
|
|
235
|
+
value={
|
|
236
|
+
<Stack direction="row" alignItems="center" spacing={1}>
|
|
237
|
+
<Avatar
|
|
238
|
+
src={getCustomerAvatar(
|
|
239
|
+
data.customer?.did,
|
|
240
|
+
data.customer?.updated_at ? new Date(data.customer.updated_at).toISOString() : '',
|
|
241
|
+
24
|
|
242
|
+
)}
|
|
243
|
+
alt={data.customer?.name}
|
|
244
|
+
sx={{ width: 24, height: 24 }}
|
|
245
|
+
/>
|
|
246
|
+
<Typography>{data.customer?.name}</Typography>
|
|
247
|
+
</Stack>
|
|
248
|
+
}
|
|
249
|
+
/>
|
|
250
|
+
<InfoRow
|
|
251
|
+
label={t('common.scope')}
|
|
252
|
+
value={
|
|
253
|
+
<Typography>
|
|
254
|
+
{data.applicability_config?.scope?.prices ? t('common.specific') : t('common.general')}
|
|
255
|
+
</Typography>
|
|
256
|
+
}
|
|
257
|
+
/>
|
|
258
|
+
<InfoRow label={t('admin.creditProduct.priority.label')} value={<Typography>{data.priority}</Typography>} />
|
|
259
|
+
<InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
|
|
260
|
+
</InfoRowGroup>
|
|
261
|
+
</Box>
|
|
262
|
+
|
|
263
|
+
<Divider />
|
|
264
|
+
{data.items && data.items.length > 0 && (
|
|
265
|
+
<>
|
|
266
|
+
<Box className="section">
|
|
267
|
+
<SectionHeader title={t('admin.creditProduct.associatedPrices.label')} />
|
|
268
|
+
<Box className="section-body">
|
|
269
|
+
<CreditGrantItemList data={data.items} currency={data.paymentCurrency} />
|
|
270
|
+
</Box>
|
|
271
|
+
</Box>
|
|
272
|
+
<Divider />
|
|
273
|
+
</>
|
|
274
|
+
)}
|
|
275
|
+
|
|
276
|
+
<Divider />
|
|
277
|
+
<Box className="section">
|
|
278
|
+
<Typography variant="h3" className="section-header">
|
|
279
|
+
{t('admin.creditTransactions.title')}
|
|
280
|
+
</Typography>
|
|
281
|
+
<Box className="section-body">
|
|
282
|
+
<CreditTransactionsList
|
|
283
|
+
customer_id={data.customer_id}
|
|
284
|
+
credit_grant_id={data.id}
|
|
285
|
+
showAdminColumns={false}
|
|
286
|
+
showTimeFilter
|
|
287
|
+
/>
|
|
288
|
+
</Box>
|
|
289
|
+
</Box>
|
|
290
|
+
</Root>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const Root = styled(Stack)`
|
|
295
|
+
margin-bottom: 24px;
|
|
296
|
+
gap: 24px;
|
|
297
|
+
flex-direction: column;
|
|
298
|
+
@media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
|
|
299
|
+
. {
|
|
300
|
+
border: none;
|
|
301
|
+
box-shadow: none;
|
|
302
|
+
padding: 0;
|
|
303
|
+
}
|
|
304
|
+
.section-header {
|
|
305
|
+
font-size: 18px;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
`;
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
usePaymentContext,
|
|
8
8
|
OverdueInvoicePayment,
|
|
9
9
|
} from '@blocklet/payment-react';
|
|
10
|
+
|
|
10
11
|
import type { GroupedBN, TCustomerExpanded } from '@blocklet/payment-types';
|
|
11
12
|
import {
|
|
12
13
|
ExpandMore,
|
|
@@ -38,6 +39,7 @@ import { flatten, isEmpty } from 'lodash';
|
|
|
38
39
|
import { memo, useEffect, useState } from 'react';
|
|
39
40
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
40
41
|
import { joinURL } from 'ufo';
|
|
42
|
+
import CreditOverview from '../../components/customer/credit-overview';
|
|
41
43
|
|
|
42
44
|
import { useTransitionContext } from '../../components/progress-bar';
|
|
43
45
|
import CurrentSubscriptions from '../../components/subscription/portal/list';
|
|
@@ -46,7 +48,15 @@ import { useSessionContext } from '../../contexts/session';
|
|
|
46
48
|
import api from '../../libs/api';
|
|
47
49
|
import CustomerRevenueList from '../../components/payouts/portal/list';
|
|
48
50
|
|
|
49
|
-
type Result = TCustomerExpanded & {
|
|
51
|
+
type Result = TCustomerExpanded & {
|
|
52
|
+
summary: { [key: string]: GroupedBN };
|
|
53
|
+
creditSummary?: {
|
|
54
|
+
grants?: { [key: string]: { totalAmount: string; remainingAmount: string; grantCount: number } };
|
|
55
|
+
transactions?: any;
|
|
56
|
+
pendingAmount?: { [key: string]: string };
|
|
57
|
+
};
|
|
58
|
+
error?: string;
|
|
59
|
+
};
|
|
50
60
|
|
|
51
61
|
const fetchData = (): Promise<Result> => {
|
|
52
62
|
return api.get('/api/customers/me?skipError=true&create=true').then((res) => res.data);
|
|
@@ -105,7 +115,14 @@ const CurrencyCard = memo(
|
|
|
105
115
|
const CardSkeleton = memo(({ height = 100 }: { height: number }) => {
|
|
106
116
|
return (
|
|
107
117
|
<Box className="base-card section">
|
|
108
|
-
<Box
|
|
118
|
+
<Box
|
|
119
|
+
className="section-header"
|
|
120
|
+
sx={{
|
|
121
|
+
display: 'flex',
|
|
122
|
+
justifyContent: 'space-between',
|
|
123
|
+
alignItems: 'center',
|
|
124
|
+
mb: 2,
|
|
125
|
+
}}>
|
|
109
126
|
<Skeleton variant="text" width={150} height={32} />
|
|
110
127
|
</Box>
|
|
111
128
|
<Skeleton variant="rectangular" height={height} />
|
|
@@ -266,6 +283,12 @@ export default function CustomerHome() {
|
|
|
266
283
|
}
|
|
267
284
|
};
|
|
268
285
|
|
|
286
|
+
// const handleAddCredit = (currency: any) => {
|
|
287
|
+
// if (currency?.id) {
|
|
288
|
+
// navigate(`/customer/credit-packages?currency=${currency.id}`);
|
|
289
|
+
// }
|
|
290
|
+
// };
|
|
291
|
+
|
|
269
292
|
const onToggleActive = (e: SelectChangeEvent) => {
|
|
270
293
|
setSubscriptionLoading(true);
|
|
271
294
|
setState({ onlyActive: e.target.value === 'active' });
|
|
@@ -274,12 +297,21 @@ export default function CustomerHome() {
|
|
|
274
297
|
}, 300);
|
|
275
298
|
};
|
|
276
299
|
|
|
300
|
+
// const handleViewCreditDetails = (currencyId: string) => {
|
|
301
|
+
// navigate(`/customer/credit-grants?currency=${currencyId}`);
|
|
302
|
+
// };
|
|
303
|
+
|
|
277
304
|
const SubscriptionCard =
|
|
278
305
|
loadingCard || !hasSubscriptions ? null : (
|
|
279
306
|
<Box className="base-card section section-subscription">
|
|
280
307
|
<Box className="section-header">
|
|
281
308
|
<Typography variant="h3">{t('customer.subscription.title')}</Typography>
|
|
282
|
-
<Stack
|
|
309
|
+
<Stack
|
|
310
|
+
direction="row"
|
|
311
|
+
spacing={1}
|
|
312
|
+
sx={{
|
|
313
|
+
alignItems: 'center',
|
|
314
|
+
}}>
|
|
283
315
|
<NotificationPreference />
|
|
284
316
|
{subscriptionStatus && (
|
|
285
317
|
<FormControl
|
|
@@ -340,14 +372,16 @@ export default function CustomerHome() {
|
|
|
340
372
|
<Typography variant="h3">{t('admin.customer.summary.stats')}</Typography>
|
|
341
373
|
</Box>
|
|
342
374
|
<Stack
|
|
343
|
-
gap={2}
|
|
344
|
-
mt={2}
|
|
345
375
|
sx={{
|
|
376
|
+
gap: 2,
|
|
377
|
+
mt: 2,
|
|
346
378
|
display: 'grid',
|
|
379
|
+
|
|
347
380
|
gridTemplateColumns: {
|
|
348
381
|
xs: 'repeat(1, 1fr)',
|
|
349
382
|
md: 'repeat(2, 1fr)',
|
|
350
383
|
},
|
|
384
|
+
|
|
351
385
|
'@container (max-width: 600px)': {
|
|
352
386
|
gridTemplateColumns: 'repeat(1, 1fr)',
|
|
353
387
|
},
|
|
@@ -368,8 +402,17 @@ export default function CustomerHome() {
|
|
|
368
402
|
borderColor: 'grey.100',
|
|
369
403
|
boxShadow: 1,
|
|
370
404
|
}}>
|
|
371
|
-
<Stack
|
|
372
|
-
|
|
405
|
+
<Stack
|
|
406
|
+
sx={{
|
|
407
|
+
flexDirection: 'row',
|
|
408
|
+
justifyContent: 'space-between',
|
|
409
|
+
}}>
|
|
410
|
+
<Box
|
|
411
|
+
sx={{
|
|
412
|
+
alignItems: 'center',
|
|
413
|
+
display: 'flex',
|
|
414
|
+
gap: 1,
|
|
415
|
+
}}>
|
|
373
416
|
<Avatar src={c?.logo} alt={c?.symbol} sx={{ width: 18, height: 18 }} />
|
|
374
417
|
<Typography
|
|
375
418
|
variant="h5"
|
|
@@ -377,7 +420,11 @@ export default function CustomerHome() {
|
|
|
377
420
|
sx={{ fontSize: '16px', color: 'text.primary', fontWeight: '500' }}>
|
|
378
421
|
{c?.symbol}
|
|
379
422
|
</Typography>
|
|
380
|
-
<Typography
|
|
423
|
+
<Typography
|
|
424
|
+
sx={{
|
|
425
|
+
color: 'text.lighter',
|
|
426
|
+
fontSize: 12,
|
|
427
|
+
}}>
|
|
381
428
|
{c?.methodName}
|
|
382
429
|
</Typography>
|
|
383
430
|
</Box>
|
|
@@ -401,7 +448,6 @@ export default function CustomerHome() {
|
|
|
401
448
|
gap: 1,
|
|
402
449
|
mt: 1.5,
|
|
403
450
|
}}>
|
|
404
|
-
{/* 使用配置渲染卡片 */}
|
|
405
451
|
{Object.entries(CARD_CONFIG).map(([type, config]) => {
|
|
406
452
|
if (!isCardVisible(type, config, data, c, method)) {
|
|
407
453
|
return null;
|
|
@@ -433,6 +479,18 @@ export default function CustomerHome() {
|
|
|
433
479
|
</Box>
|
|
434
480
|
);
|
|
435
481
|
|
|
482
|
+
// 独立的Credit Card组件
|
|
483
|
+
const CreditCard = loadingCard ? (
|
|
484
|
+
<CardSkeleton height={400} />
|
|
485
|
+
) : (
|
|
486
|
+
<Box className="base-card section section-credit">
|
|
487
|
+
<Box className="section-header" sx={{ mb: 2 }}>
|
|
488
|
+
<Typography variant="h3">{t('admin.creditGrants.title')}</Typography>
|
|
489
|
+
</Box>
|
|
490
|
+
<CreditOverview customerId={data?.id} settings={settings} />
|
|
491
|
+
</Box>
|
|
492
|
+
);
|
|
493
|
+
|
|
436
494
|
const InvoiceCard = loadingCard ? (
|
|
437
495
|
<CardSkeleton height={200} />
|
|
438
496
|
) : (
|
|
@@ -461,15 +519,14 @@ export default function CustomerHome() {
|
|
|
461
519
|
</Box>
|
|
462
520
|
);
|
|
463
521
|
|
|
464
|
-
const RevenueCard =
|
|
465
|
-
|
|
466
|
-
<Box className="
|
|
467
|
-
<
|
|
468
|
-
<Typography variant="h3">{t('customer.payout.title')}</Typography>
|
|
469
|
-
</Box>
|
|
470
|
-
<CustomerRevenueList setHasRevenues={setHasRevenues} />
|
|
522
|
+
const RevenueCard = loadingCard ? null : (
|
|
523
|
+
<Box className="base-card section section-revenue" sx={{ visibility: hasRevenues ? 'visible' : 'hidden' }}>
|
|
524
|
+
<Box className="section-header">
|
|
525
|
+
<Typography variant="h3">{t('customer.payout.title')}</Typography>
|
|
471
526
|
</Box>
|
|
472
|
-
|
|
527
|
+
<CustomerRevenueList setHasRevenues={setHasRevenues} />
|
|
528
|
+
</Box>
|
|
529
|
+
);
|
|
473
530
|
|
|
474
531
|
return (
|
|
475
532
|
<Content>
|
|
@@ -525,6 +582,8 @@ export default function CustomerHome() {
|
|
|
525
582
|
<Root>
|
|
526
583
|
{SummaryCard}
|
|
527
584
|
{SummaryCard && <Divider />}
|
|
585
|
+
{CreditCard}
|
|
586
|
+
{CreditCard && <Divider />}
|
|
528
587
|
{SubscriptionCard}
|
|
529
588
|
{SubscriptionCard && <Divider />}
|
|
530
589
|
{InvoiceCard}
|
|
@@ -128,18 +128,32 @@ export default function CustomerInvoiceDetail() {
|
|
|
128
128
|
const paymentDetails = data.paymentIntent?.payment_details || data.metadata?.payment_details;
|
|
129
129
|
return (
|
|
130
130
|
<InvoiceDetailRoot direction="column" spacing={3}>
|
|
131
|
-
<Stack
|
|
131
|
+
<Stack
|
|
132
|
+
direction="row"
|
|
133
|
+
sx={{
|
|
134
|
+
justifyContent: 'space-between',
|
|
135
|
+
}}>
|
|
132
136
|
<Stack
|
|
133
137
|
direction="row"
|
|
134
138
|
onClick={() => goBackOrFallback('/customer')}
|
|
135
|
-
|
|
136
|
-
|
|
139
|
+
sx={{
|
|
140
|
+
alignItems: 'center',
|
|
141
|
+
fontWeight: 'normal',
|
|
142
|
+
padding: '10px 0',
|
|
143
|
+
cursor: 'pointer',
|
|
144
|
+
}}>
|
|
137
145
|
<ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
|
|
138
146
|
<Typography variant="h6" sx={{ color: 'text.secondary', fontWeight: 'normal' }}>
|
|
139
147
|
{t('common.previous')}
|
|
140
148
|
</Typography>
|
|
141
149
|
</Stack>
|
|
142
|
-
<Stack
|
|
150
|
+
<Stack
|
|
151
|
+
direction="row"
|
|
152
|
+
spacing={1}
|
|
153
|
+
sx={{
|
|
154
|
+
justifyContent: 'flex-end',
|
|
155
|
+
alignItems: 'center',
|
|
156
|
+
}}>
|
|
143
157
|
{['open', 'paid', 'uncollectible'].includes(data.status) && !isDonation && <Download data={data} />}
|
|
144
158
|
{data?.paymentLink?.donation_settings?.reference && (
|
|
145
159
|
<Button
|
|
@@ -164,49 +178,68 @@ export default function CustomerInvoiceDetail() {
|
|
|
164
178
|
</Stack>
|
|
165
179
|
</Stack>
|
|
166
180
|
<Box
|
|
167
|
-
mt={4}
|
|
168
181
|
sx={{
|
|
182
|
+
mt: 4,
|
|
169
183
|
display: 'flex',
|
|
184
|
+
|
|
170
185
|
gap: {
|
|
171
186
|
xs: 2,
|
|
172
187
|
sm: 2,
|
|
173
188
|
md: 5,
|
|
174
189
|
},
|
|
190
|
+
|
|
175
191
|
flexWrap: 'wrap',
|
|
192
|
+
|
|
176
193
|
flexDirection: {
|
|
177
194
|
xs: 'column',
|
|
178
195
|
sm: 'column',
|
|
179
196
|
md: 'row',
|
|
180
197
|
},
|
|
198
|
+
|
|
181
199
|
alignItems: {
|
|
182
200
|
xs: 'flex-start',
|
|
183
201
|
sm: 'flex-start',
|
|
184
202
|
md: 'center',
|
|
185
203
|
},
|
|
186
204
|
}}>
|
|
187
|
-
<Stack
|
|
188
|
-
|
|
205
|
+
<Stack
|
|
206
|
+
direction="row"
|
|
207
|
+
sx={{
|
|
208
|
+
justifyContent: 'space-between',
|
|
209
|
+
alignItems: 'center',
|
|
210
|
+
}}>
|
|
211
|
+
<Stack
|
|
212
|
+
direction="row"
|
|
213
|
+
sx={{
|
|
214
|
+
alignItems: 'center',
|
|
215
|
+
flexWrap: 'wrap',
|
|
216
|
+
gap: 1,
|
|
217
|
+
}}>
|
|
189
218
|
<Typography variant="h1">{data.number}</Typography>
|
|
190
219
|
</Stack>
|
|
191
220
|
</Stack>
|
|
192
221
|
<Stack
|
|
193
222
|
className="section-body"
|
|
194
|
-
justifyContent="flex-start"
|
|
195
|
-
flexWrap="wrap"
|
|
196
223
|
sx={{
|
|
224
|
+
justifyContent: 'flex-start',
|
|
225
|
+
flexWrap: 'wrap',
|
|
226
|
+
|
|
197
227
|
'hr.MuiDivider-root:last-child': {
|
|
198
228
|
display: 'none',
|
|
199
229
|
},
|
|
230
|
+
|
|
200
231
|
flexDirection: {
|
|
201
232
|
xs: 'column',
|
|
202
233
|
sm: 'column',
|
|
203
234
|
md: 'row',
|
|
204
235
|
},
|
|
236
|
+
|
|
205
237
|
alignItems: {
|
|
206
238
|
xs: 'flex-start',
|
|
207
239
|
sm: 'flex-start',
|
|
208
240
|
md: 'center',
|
|
209
241
|
},
|
|
242
|
+
|
|
210
243
|
gap: {
|
|
211
244
|
xs: 1,
|
|
212
245
|
sm: 1,
|
|
@@ -232,7 +265,11 @@ export default function CustomerInvoiceDetail() {
|
|
|
232
265
|
label={t('admin.subscription.name')}
|
|
233
266
|
value={
|
|
234
267
|
<Link to={`/customer/subscription/${data.subscription.id}`}>
|
|
235
|
-
<Typography
|
|
268
|
+
<Typography
|
|
269
|
+
variant="body1"
|
|
270
|
+
sx={{
|
|
271
|
+
color: 'text.link',
|
|
272
|
+
}}>
|
|
236
273
|
{data.subscription.description || data.subscription.id}
|
|
237
274
|
</Typography>
|
|
238
275
|
</Link>
|
|
@@ -252,7 +289,12 @@ export default function CustomerInvoiceDetail() {
|
|
|
252
289
|
</Box>
|
|
253
290
|
<Divider />
|
|
254
291
|
<Box className="section" sx={{ containerType: 'inline-size' }}>
|
|
255
|
-
<Typography
|
|
292
|
+
<Typography
|
|
293
|
+
variant="h3"
|
|
294
|
+
className="section-header"
|
|
295
|
+
sx={{
|
|
296
|
+
mb: 3,
|
|
297
|
+
}}>
|
|
256
298
|
{t('payment.customer.invoice.details')}
|
|
257
299
|
</Typography>
|
|
258
300
|
<InfoRowGroup
|
|
@@ -331,11 +373,13 @@ export default function CustomerInvoiceDetail() {
|
|
|
331
373
|
value={
|
|
332
374
|
<Typography
|
|
333
375
|
variant="body1"
|
|
334
|
-
color="text.link"
|
|
335
|
-
sx={{ cursor: 'pointer' }}
|
|
336
376
|
component="a"
|
|
337
377
|
onClick={() => {
|
|
338
378
|
window.open(joinURL(getPrefix(), `/customer/invoice/${data.relatedInvoice?.id}`), '_self');
|
|
379
|
+
}}
|
|
380
|
+
sx={{
|
|
381
|
+
color: 'text.link',
|
|
382
|
+
cursor: 'pointer',
|
|
339
383
|
}}>
|
|
340
384
|
{data.relatedInvoice?.number}
|
|
341
385
|
</Typography>
|
|
@@ -367,7 +411,12 @@ export default function CustomerInvoiceDetail() {
|
|
|
367
411
|
<>
|
|
368
412
|
<Divider />
|
|
369
413
|
<Box className="section">
|
|
370
|
-
<Typography
|
|
414
|
+
<Typography
|
|
415
|
+
variant="h3"
|
|
416
|
+
className="section-header"
|
|
417
|
+
sx={{
|
|
418
|
+
mb: 1.5,
|
|
419
|
+
}}>
|
|
371
420
|
{t('payment.customer.products')}
|
|
372
421
|
</Typography>
|
|
373
422
|
<InvoiceTable invoice={data} simple />
|
|
@@ -109,12 +109,20 @@ export default function CustomerInvoicePastDue() {
|
|
|
109
109
|
|
|
110
110
|
return (
|
|
111
111
|
<Stack direction="column" spacing={3} sx={{ my: 2 }}>
|
|
112
|
-
<Stack
|
|
112
|
+
<Stack
|
|
113
|
+
direction="row"
|
|
114
|
+
sx={{
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
justifyContent: 'space-between',
|
|
117
|
+
}}>
|
|
113
118
|
<Stack
|
|
114
119
|
direction="row"
|
|
115
120
|
onClick={() => goBackOrFallback('/customer')}
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
sx={{
|
|
122
|
+
alignItems: 'center',
|
|
123
|
+
fontWeight: 'normal',
|
|
124
|
+
cursor: 'pointer',
|
|
125
|
+
}}>
|
|
118
126
|
<ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
|
|
119
127
|
<Typography variant="h6" sx={{ color: 'text.secondary', fontWeight: 'normal' }}>
|
|
120
128
|
{t('common.previous')}
|