payment-kit 1.18.15 → 1.18.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/api/src/index.ts +2 -0
  2. package/api/src/libs/invoice.ts +5 -3
  3. package/api/src/libs/notification/template/customer-reward-succeeded.ts +32 -14
  4. package/api/src/libs/session.ts +9 -1
  5. package/api/src/libs/util.ts +12 -4
  6. package/api/src/routes/checkout-sessions.ts +286 -120
  7. package/api/src/routes/connect/change-payment.ts +9 -1
  8. package/api/src/routes/connect/change-plan.ts +9 -1
  9. package/api/src/routes/connect/collect-batch.ts +7 -5
  10. package/api/src/routes/connect/pay.ts +1 -1
  11. package/api/src/routes/connect/recharge-account.ts +124 -0
  12. package/api/src/routes/connect/setup.ts +8 -1
  13. package/api/src/routes/connect/shared.ts +175 -54
  14. package/api/src/routes/connect/subscribe.ts +11 -1
  15. package/api/src/routes/customers.ts +150 -7
  16. package/api/src/routes/donations.ts +1 -1
  17. package/api/src/routes/invoices.ts +47 -1
  18. package/api/src/routes/subscriptions.ts +0 -3
  19. package/blocklet.yml +2 -1
  20. package/package.json +16 -16
  21. package/src/app.tsx +11 -3
  22. package/src/components/info-card.tsx +6 -2
  23. package/src/components/info-row.tsx +1 -0
  24. package/src/components/invoice/recharge.tsx +85 -56
  25. package/src/components/invoice/table.tsx +7 -1
  26. package/src/components/subscription/portal/actions.tsx +1 -1
  27. package/src/components/subscription/portal/list.tsx +6 -0
  28. package/src/locales/en.tsx +9 -0
  29. package/src/locales/zh.tsx +9 -0
  30. package/src/pages/admin/payments/payouts/detail.tsx +16 -5
  31. package/src/pages/customer/index.tsx +226 -284
  32. package/src/pages/customer/invoice/detail.tsx +24 -16
  33. package/src/pages/customer/invoice/past-due.tsx +46 -23
  34. package/src/pages/customer/payout/detail.tsx +16 -5
  35. package/src/pages/customer/recharge/account.tsx +513 -0
  36. package/src/pages/customer/{recharge.tsx → recharge/subscription.tsx} +22 -19
  37. package/src/pages/customer/subscription/embed.tsx +16 -1
