payment-kit 1.18.18 → 1.18.20
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/subscription.ts +116 -0
- package/api/src/routes/checkout-sessions.ts +28 -1
- package/api/src/routes/customers.ts +5 -1
- package/api/src/store/migrations/20250318-donate-invoice.ts +45 -0
- package/api/tests/libs/subscription.spec.ts +311 -0
- package/blocklet.yml +1 -1
- package/package.json +8 -8
- package/src/components/currency.tsx +11 -4
- package/src/components/customer/link.tsx +54 -14
- package/src/components/customer/overdraft-protection.tsx +36 -2
- package/src/components/info-card.tsx +55 -7
- package/src/components/info-row-group.tsx +122 -0
- package/src/components/info-row.tsx +14 -1
- package/src/components/payouts/portal/list.tsx +7 -2
- package/src/components/subscription/items/index.tsx +1 -1
- package/src/components/subscription/metrics.tsx +14 -6
- package/src/contexts/info-row.tsx +4 -0
- package/src/libs/dayjs.ts +4 -3
- package/src/locales/en.tsx +1 -0
- package/src/locales/zh.tsx +1 -0
- package/src/pages/admin/billing/invoices/detail.tsx +54 -76
- package/src/pages/admin/billing/subscriptions/detail.tsx +34 -71
- package/src/pages/admin/customers/customers/detail.tsx +41 -64
- package/src/pages/admin/payments/intents/detail.tsx +28 -42
- package/src/pages/admin/payments/payouts/detail.tsx +27 -36
- package/src/pages/admin/payments/refunds/detail.tsx +27 -41
- package/src/pages/admin/products/links/detail.tsx +30 -55
- package/src/pages/admin/products/prices/detail.tsx +43 -50
- package/src/pages/admin/products/pricing-tables/detail.tsx +23 -25
- package/src/pages/admin/products/products/detail.tsx +52 -81
- package/src/pages/customer/index.tsx +189 -107
- package/src/pages/customer/invoice/detail.tsx +49 -50
- package/src/pages/customer/payout/detail.tsx +16 -22
- package/src/pages/customer/recharge/account.tsx +119 -34
- package/src/pages/customer/recharge/subscription.tsx +11 -1
- package/src/pages/customer/subscription/detail.tsx +176 -94
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getPrefix,
|
|
15
15
|
usePaymentContext,
|
|
16
16
|
PaymentBeneficiaries,
|
|
17
|
+
useMobile,
|
|
17
18
|
} from '@blocklet/payment-react';
|
|
18
19
|
import type { TCheckoutSession, TInvoiceExpanded, TPaymentLink } from '@blocklet/payment-types';
|
|
19
20
|
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
@@ -34,6 +35,7 @@ import InvoiceTable from '../../../components/invoice/table';
|
|
|
34
35
|
import { goBackOrFallback } from '../../../libs/util';
|
|
35
36
|
import CustomerRefundList from '../refund/list';
|
|
36
37
|
import InfoMetric from '../../../components/info-metric';
|
|
38
|
+
import InfoRowGroup from '../../../components/info-row-group';
|
|
37
39
|
|
|
38
40
|
const fetchData = (
|
|
39
41
|
id: string
|
|
@@ -43,11 +45,10 @@ const fetchData = (
|
|
|
43
45
|
return api.get(`/api/invoices/${id}`).then((res) => res.data);
|
|
44
46
|
};
|
|
45
47
|
|
|
46
|
-
const InfoDirection = 'column';
|
|
47
|
-
const InfoAlignItems = 'flex-start';
|
|
48
48
|
export default function CustomerInvoiceDetail() {
|
|
49
49
|
const { t, locale } = useLocaleContext();
|
|
50
50
|
const [searchParams] = useSearchParams();
|
|
51
|
+
const { isMobile } = useMobile();
|
|
51
52
|
const { connect } = usePaymentContext();
|
|
52
53
|
const params = useParams<{ id: string }>();
|
|
53
54
|
const [state, setState] = useSetState({
|
|
@@ -138,7 +139,16 @@ export default function CustomerInvoiceDetail() {
|
|
|
138
139
|
</Typography>
|
|
139
140
|
</Stack>
|
|
140
141
|
<Stack direction="row" spacing={1} justifyContent="flex-end" alignItems="center">
|
|
141
|
-
{['open', 'paid', 'uncollectible'].includes(data.status) && <Download data={data} />}
|
|
142
|
+
{['open', 'paid', 'uncollectible'].includes(data.status) && !isDonation && <Download data={data} />}
|
|
143
|
+
{data?.paymentLink?.donation_settings?.reference && (
|
|
144
|
+
<Button
|
|
145
|
+
variant="outlined"
|
|
146
|
+
onClick={() => {
|
|
147
|
+
window.open(data?.paymentLink?.donation_settings?.reference, '_blank');
|
|
148
|
+
}}>
|
|
149
|
+
{t('customer.payout.viewReference')}
|
|
150
|
+
</Button>
|
|
151
|
+
)}
|
|
142
152
|
{['open', 'uncollectible'].includes(data.status) && !hidePayButton && (
|
|
143
153
|
<Button
|
|
144
154
|
variant="outlined"
|
|
@@ -240,51 +250,50 @@ export default function CustomerInvoiceDetail() {
|
|
|
240
250
|
</Stack>
|
|
241
251
|
</Box>
|
|
242
252
|
<Divider />
|
|
243
|
-
<Box className="section">
|
|
253
|
+
<Box className="section" sx={{ containerType: 'inline-size' }}>
|
|
244
254
|
<Typography variant="h3" mb={3} className="section-header">
|
|
245
255
|
{t('payment.customer.invoice.details')}
|
|
246
256
|
</Typography>
|
|
247
|
-
<
|
|
248
|
-
className="invoice-summary-wrapper"
|
|
257
|
+
<InfoRowGroup
|
|
249
258
|
sx={{
|
|
250
259
|
display: 'grid',
|
|
251
260
|
gridTemplateColumns: {
|
|
252
261
|
xs: 'repeat(1, 1fr)',
|
|
253
|
-
|
|
254
|
-
md: 'repeat(2, 1fr)',
|
|
255
|
-
lg: 'repeat(3, 1fr)',
|
|
262
|
+
lg: 'repeat(2, 1fr)',
|
|
256
263
|
},
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
264
|
+
'@container (min-width: 980px)': {
|
|
265
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
266
|
+
},
|
|
267
|
+
'.info-row-wrapper': {
|
|
268
|
+
gap: 1,
|
|
269
|
+
flexDirection: {
|
|
270
|
+
xs: 'column',
|
|
271
|
+
lg: 'row',
|
|
272
|
+
},
|
|
273
|
+
alignItems: {
|
|
274
|
+
xs: 'flex-start',
|
|
275
|
+
lg: 'center',
|
|
276
|
+
},
|
|
277
|
+
'@container (min-width: 980px)': {
|
|
278
|
+
flexDirection: 'row',
|
|
279
|
+
alignItems: 'center',
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
'.currency-name': {
|
|
283
|
+
color: 'text.secondary',
|
|
260
284
|
},
|
|
261
285
|
}}>
|
|
262
286
|
<InfoRow
|
|
263
|
-
label={t('admin.invoice.
|
|
264
|
-
value={
|
|
265
|
-
direction={InfoDirection}
|
|
266
|
-
alignItems={InfoAlignItems}
|
|
267
|
-
/>
|
|
268
|
-
<InfoRow
|
|
269
|
-
label={t('admin.invoice.from')}
|
|
270
|
-
value={data.statement_descriptor || blocklet.appName}
|
|
271
|
-
direction={InfoDirection}
|
|
272
|
-
alignItems={InfoAlignItems}
|
|
287
|
+
label={t('admin.invoice.billTo')}
|
|
288
|
+
value={<CustomerLink customer={data.customer} linked={false} size={isMobile ? 'default' : 'small'} />}
|
|
273
289
|
/>
|
|
274
|
-
|
|
290
|
+
<InfoRow label={t('admin.invoice.from')} value={data.statement_descriptor || blocklet.appName} />
|
|
275
291
|
<InfoRow
|
|
276
|
-
label={t('admin.invoice.
|
|
277
|
-
value={
|
|
278
|
-
direction={InfoDirection}
|
|
279
|
-
alignItems={InfoAlignItems}
|
|
292
|
+
label={t('admin.invoice.description')}
|
|
293
|
+
value={getInvoiceDescriptionAndReason(data, locale)?.description}
|
|
280
294
|
/>
|
|
281
295
|
{data.status_transitions?.paid_at && (
|
|
282
|
-
<InfoRow
|
|
283
|
-
label={t('admin.invoice.paidAt')}
|
|
284
|
-
value={formatTime(data.status_transitions.paid_at * 1000)}
|
|
285
|
-
direction={InfoDirection}
|
|
286
|
-
alignItems={InfoAlignItems}
|
|
287
|
-
/>
|
|
296
|
+
<InfoRow label={t('admin.invoice.paidAt')} value={formatTime(data.status_transitions.paid_at * 1000)} />
|
|
288
297
|
)}
|
|
289
298
|
<InfoRow
|
|
290
299
|
label={t('admin.paymentCurrency.name')}
|
|
@@ -294,15 +303,17 @@ export default function CustomerInvoiceDetail() {
|
|
|
294
303
|
name={`${data.paymentCurrency.symbol} (${data.paymentMethod.name})`}
|
|
295
304
|
/>
|
|
296
305
|
}
|
|
297
|
-
direction={InfoDirection}
|
|
298
|
-
alignItems={InfoAlignItems}
|
|
299
306
|
/>
|
|
307
|
+
{(isStake || isSlashStake) && (
|
|
308
|
+
<InfoRow
|
|
309
|
+
label={isSlashStake ? t('common.slashStakeAmount') : t('common.stakeAmount')}
|
|
310
|
+
value={`${formatAmount(data.total, data.paymentCurrency.decimal)} ${data.paymentCurrency.symbol}`}
|
|
311
|
+
/>
|
|
312
|
+
)}
|
|
300
313
|
{(!!data.paymentIntent?.payment_details?.ethereum || !!data.paymentIntent?.payment_details?.base) && (
|
|
301
314
|
<InfoRow
|
|
302
315
|
label={t('common.txGas')}
|
|
303
316
|
value={<TxGas details={data.paymentIntent.payment_details as any} method={data.paymentMethod} />}
|
|
304
|
-
direction={InfoDirection}
|
|
305
|
-
alignItems={InfoAlignItems}
|
|
306
317
|
/>
|
|
307
318
|
)}
|
|
308
319
|
{paymentDetails && (
|
|
@@ -311,16 +322,6 @@ export default function CustomerInvoiceDetail() {
|
|
|
311
322
|
isStake ? t('common.stakeTxHash') : t(`common.${paymentDetails?.arcblock?.type || 'transfer'}TxHash`)
|
|
312
323
|
}
|
|
313
324
|
value={<TxLink details={paymentDetails} method={data.paymentMethod} mode="customer" />}
|
|
314
|
-
direction={InfoDirection}
|
|
315
|
-
alignItems={InfoAlignItems}
|
|
316
|
-
/>
|
|
317
|
-
)}
|
|
318
|
-
{(isStake || isSlashStake) && (
|
|
319
|
-
<InfoRow
|
|
320
|
-
label={isSlashStake ? t('common.slashStakeAmount') : t('common.stakeAmount')}
|
|
321
|
-
value={`${formatAmount(data.total, data.paymentCurrency.decimal)} ${data.paymentCurrency.symbol}`}
|
|
322
|
-
direction={InfoDirection}
|
|
323
|
-
alignItems={InfoAlignItems}
|
|
324
325
|
/>
|
|
325
326
|
)}
|
|
326
327
|
{data?.relatedInvoice && (
|
|
@@ -338,11 +339,9 @@ export default function CustomerInvoiceDetail() {
|
|
|
338
339
|
{data.relatedInvoice?.number}
|
|
339
340
|
</Typography>
|
|
340
341
|
}
|
|
341
|
-
direction={InfoDirection}
|
|
342
|
-
alignItems={InfoAlignItems}
|
|
343
342
|
/>
|
|
344
343
|
)}
|
|
345
|
-
</
|
|
344
|
+
</InfoRowGroup>
|
|
346
345
|
</Box>
|
|
347
346
|
{isDonation && !isEmpty(data.paymentIntent?.beneficiaries) && (
|
|
348
347
|
<>
|
|
@@ -25,6 +25,7 @@ import SectionHeader from '../../../components/section/header';
|
|
|
25
25
|
import { getCustomerProfileUrl, goBackOrFallback } from '../../../libs/util';
|
|
26
26
|
import CustomerLink from '../../../components/customer/link';
|
|
27
27
|
import InfoCard from '../../../components/info-card';
|
|
28
|
+
import InfoRowGroup from '../../../components/info-row-group';
|
|
28
29
|
|
|
29
30
|
const fetchData = (
|
|
30
31
|
id: string
|
|
@@ -37,9 +38,6 @@ const fetchData = (
|
|
|
37
38
|
return api.get(`/api/payouts/${id}`).then((res) => res.data);
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
const InfoDirection = 'column';
|
|
41
|
-
const InfoAlignItems = 'flex-start';
|
|
42
|
-
|
|
43
41
|
export default function PayoutDetail() {
|
|
44
42
|
const { t } = useLocaleContext();
|
|
45
43
|
const { isMobile } = useMobile();
|
|
@@ -237,41 +235,37 @@ export default function PayoutDetail() {
|
|
|
237
235
|
<Box flex={1} className="payment-link-column-1" sx={{ gap: 2.5, display: 'flex', flexDirection: 'column' }}>
|
|
238
236
|
<Box className="section">
|
|
239
237
|
<SectionHeader title={t('common.detail')} />
|
|
240
|
-
<
|
|
238
|
+
<InfoRowGroup
|
|
241
239
|
sx={{
|
|
242
240
|
display: 'grid',
|
|
243
241
|
gridTemplateColumns: {
|
|
244
242
|
xs: 'repeat(1, 1fr)',
|
|
245
|
-
|
|
246
|
-
md: 'repeat(2, 1fr)',
|
|
247
|
-
lg: 'repeat(3, 1fr)',
|
|
243
|
+
lg: 'repeat(2, 1fr)',
|
|
248
244
|
},
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
245
|
+
'.info-row-wrapper': {
|
|
246
|
+
gap: 1,
|
|
247
|
+
flexDirection: {
|
|
248
|
+
xs: 'column',
|
|
249
|
+
lg: 'row',
|
|
250
|
+
},
|
|
251
|
+
alignItems: {
|
|
252
|
+
xs: 'flex-start',
|
|
253
|
+
lg: 'center',
|
|
254
|
+
},
|
|
252
255
|
},
|
|
253
256
|
}}>
|
|
254
|
-
<InfoRow
|
|
255
|
-
label={t('common.createdAt')}
|
|
256
|
-
value={formatTime(data.created_at)}
|
|
257
|
-
direction={InfoDirection}
|
|
258
|
-
alignItems={InfoAlignItems}
|
|
259
|
-
/>
|
|
257
|
+
<InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
|
|
260
258
|
{paymentIntent && paymentIntent.payment_details && (
|
|
261
259
|
<InfoRow
|
|
262
260
|
label={t('customer.payout.payTxHash')}
|
|
263
261
|
value={<TxLink details={paymentIntent.payment_details} method={data.paymentMethod} mode="customer" />}
|
|
264
|
-
direction={InfoDirection}
|
|
265
|
-
alignItems={InfoAlignItems}
|
|
266
262
|
/>
|
|
267
263
|
)}
|
|
268
264
|
<InfoRow
|
|
269
265
|
label={t('customer.payout.receiver')}
|
|
270
|
-
value={<CustomerLink customer={data.customer} linked={false} />}
|
|
271
|
-
direction={InfoDirection}
|
|
272
|
-
alignItems={InfoAlignItems}
|
|
266
|
+
value={<CustomerLink customer={data.customer} linked={false} size={isMobile ? 'default' : 'small'} />}
|
|
273
267
|
/>
|
|
274
|
-
</
|
|
268
|
+
</InfoRowGroup>
|
|
275
269
|
</Box>
|
|
276
270
|
</Box>
|
|
277
271
|
</Stack>
|
|
@@ -31,9 +31,11 @@ import type { TPaymentCurrency, TPaymentMethod } from '@blocklet/payment-types';
|
|
|
31
31
|
import { joinURL } from 'ufo';
|
|
32
32
|
import { AccountBalanceWalletOutlined, ArrowBackOutlined, ArrowForwardOutlined } from '@mui/icons-material';
|
|
33
33
|
import Empty from '@arcblock/ux/lib/Empty';
|
|
34
|
+
import { BN } from '@ocap/util';
|
|
34
35
|
import RechargeList from '../../../components/invoice/recharge';
|
|
35
36
|
import { getTokenBalanceLink, goBackOrFallback } from '../../../libs/util';
|
|
36
37
|
import { useSessionContext } from '../../../contexts/session';
|
|
38
|
+
import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
|
|
37
39
|
|
|
38
40
|
// 扩展PaymentCurrency类型以包含paymentMethod
|
|
39
41
|
interface ExtendedPaymentCurrency extends TPaymentCurrency {
|
|
@@ -81,12 +83,21 @@ export default function BalanceRechargePage() {
|
|
|
81
83
|
const [loading, setLoading] = useState(true);
|
|
82
84
|
const [error, setError] = useState('');
|
|
83
85
|
const customInputRef = useRef<HTMLInputElement>(null);
|
|
86
|
+
const [unitCycle, setUnitCycle] = useState<{
|
|
87
|
+
amount: string;
|
|
88
|
+
interval: TimeUnit;
|
|
89
|
+
cycle: number;
|
|
90
|
+
}>({
|
|
91
|
+
amount: '0',
|
|
92
|
+
interval: 'month',
|
|
93
|
+
cycle: 1,
|
|
94
|
+
});
|
|
84
95
|
const [payerValue, setPayerValue] = useState<{
|
|
85
96
|
paymentAddress: string;
|
|
86
97
|
token: string;
|
|
87
98
|
} | null>(null);
|
|
88
99
|
const [customAmount, setCustomAmount] = useState(false);
|
|
89
|
-
const [presetAmounts] = useState<Array<{ amount: string
|
|
100
|
+
const [presetAmounts, setPresetAmounts] = useState<Array<{ amount: string; multiplier: number; label: string }>>([]);
|
|
90
101
|
const { session } = useSessionContext();
|
|
91
102
|
const [currency, setCurrency] = useState<ExtendedPaymentCurrency | null>(null);
|
|
92
103
|
const [relatedSubscriptions, setRelatedSubscriptions] = useState<Subscription[]>([]);
|
|
@@ -105,6 +116,62 @@ export default function BalanceRechargePage() {
|
|
|
105
116
|
if (data.currency) {
|
|
106
117
|
setCurrency(data.currency);
|
|
107
118
|
setRelatedSubscriptions(data.relatedSubscriptions || []);
|
|
119
|
+
|
|
120
|
+
if (data.recommendedRecharge && data.recommendedRecharge.amount && data.recommendedRecharge.amount !== '0') {
|
|
121
|
+
const baseAmount = data.recommendedRecharge.amount;
|
|
122
|
+
const decimal = data.currency.decimal || 0;
|
|
123
|
+
setUnitCycle({
|
|
124
|
+
amount: parseFloat(formatBNStr(baseAmount, decimal, 6, true)).toString(),
|
|
125
|
+
interval: data.recommendedRecharge.interval as TimeUnit,
|
|
126
|
+
cycle: data.recommendedRecharge.cycle,
|
|
127
|
+
});
|
|
128
|
+
setPresetAmounts([
|
|
129
|
+
{
|
|
130
|
+
amount: Math.ceil(parseFloat(formatBNStr(baseAmount, decimal, 6, true))).toString(),
|
|
131
|
+
multiplier: data.recommendedRecharge.cycle,
|
|
132
|
+
label: t('common.estimatedDuration', {
|
|
133
|
+
duration: formatSmartDuration(1, data.recommendedRecharge.interval as TimeUnit, {
|
|
134
|
+
t,
|
|
135
|
+
}),
|
|
136
|
+
}),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
amount: Math.ceil(
|
|
140
|
+
parseFloat(formatBNStr(new BN(baseAmount).mul(new BN('4')).toString(), decimal, 6, true))
|
|
141
|
+
).toString(),
|
|
142
|
+
multiplier: data.recommendedRecharge.cycle * 4,
|
|
143
|
+
label: t('common.estimatedDuration', {
|
|
144
|
+
duration: formatSmartDuration(4, data.recommendedRecharge.interval as TimeUnit, {
|
|
145
|
+
t,
|
|
146
|
+
}),
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
amount: Math.ceil(
|
|
151
|
+
parseFloat(formatBNStr(new BN(baseAmount).mul(new BN('8')).toString(), decimal, 6, true))
|
|
152
|
+
).toString(),
|
|
153
|
+
multiplier: data.recommendedRecharge.cycle * 8,
|
|
154
|
+
label: t('common.estimatedDuration', {
|
|
155
|
+
duration: formatSmartDuration(8, data.recommendedRecharge.interval as TimeUnit, {
|
|
156
|
+
t,
|
|
157
|
+
}),
|
|
158
|
+
}),
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
setAmount(
|
|
163
|
+
Math.ceil(
|
|
164
|
+
parseFloat(formatBNStr(new BN(baseAmount).mul(new BN('4')).toString(), decimal, 6, true))
|
|
165
|
+
).toString()
|
|
166
|
+
);
|
|
167
|
+
} else {
|
|
168
|
+
setPresetAmounts([
|
|
169
|
+
{ amount: '10', multiplier: 0, label: '' },
|
|
170
|
+
{ amount: '50', multiplier: 0, label: '' },
|
|
171
|
+
{ amount: '100', multiplier: 0, label: '' },
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
174
|
+
|
|
108
175
|
const supportRecharge = data.currency?.paymentMethod?.type === 'arcblock';
|
|
109
176
|
if (supportRecharge) {
|
|
110
177
|
const payerTokenRes = await api.get(`/api/customers/payer-token?currencyId=${currencyId}`);
|
|
@@ -261,7 +328,7 @@ export default function BalanceRechargePage() {
|
|
|
261
328
|
|
|
262
329
|
{currency && (
|
|
263
330
|
<Box ref={rechargeRef}>
|
|
264
|
-
<Stack sx={{ maxWidth: '
|
|
331
|
+
<Stack sx={{ maxWidth: '760px' }}>
|
|
265
332
|
<Box sx={{ mb: 4 }}>
|
|
266
333
|
<BalanceCard elevation={0}>
|
|
267
334
|
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
|
|
@@ -341,7 +408,7 @@ export default function BalanceRechargePage() {
|
|
|
341
408
|
|
|
342
409
|
<Paper elevation={0} sx={{ mb: 3, backgroundColor: 'background.default', borderRadius: '16px' }}>
|
|
343
410
|
<Grid container spacing={2}>
|
|
344
|
-
{presetAmounts.map(({ amount: presetAmount }) => (
|
|
411
|
+
{presetAmounts.map(({ amount: presetAmount, label, multiplier }) => (
|
|
345
412
|
<Grid item xs={6} sm={3} key={presetAmount}>
|
|
346
413
|
<Card
|
|
347
414
|
variant="outlined"
|
|
@@ -365,7 +432,9 @@ export default function BalanceRechargePage() {
|
|
|
365
432
|
}
|
|
366
433
|
: {}),
|
|
367
434
|
}}>
|
|
368
|
-
<CardActionArea
|
|
435
|
+
<CardActionArea
|
|
436
|
+
onClick={() => handleSelect(presetAmount)}
|
|
437
|
+
sx={{ height: '100%', p: 1.5, textAlign: 'center' }}>
|
|
369
438
|
<Typography
|
|
370
439
|
variant="h6"
|
|
371
440
|
align="center"
|
|
@@ -375,6 +444,11 @@ export default function BalanceRechargePage() {
|
|
|
375
444
|
}}>
|
|
376
445
|
{presetAmount} {currency.symbol}
|
|
377
446
|
</Typography>
|
|
447
|
+
{multiplier > 0 && label && (
|
|
448
|
+
<Typography variant="caption" align="center" color="text.secondary">
|
|
449
|
+
{label}
|
|
450
|
+
</Typography>
|
|
451
|
+
)}
|
|
378
452
|
</CardActionArea>
|
|
379
453
|
</Card>
|
|
380
454
|
</Grid>
|
|
@@ -434,6 +508,19 @@ export default function BalanceRechargePage() {
|
|
|
434
508
|
}}
|
|
435
509
|
inputRef={customInputRef}
|
|
436
510
|
/>
|
|
511
|
+
{amount && Number(amount) > 0 && Number(unitCycle.amount) > 0 && !amountError && (
|
|
512
|
+
<Typography variant="body2" sx={{ color: 'text.lighter', mt: 1 }} fontSize={12}>
|
|
513
|
+
{t('common.estimatedDuration', {
|
|
514
|
+
duration: formatSmartDuration(
|
|
515
|
+
Math.floor(Number(amount) / Number(unitCycle.amount)) < 1
|
|
516
|
+
? parseFloat((Number(amount) / Number(unitCycle.amount)).toFixed(1))
|
|
517
|
+
: Math.floor(Number(amount) / Number(unitCycle.amount)),
|
|
518
|
+
unitCycle.interval,
|
|
519
|
+
{ t }
|
|
520
|
+
),
|
|
521
|
+
})}
|
|
522
|
+
</Typography>
|
|
523
|
+
)}
|
|
437
524
|
</Box>
|
|
438
525
|
)}
|
|
439
526
|
|
|
@@ -464,39 +551,37 @@ export default function BalanceRechargePage() {
|
|
|
464
551
|
{t('customer.recharge.relatedSubscriptions')}
|
|
465
552
|
</Typography>
|
|
466
553
|
|
|
467
|
-
<
|
|
554
|
+
<Grid container spacing={2}>
|
|
468
555
|
{relatedSubscriptions.map((subscription) => (
|
|
469
|
-
<
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
{subscription.description || subscription.id}
|
|
489
|
-
</Typography>
|
|
490
|
-
</Stack>
|
|
556
|
+
<Grid item xs={12} sm={6} md={4} lg={3} key={subscription.id}>
|
|
557
|
+
<Stack
|
|
558
|
+
onClick={() => handleSubscriptionClick(subscription.id)}
|
|
559
|
+
className="base-card"
|
|
560
|
+
sx={{ height: '100%' }}>
|
|
561
|
+
<Stack direction="row" alignItems="center" spacing={1.5} sx={{ mb: 1 }}>
|
|
562
|
+
<Typography
|
|
563
|
+
variant="subtitle1"
|
|
564
|
+
sx={{
|
|
565
|
+
fontWeight: 'medium',
|
|
566
|
+
overflow: 'hidden',
|
|
567
|
+
textOverflow: 'ellipsis',
|
|
568
|
+
whiteSpace: 'nowrap',
|
|
569
|
+
color: 'text.link',
|
|
570
|
+
cursor: 'pointer',
|
|
571
|
+
}}>
|
|
572
|
+
{subscription.description || subscription.id}
|
|
573
|
+
</Typography>
|
|
574
|
+
</Stack>
|
|
491
575
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
576
|
+
{subscription.items && subscription.items[0] && currency && (
|
|
577
|
+
<Typography variant="body1" sx={{ color: 'text.secondary' }}>
|
|
578
|
+
{formatPrice(subscription.items[0].price, currency)}
|
|
579
|
+
</Typography>
|
|
580
|
+
)}
|
|
581
|
+
</Stack>
|
|
582
|
+
</Grid>
|
|
498
583
|
))}
|
|
499
|
-
</
|
|
584
|
+
</Grid>
|
|
500
585
|
</Box>
|
|
501
586
|
)}
|
|
502
587
|
<Divider sx={{ mb: 3 }} />
|
|
@@ -294,6 +294,12 @@ export default function RechargePage() {
|
|
|
294
294
|
display: 'grid',
|
|
295
295
|
gridTemplateColumns: { xs: 'repeat(1, 1fr)', sm: 'repeat(1, 1fr)', md: 'repeat(1, 1fr)' },
|
|
296
296
|
gap: { xs: 0, md: 2 },
|
|
297
|
+
'.currency-name': {
|
|
298
|
+
color: 'text.secondary',
|
|
299
|
+
},
|
|
300
|
+
'.info-row-label': {
|
|
301
|
+
fontWeight: 500,
|
|
302
|
+
},
|
|
297
303
|
}}>
|
|
298
304
|
<InfoRow
|
|
299
305
|
label={t('common.customer')}
|
|
@@ -473,7 +479,11 @@ export default function RechargePage() {
|
|
|
473
479
|
/>
|
|
474
480
|
{amount && Number(amount) > 0 && Number(cycleAmount) > 0 && !amountError && (
|
|
475
481
|
<Typography variant="body2" sx={{ color: 'text.lighter', mt: '8px !important' }} fontSize={12}>
|
|
476
|
-
{formatEstimatedDuration(
|
|
482
|
+
{formatEstimatedDuration(
|
|
483
|
+
Math.floor(Number(amount) / Number(cycleAmount)) < 1
|
|
484
|
+
? parseFloat((Number(amount) / Number(cycleAmount)).toFixed(1))
|
|
485
|
+
: Math.floor(Number(amount) / Number(cycleAmount))
|
|
486
|
+
)}
|
|
477
487
|
</Typography>
|
|
478
488
|
)}
|
|
479
489
|
</Box>
|