@@ -0,0 +1,513 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { useParams, useNavigate } from 'react-router-dom';
3
+ import Toast from '@arcblock/ux/lib/Toast';
4
+ import {
5
+ Box,
6
+ Typography,
7
+ TextField,
8
+ Button,
9
+ CircularProgress,
10
+ Alert,
11
+ Stack,
12
+ Divider,
13
+ Card,
14
+ CardActionArea,
15
+ Grid,
16
+ Paper,
17
+ Avatar,
18
+ } from '@mui/material';
19
+ import { styled } from '@mui/system';
20
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
21
+ import {
22
+ usePaymentContext,
23
+ getPrefix,
24
+ formatError,
25
+ formatAmountPrecisionLimit,
26
+ api,
27
+ formatBNStr,
28
+ formatPrice,
29
+ } from '@blocklet/payment-react';
30
+ import type { TPaymentCurrency, TPaymentMethod } from '@blocklet/payment-types';
31
+ import { joinURL } from 'ufo';
32
+ import { AccountBalanceWalletOutlined, ArrowBackOutlined, ArrowForwardOutlined } from '@mui/icons-material';
33
+ import Empty from '@arcblock/ux/lib/Empty';
34
+ import RechargeList from '../../../components/invoice/recharge';
35
+ import { getTokenBalanceLink, goBackOrFallback } from '../../../libs/util';
36
+ import { useSessionContext } from '../../../contexts/session';
37
+
38
+ // 扩展PaymentCurrency类型以包含paymentMethod
39
+ interface ExtendedPaymentCurrency extends TPaymentCurrency {
40
+ paymentMethod?: TPaymentMethod;
41
+ }
42
+
43
+ interface Subscription {
44
+ id: string;
45
+ description?: string;
46
+ status: string;
47
+ paymentMethod?: {
48
+ logo?: string;
49
+ name?: string;
50
+ };
51
+ paymentCurrency?: {
52
+ symbol: string;
53
+ decimal: number;
54
+ };
55
+ items?: Array<{
56
+ price: any;
57
+ }>;
58
+ }
59
+
60
+ const Root = styled(Stack)(({ theme }) => ({
61
+ marginBottom: theme.spacing(3),
62
+ gap: theme.spacing(3),
63
+ flexDirection: 'column',
64
+ }));
65
+
66
+ const BalanceCard = styled(Card)(({ theme }) => ({
67
+ padding: theme.spacing(2),
68
+ backgroundColor: theme.palette.background.paper,
69
+ border: `1px solid ${theme.palette.divider}`,
70
+ borderRadius: theme.shape.borderRadius,
71
+ marginBottom: theme.spacing(2),
72
+ }));
73
+
74
+ export default function BalanceRechargePage() {
75
+ const { t, locale } = useLocaleContext();
76
+ const { currencyId } = useParams<{ currencyId: string }>();
77
+ const navigate = useNavigate();
78
+ const { connect } = usePaymentContext();
79
+ const [amount, setAmount] = useState('50');
80
+ const [amountError, setAmountError] = useState('');
81
+ const [loading, setLoading] = useState(true);
82
+ const [error, setError] = useState('');
83
+ const customInputRef = useRef<HTMLInputElement>(null);
84
+ const [payerValue, setPayerValue] = useState<{
85
+ paymentAddress: string;
86
+ token: string;
87
+ } | null>(null);
88
+ const [customAmount, setCustomAmount] = useState(false);
89
+ const [presetAmounts] = useState<Array<{ amount: string }>>([{ amount: '10' }, { amount: '50' }, { amount: '100' }]);
90
+ const { session } = useSessionContext();
91
+ const [currency, setCurrency] = useState<ExtendedPaymentCurrency | null>(null);
92
+ const [relatedSubscriptions, setRelatedSubscriptions] = useState<Subscription[]>([]);
93
+
94
+ const fetchData = async () => {
95
+ try {
96
+ setLoading(true);
97
+ if (!currencyId) {
98
+ // 如果没有 currencyId,重定向回客户页面
99
+ navigate('/customer');
100
+ return;
101
+ }
102
+
103
+ const { data } = await api.get(`/api/customers/recharge?currencyId=${currencyId}`);
104
+
105
+ if (data.currency) {
106
+ setCurrency(data.currency);
107
+ setRelatedSubscriptions(data.relatedSubscriptions || []);
108
+ const supportRecharge = data.currency?.paymentMethod?.type === 'arcblock';
109
+ if (supportRecharge) {
110
+ const payerTokenRes = await api.get(`/api/customers/payer-token?currencyId=${currencyId}`);
111
+ if (payerTokenRes?.data) {
112
+ setPayerValue(payerTokenRes.data);
113
+ }
114
+ }
115
+ } else {
116
+ setError(t('customer.balance.currency.notFound'));
117
+ }
118
+ } catch (err) {
119
+ setError(formatError(err) || t('common.fetchError'));
120
+ console.error(err);
121
+ } finally {
122
+ setLoading(false);
123
+ }
124
+ };
125
+
126
+ const rechargeRef = useRef<HTMLDivElement>(null);
127
+
128
+ useEffect(() => {
129
+ fetchData();
130
+ }, [currencyId]);
131
+
132
+ useEffect(() => {
133
+ if (rechargeRef.current && currency) {
134
+ setTimeout(() => {
135
+ if (rechargeRef.current) {
136
+ const rechargePosition = rechargeRef.current.getBoundingClientRect();
137
+ const absoluteTop = window.scrollY + rechargePosition.top;
138
+ const scrollToPosition = absoluteTop - 120;
139
+ window.scrollTo({
140
+ top: scrollToPosition,
141
+ behavior: 'smooth',
142
+ });
143
+ }
144
+ }, 200);
145
+ }
146
+ }, [currency, loading]);
147
+
148
+ const handleRecharge = () => {
149
+ if (!currency) return;
150
+
151
+ if (Number.isNaN(Number(amount))) {
152
+ return;
153
+ }
154
+
155
+ connect.open({
156
+ containerEl: undefined as unknown as Element,
157
+ saveConnect: false,
158
+ action: 'recharge-account',
159
+ prefix: joinURL(getPrefix(), '/api/did'),
160
+ extraParams: {
161
+ customerDid: session?.user?.did,
162
+ currencyId: currency.id,
163
+ amount: Number(amount),
164
+ },
165
+ onSuccess: () => {
166
+ connect.close();
167
+ Toast.success(t('customer.recharge.success'));
168
+ fetchData();
169
+ },
170
+ onClose: () => {
171
+ connect.close();
172
+ },
173
+ onError: (err: any) => {
174
+ Toast.error(formatError(err));
175
+ },
176
+ });
177
+ };
178
+
179
+ const handleAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
180
+ const { value } = e.target;
181
+ if (!currency) return;
182
+ if (!/^\d*\.?\d*$/.test(value)) return;
183
+ // 不允许以小数点开头
184
+ if (value.startsWith('.')) return;
185
+ const precision = currency.decimal;
186
+ const errorMessage = formatAmountPrecisionLimit(value, locale, precision || 6);
187
+ setAmountError(errorMessage || '');
188
+ setAmount(value);
189
+ };
190
+
191
+ const handleSelect = (selectedAmount: string) => {
192
+ setAmount(selectedAmount);
193
+ setCustomAmount(false);
194
+ };
195
+
196
+ const handleCustomSelect = () => {
197
+ setCustomAmount(true);
198
+ setAmount('');
199
+ setTimeout(() => {
200
+ customInputRef.current?.focus();
201
+ }, 0);
202
+ };
203
+
204
+ const handleSubscriptionClick = (subscriptionId: string) => {
205
+ navigate(`/customer/subscription/${subscriptionId}`);
206
+ };
207
+
208
+ if (loading) {
209
+ return (
210
+ <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '50vh' }}>
211
+ <CircularProgress />
212
+ </Box>
213
+ );
214
+ }
215
+
216
+ if (currency?.paymentMethod?.type !== 'arcblock') {
217
+ return (
218
+ <Box>
219
+ <Button startIcon={<ArrowBackOutlined />} variant="outlined" onClick={() => goBackOrFallback('/customer')}>
220
+ {t('common.previous')}
221
+ </Button>
222
+ <Empty>{t('customer.recharge.unsupported')}</Empty>
223
+ </Box>
224
+ );
225
+ }
226
+
227
+ if (error) {
228
+ return (
229
+ <Box>
230
+ <Button startIcon={<ArrowBackOutlined />} variant="outlined" onClick={() => goBackOrFallback('/customer')}>
231
+ {t('common.previous')}
232
+ </Button>
233
+ <Alert severity="error" sx={{ mt: 2 }}>
234
+ {error}
235
+ </Alert>
236
+ </Box>
237
+ );
238
+ }
239
+
240
+ const currentBalance = formatBNStr(payerValue?.token || '0', currency?.decimal || 0, 6, false);
241
+ const balanceLink = currency?.paymentMethod
242
+ ? getTokenBalanceLink(currency.paymentMethod, payerValue?.paymentAddress || '')
243
+ : undefined;
244
+
245
+ return (
246
+ <Root>
247
+ <Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ position: 'relative' }}>
248
+ <Button
249
+ startIcon={<ArrowBackOutlined />}
250
+ variant="text"
251
+ color="inherit"
252
+ onClick={() => goBackOrFallback('/customer')}
253
+ sx={{ color: 'text.secondary' }}>
254
+ {t('common.previous')}
255
+ </Button>
256
+ </Stack>
257
+
258
+ <Typography variant="h2" gutterBottom>
259
+ {currency?.symbol} {t('customer.recharge.title')}
260
+ </Typography>
261
+
262
+ {currency && (
263
+ <Box ref={rechargeRef}>
264
+ <Stack sx={{ maxWidth: '600px' }}>
265
+ <Box sx={{ mb: 4 }}>
266
+ <BalanceCard elevation={0}>
267
+ <Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
268
+ <Stack spacing={0.5}>
269
+ <Typography variant="body2" color="text.secondary">
270
+ {t('customer.recharge.receiveAddress')}
271
+ </Typography>
272
+ <Typography variant="body1" sx={{ wordBreak: 'break-all', fontFamily: 'monospace' }}>
273
+ {payerValue?.paymentAddress || t('customer.balance.addressNotFound')}
274
+ </Typography>
275
+ </Stack>
276
+ {currency.logo && (
277
+ <Avatar
278
+ src={currency.logo}
279
+ alt={currency.name}
280
+ sx={{
281
+ width: {
282
+ xs: 30,
283
+ sm: 40,
284
+ },
285
+ height: {
286
+ xs: 30,
287
+ sm: 40,
288
+ },
289
+ }}
290
+ />
291
+ )}
292
+ </Stack>
293
+ </BalanceCard>
294
+
295
+ <BalanceCard
296
+ elevation={0}
297
+ onClick={() => balanceLink && window.open(balanceLink, '_blank')}
298
+ sx={{
299
+ color: 'text.primary',
300
+ borderRadius: 'var(--radius-m, 8px)',
301
+ transition: 'all 0.2s ease-in-out',
302
+ cursor: balanceLink ? 'pointer' : 'default',
303
+ position: 'relative',
304
+ '&:hover': balanceLink
305
+ ? {
306
+ '& .arrow-icon': {
307
+ opacity: 1,
308
+ transform: 'translateX(0)',
309
+ },
310
+ }
311
+ : undefined,
312
+ }}>
313
+ <Stack direction="row" alignItems="center" justifyContent="space-between">
314
+ <Stack>
315
+ <Typography
316
+ variant="body1"
317
+ color="inherit"
318
+ sx={{ display: 'flex', alignItems: 'center', gap: 1, color: 'text.secondary' }}>
319
+ <AccountBalanceWalletOutlined color="success" fontSize="small" />
320
+ {t('admin.customer.summary.balance')}
321
+ </Typography>
322
+ <Typography variant="h3" sx={{ fontWeight: 'bold', mt: 1, color: 'text.primary' }}>
323
+ {currentBalance} {currency.symbol}
324
+ </Typography>
325
+ </Stack>
326
+ {balanceLink && (
327
+ <ArrowForwardOutlined
328
+ className="arrow-icon"
329
+ sx={{
330
+ opacity: 0,
331
+ transform: 'translateX(-10px)',
332
+ transition: 'all 0.2s ease-in-out',
333
+ color: 'inherit',
334
+ fontSize: 30,
335
+ }}
336
+ />
337
+ )}
338
+ </Stack>
339
+ </BalanceCard>
340
+ </Box>
341
+
342
+ <Paper elevation={0} sx={{ mb: 3, backgroundColor: 'background.default', borderRadius: '16px' }}>
343
+ <Grid container spacing={2}>
344
+ {presetAmounts.map(({ amount: presetAmount }) => (
345
+ <Grid item xs={6} sm={3} key={presetAmount}>
346
+ <Card
347
+ variant="outlined"
348
+ sx={{
349
+ height: '100%',
350
+ display: 'flex',
351
+ flexDirection: 'column',
352
+ justifyContent: 'center',
353
+ transition: 'all 0.2s',
354
+ cursor: 'pointer',
355
+ borderRadius: '12px',
356
+ '&:hover': {
357
+ transform: 'translateY(-4px)',
358
+ boxShadow: 3,
359
+ },
360
+ ...(amount === presetAmount && !customAmount
361
+ ? {
362
+ borderColor: 'primary.main',
363
+ borderWidth: 2,
364
+ backgroundColor: 'primary.lighter',
365
+ }
366
+ : {}),
367
+ }}>
368
+ <CardActionArea onClick={() => handleSelect(presetAmount)} sx={{ height: '100%', p: 1.5 }}>
369
+ <Typography
370
+ variant="h6"
371
+ align="center"
372
+ sx={{
373
+ fontWeight: 600,
374
+ color: amount === presetAmount && !customAmount ? 'primary.main' : 'text.primary',
375
+ }}>
376
+ {presetAmount} {currency.symbol}
377
+ </Typography>
378
+ </CardActionArea>
379
+ </Card>
380
+ </Grid>
381
+ ))}
382
+ <Grid item xs={6} sm={3}>
383
+ <Card
384
+ variant="outlined"
385
+ sx={{
386
+ height: '100%',
387
+ display: 'flex',
388
+ flexDirection: 'column',
389
+ justifyContent: 'center',
390
+ transition: 'all 0.2s',
391
+ cursor: 'pointer',
392
+ borderRadius: '12px',
393
+ '&:hover': {
394
+ transform: 'translateY(-4px)',
395
+ boxShadow: '0 4px 10px rgba(0,0,0,0.1)',
396
+ },
397
+ ...(customAmount
398
+ ? {
399
+ borderColor: 'primary.main',
400
+ borderWidth: 2,
401
+ backgroundColor: 'primary.lighter',
402
+ }
403
+ : {}),
404
+ }}>
405
+ <CardActionArea onClick={handleCustomSelect} sx={{ height: '100%', p: 1 }}>
406
+ <Stack direction="row" spacing={1} justifyContent="center" alignItems="center">
407
+ <Typography
408
+ variant="h6"
409
+ align="center"
410
+ sx={{ fontWeight: 600, color: customAmount ? 'primary.main' : 'text.primary' }}>
411
+ {t('customer.recharge.custom') || t('common.custom')}
412
+ </Typography>
413
+ </Stack>
414
+ </CardActionArea>
415
+ </Card>
416
+ </Grid>
417
+ </Grid>
418
+ </Paper>
419
+
420
+ {customAmount && (
421
+ <Box sx={{ mb: 3 }}>
422
+ <TextField
423
+ fullWidth
424
+ label={t('customer.recharge.amount')}
425
+ variant="outlined"
426
+ type="text"
427
+ value={amount}
428
+ error={!!amountError}
429
+ helperText={amountError}
430
+ onChange={handleAmountChange}
431
+ InputProps={{
432
+ endAdornment: <Typography>{currency.symbol}</Typography>,
433
+ autoComplete: 'off',
434
+ }}
435
+ inputRef={customInputRef}
436
+ />
437
+ </Box>
438
+ )}
439
+
440
+ <Button
441
+ fullWidth
442
+ size="large"
443
+ variant="contained"
444
+ color="primary"
445
+ onClick={handleRecharge}
446
+ disabled={!amount || parseFloat(amount) <= 0 || !!amountError}
447
+ sx={{
448
+ mb: 4,
449
+ py: 1.5,
450
+ borderRadius: '8px',
451
+ boxShadow: '0 4px 10px rgba(0,0,0,0.1)',
452
+ '&:hover': {
453
+ boxShadow: '0 6px 15px rgba(0,0,0,0.15)',
454
+ transform: 'translateY(-2px)',
455
+ },
456
+ transition: 'all 0.2s',
457
+ }}>
458
+ {t('customer.recharge.submit')}
459
+ </Button>
460
+ </Stack>
461
+ {relatedSubscriptions.length > 0 && (
462
+ <Box sx={{ mb: 3 }}>
463
+ <Typography variant="h4" gutterBottom>
464
+ {t('customer.recharge.relatedSubscriptions')}
465
+ </Typography>
466
+
467
+ <Stack direction="row" sx={{ flexWrap: 'wrap', gap: 2 }}>
468
+ {relatedSubscriptions.map((subscription) => (
469
+ <Stack
470
+ key={subscription.id}
471
+ onClick={() => handleSubscriptionClick(subscription.id)}
472
+ className="base-card"
473
+ sx={{
474
+ minWidth: '220px',
475
+ flex: 1,
476
+ }}>
477
+ <Stack direction="row" alignItems="center" spacing={1.5} sx={{ mb: 1 }}>
478
+ <Typography
479
+ variant="subtitle1"
480
+ sx={{
481
+ fontWeight: 'medium',
482
+ overflow: 'hidden',
483
+ textOverflow: 'ellipsis',
484
+ whiteSpace: 'nowrap',
485
+ color: 'text.link',
486
+ cursor: 'pointer',
487
+ }}>
488
+ {subscription.description || subscription.id}
489
+ </Typography>
490
+ </Stack>
491
+
492
+ {subscription.items && subscription.items[0] && currency && (
493
+ <Typography variant="body1" sx={{ color: 'text.secondary' }}>
494
+ {formatPrice(subscription.items[0].price, currency)}
495
+ </Typography>
496
+ )}
497
+ </Stack>
498
+ ))}
499
+ </Stack>
500
+ </Box>
501
+ )}
502
+ <Divider sx={{ mb: 3 }} />
503
+
504
+ <Typography variant="h4" gutterBottom>
505
+ {t('customer.recharge.history')}
506
+ </Typography>
507
+
508
+ <RechargeList currency_id={currencyId} />
509
+ </Box>
510
+ )}
511
+ </Root>
512
+ );
513
+ }
@@ -28,17 +28,17 @@ import {
28
28
  } from '@blocklet/payment-react';
29
29
  import { joinURL } from 'ufo';
30
30
  import type { TSubscriptionExpanded } from '@blocklet/payment-types';
31
- import { ArrowBackOutlined, ArrowForwardOutlined } from '@mui/icons-material';
31
+ import { AccountBalanceWalletOutlined, ArrowBackOutlined, ArrowForwardOutlined } from '@mui/icons-material';
32
32
  import { BN, fromUnitToToken } from '@ocap/util';
33
- import RechargeList from '../../components/invoice/recharge';
34
- import SubscriptionDescription from '../../components/subscription/description';
35
- import InfoRow from '../../components/info-row';
36
- import Currency from '../../components/currency';
37
- import SubscriptionMetrics from '../../components/subscription/metrics';
38
- import { getTokenBalanceLink, goBackOrFallback } from '../../libs/util';
39
- import CustomerLink from '../../components/customer/link';
40
- import { useSessionContext } from '../../contexts/session';
41
- import { formatSmartDuration, TimeUnit } from '../../libs/dayjs';
33
+ import RechargeList from '../../../components/invoice/recharge';
34
+ import SubscriptionDescription from '../../../components/subscription/description';
35
+ import InfoRow from '../../../components/info-row';
36
+ import Currency from '../../../components/currency';
37
+ import SubscriptionMetrics from '../../../components/subscription/metrics';
38
+ import { getTokenBalanceLink, goBackOrFallback } from '../../../libs/util';
39
+ import CustomerLink from '../../../components/customer/link';
40
+ import { useSessionContext } from '../../../contexts/session';
41
+ import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
42
42
 
43
43
  const Root = styled(Stack)(({ theme }) => ({
44
44
  marginBottom: theme.spacing(3),
@@ -328,7 +328,7 @@ export default function RechargePage() {
328
328
  <Divider />
329
329
 
330
330
  <Box sx={{ maxWidth: 600 }} ref={rechargeRef}>
331
- <Typography variant="h2" gutterBottom>
331
+ <Typography variant="h4" gutterBottom>
332
332
  {t('customer.recharge.title')}
333
333
  </Typography>
334
334
 
@@ -344,14 +344,11 @@ export default function RechargePage() {
344
344
  <BalanceCard
345
345
  onClick={() => balanceLink && window.open(balanceLink, '_blank')}
346
346
  sx={{
347
- background: 'var(--tags-tag-orange-bg, #B7FEE3)',
348
- color: 'var(--tags-tag-orange-text, #007C52)',
347
+ color: 'text.primary',
349
348
  borderRadius: 'var(--radius-m, 8px)',
350
- border: 'none',
351
-
352
349
  transition: 'all 0.2s ease-in-out',
353
- position: 'relative',
354
350
  cursor: balanceLink ? 'pointer' : 'default',
351
+ position: 'relative',
355
352
  '&:hover': balanceLink
356
353
  ? {
357
354
  '& .MuiSvgIcon-root': {
@@ -363,8 +360,14 @@ export default function RechargePage() {
363
360
  }}>
364
361
  <Stack direction="row" alignItems="center" spacing={1}>
365
362
  <Stack flex={1}>
366
- <Typography variant="subtitle1">{t('admin.customer.summary.balance')}</Typography>
367
- <Typography variant="h4">
363
+ <Typography
364
+ variant="body1"
365
+ color="inherit"
366
+ sx={{ display: 'flex', alignItems: 'center', gap: 1, color: 'text.secondary' }}>
367
+ <AccountBalanceWalletOutlined color="success" fontSize="small" />
368
+ {t('admin.customer.summary.balance')}
369
+ </Typography>
370
+ <Typography variant="h3" sx={{ fontWeight: 'bold', mt: 1, color: 'text.primary' }}>
368
371
  {currentBalance} {subscription.paymentCurrency.symbol}
369
372
  </Typography>
370
373
  </Stack>
@@ -492,7 +495,7 @@ export default function RechargePage() {
492
495
  )}
493
496
  </Box>
494
497
  <Divider />
495
- <Typography variant="h2" gutterBottom>
498
+ <Typography variant="h4" gutterBottom>
496
499
  {t('customer.recharge.history')}
497
500
  </Typography>
498
501
  <RechargeList subscription_id={subscriptionId} currency_id={paymentCurrency?.id} />
@@ -216,6 +216,7 @@ export default function SubscriptionEmbed() {
216
216
  )}
217
217
  name={`${subscription.customer.name} (${subscription.customer.email})`}
218
218
  description={<DidAddress did={subscription.customer.did} responsive={false} compact />}
219
+ className="owner-info-card"
219
220
  />
220
221
  ),
221
222
  });
@@ -241,7 +242,21 @@ export default function SubscriptionEmbed() {
241
242
  </Typography>
242
243
  <Box sx={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
243
244
  {infoList.map(({ name, value }) => {
244
- return <InfoRow label={name} value={value} direction="column" alignItems="flex-start" sx={{ mb: 0 }} />;
245
+ return (
246
+ <InfoRow
247
+ label={name}
248
+ value={value}
249
+ sx={{
250
+ mb: 0,
251
+ '.info-row-label': {
252
+ whiteSpace: 'nowrap',
253
+ },
254
+ '.info-row-value': {
255
+ flex: 'none',
256
+ },
257
+ }}
258
+ />
259
+ );
245
260
  })}
246
261
  </Box>
247
262
  <Divider />