payment-kit 1.14.21 → 1.14.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blocklet.yml +1 -1
- package/package.json +19 -19
- package/src/app.tsx +13 -12
- package/src/components/balance-list.tsx +12 -2
- package/src/components/copyable.tsx +3 -2
- package/src/components/customer/edit.tsx +25 -21
- package/src/components/customer/form.tsx +18 -28
- package/src/components/customer/link.tsx +1 -2
- package/src/components/date-range-picker.tsx +21 -0
- package/src/components/drawer-form.tsx +27 -4
- package/src/components/event/list.tsx +3 -2
- package/src/components/filter-toolbar.tsx +11 -4
- package/src/components/info-card.tsx +4 -2
- package/src/components/info-metric.tsx +2 -2
- package/src/components/info-row.tsx +33 -26
- package/src/components/invoice/list.tsx +2 -2
- package/src/components/invoice/table.tsx +148 -85
- package/src/components/invoice-pdf/pdf.tsx +5 -1
- package/src/components/layout/admin.tsx +8 -2
- package/src/components/metadata/editor.tsx +25 -18
- package/src/components/metadata/form.tsx +83 -25
- package/src/components/metadata/list.tsx +22 -6
- package/src/components/payment-intent/list.tsx +3 -3
- package/src/components/payment-link/preview.tsx +42 -24
- package/src/components/payouts/list.tsx +2 -3
- package/src/components/price/form.tsx +27 -12
- package/src/components/price/upsell.tsx +1 -4
- package/src/components/pricing-table/preview.tsx +42 -23
- package/src/components/product/cross-sell-select.tsx +1 -1
- package/src/components/product/cross-sell.tsx +3 -4
- package/src/components/product/edit-price.tsx +0 -1
- package/src/components/refund/list.tsx +9 -4
- package/src/components/section/header.tsx +11 -4
- package/src/components/subscription/description.tsx +10 -6
- package/src/components/subscription/items/index.tsx +28 -6
- package/src/components/subscription/list.tsx +2 -2
- package/src/components/subscription/metrics.tsx +10 -8
- package/src/components/subscription/portal/actions.tsx +37 -11
- package/src/components/subscription/portal/list.tsx +131 -70
- package/src/components/subscription/status.tsx +9 -3
- package/src/global.css +1 -1
- package/src/hooks/mobile.ts +3 -3
- package/src/libs/util.ts +6 -2
- package/src/locales/en.tsx +37 -1
- package/src/locales/zh.tsx +37 -1
- package/src/pages/admin/billing/index.tsx +24 -4
- package/src/pages/admin/billing/invoices/detail.tsx +302 -147
- package/src/pages/admin/billing/subscriptions/detail.tsx +259 -134
- package/src/pages/admin/customers/customers/detail.tsx +358 -175
- package/src/pages/admin/customers/customers/index.tsx +8 -5
- package/src/pages/admin/customers/index.tsx +22 -5
- package/src/pages/admin/developers/webhooks/index.tsx +2 -2
- package/src/pages/admin/index.tsx +24 -10
- package/src/pages/admin/overview.tsx +1 -1
- package/src/pages/admin/payments/index.tsx +22 -7
- package/src/pages/admin/payments/intents/detail.tsx +263 -121
- package/src/pages/admin/payments/payouts/detail.tsx +235 -102
- package/src/pages/admin/payments/refunds/detail.tsx +286 -133
- package/src/pages/admin/products/index.tsx +28 -12
- package/src/pages/admin/products/links/create.tsx +16 -6
- package/src/pages/admin/products/links/detail.tsx +280 -176
- package/src/pages/admin/products/links/index.tsx +4 -7
- package/src/pages/admin/products/passports/index.tsx +6 -3
- package/src/pages/admin/products/prices/detail.tsx +260 -139
- package/src/pages/admin/products/prices/list.tsx +7 -3
- package/src/pages/admin/products/pricing-tables/create.tsx +17 -5
- package/src/pages/admin/products/pricing-tables/detail.tsx +221 -121
- package/src/pages/admin/products/pricing-tables/index.tsx +1 -2
- package/src/pages/admin/products/products/detail.tsx +262 -119
- package/src/pages/admin/products/products/index.tsx +1 -2
- package/src/pages/admin/settings/index.tsx +27 -7
- package/src/pages/customer/index.tsx +431 -143
- package/src/pages/customer/invoice/detail.tsx +138 -26
- package/src/pages/customer/refund/list.tsx +193 -4
- package/src/pages/customer/subscription/change-payment.tsx +20 -20
- package/src/pages/customer/subscription/detail.tsx +168 -34
- package/src/pages/customer/subscription/embed.tsx +22 -19
- package/src/pages/home.tsx +7 -1
- package/src/components/table.tsx +0 -93
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
-
import { Status, api, formatBNStr, formatTime, getPaymentIntentStatusColor } from '@blocklet/payment-react';
|
|
3
|
+
import { Status, api, formatBNStr, formatTime, getPaymentIntentStatusColor, Table } from '@blocklet/payment-react';
|
|
4
4
|
import type { TPaymentIntentExpanded } from '@blocklet/payment-types';
|
|
5
5
|
import { CircularProgress, Typography } from '@mui/material';
|
|
6
6
|
import { useLocalStorageState } from 'ahooks';
|
|
@@ -10,7 +10,6 @@ import { Link } from 'react-router-dom';
|
|
|
10
10
|
import { debounce } from '../../libs/util';
|
|
11
11
|
import CustomerLink from '../customer/link';
|
|
12
12
|
import FilterToolbar from '../filter-toolbar';
|
|
13
|
-
import Table from '../table';
|
|
14
13
|
import PaymentIntentActions from './actions';
|
|
15
14
|
|
|
16
15
|
const fetchData = (params: Record<string, any> = {}): Promise<{ list: TPaymentIntentExpanded[]; count: number }> => {
|
|
@@ -105,7 +104,7 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
105
104
|
label: t('common.amount'),
|
|
106
105
|
name: 'id',
|
|
107
106
|
align: 'right',
|
|
108
|
-
width:
|
|
107
|
+
width: 80,
|
|
109
108
|
options: {
|
|
110
109
|
customBodyRenderLite: (_: string, index: number) => {
|
|
111
110
|
const item = data.list[index] as TPaymentIntentExpanded;
|
|
@@ -270,6 +269,7 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
270
269
|
/>
|
|
271
270
|
)
|
|
272
271
|
}
|
|
272
|
+
emptyNodeText={t('empty.payments')}
|
|
273
273
|
/>
|
|
274
274
|
);
|
|
275
275
|
}
|
|
@@ -1,31 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { useSize } from 'ahooks';
|
|
1
|
+
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
|
2
|
+
import { useFullscreen, useSize } from 'ahooks';
|
|
4
3
|
import IframeResizer from 'iframe-resizer-react';
|
|
5
|
-
import { useRef } from 'react';
|
|
6
|
-
|
|
7
4
|
import Chrome from './chrome';
|
|
8
5
|
|
|
6
|
+
const PaymentLinkPreview = forwardRef(({ id, version = 1 }: { id: string; version?: number }, ref) => {
|
|
7
|
+
const innerRef = useRef(null);
|
|
8
|
+
const size = useSize(innerRef);
|
|
9
|
+
const scale = (size?.width || 0) / 1280;
|
|
10
|
+
const [fullscreen, { toggleFullscreen }] = useFullscreen(innerRef);
|
|
11
|
+
|
|
12
|
+
useImperativeHandle(ref, () => ({
|
|
13
|
+
ref: innerRef.current,
|
|
14
|
+
fullscreen,
|
|
15
|
+
toggleFullscreen,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div ref={innerRef}>
|
|
20
|
+
{fullscreen ? (
|
|
21
|
+
<div style={{ width: '100%', height: '100%', background: '#fff' }}>
|
|
22
|
+
<IframeResizer
|
|
23
|
+
style={{ width: '100%', height: '100vh', overflow: 'hidden', border: 'none' }}
|
|
24
|
+
src={`${window.blocklet.prefix}checkout/pay/${id}?preview=1&version=${version}`}
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
) : (
|
|
28
|
+
<Chrome sx={{ width: '100%', height: 800 * scale }}>
|
|
29
|
+
<IframeResizer
|
|
30
|
+
style={{
|
|
31
|
+
width: '1280px',
|
|
32
|
+
height: '800px',
|
|
33
|
+
transform: `scale(${scale})`,
|
|
34
|
+
transformOrigin: 'top left',
|
|
35
|
+
border: 'none',
|
|
36
|
+
}}
|
|
37
|
+
src={`${window.blocklet.prefix}checkout/pay/${id}?preview=1&version=${version}`}
|
|
38
|
+
/>
|
|
39
|
+
</Chrome>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
9
45
|
PaymentLinkPreview.defaultProps = {
|
|
10
46
|
version: 1,
|
|
11
47
|
};
|
|
12
48
|
|
|
13
|
-
export default
|
|
14
|
-
const ref = useRef(null);
|
|
15
|
-
const size = useSize(ref);
|
|
16
|
-
return (
|
|
17
|
-
<Chrome sx={{ width: '100%', maxWidth: 840 }}>
|
|
18
|
-
<Box ref={ref}> </Box>
|
|
19
|
-
<IframeResizer
|
|
20
|
-
style={{
|
|
21
|
-
minWidth: size ? size.width / 0.8 : 1050,
|
|
22
|
-
minHeight: '64vh',
|
|
23
|
-
transform: 'scale(0.8)',
|
|
24
|
-
transformOrigin: 'top left',
|
|
25
|
-
border: 'none',
|
|
26
|
-
}}
|
|
27
|
-
src={`${window.blocklet.prefix}checkout/pay/${id}?preview=1&version=${version}`}
|
|
28
|
-
/>
|
|
29
|
-
</Chrome>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
49
|
+
export default PaymentLinkPreview;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
-
import { Status, api, formatBNStr, formatTime, getPayoutStatusColor } from '@blocklet/payment-react';
|
|
3
|
+
import { Status, api, formatBNStr, formatTime, getPayoutStatusColor, Table } from '@blocklet/payment-react';
|
|
4
4
|
import type { TPayoutExpanded } from '@blocklet/payment-types';
|
|
5
5
|
import { CircularProgress, Typography } from '@mui/material';
|
|
6
6
|
import { useLocalStorageState } from 'ahooks';
|
|
@@ -10,7 +10,6 @@ import { Link } from 'react-router-dom';
|
|
|
10
10
|
import { debounce } from '../../libs/util';
|
|
11
11
|
import CustomerLink from '../customer/link';
|
|
12
12
|
import FilterToolbar from '../filter-toolbar';
|
|
13
|
-
import Table from '../table';
|
|
14
13
|
import PayoutActions from './actions';
|
|
15
14
|
|
|
16
15
|
const fetchData = (params: Record<string, any> = {}): Promise<{ list: TPayoutExpanded[]; count: number }> => {
|
|
@@ -103,7 +102,7 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
|
|
|
103
102
|
label: t('common.amount'),
|
|
104
103
|
name: 'id',
|
|
105
104
|
align: 'right',
|
|
106
|
-
width:
|
|
105
|
+
width: 80,
|
|
107
106
|
options: {
|
|
108
107
|
customBodyRenderLite: (_: string, index: number) => {
|
|
109
108
|
const item = data.list[index] as TPayoutExpanded;
|
|
@@ -30,6 +30,7 @@ import { styled } from '@mui/system';
|
|
|
30
30
|
import { Controller, useFieldArray, useFormContext, useWatch } from 'react-hook-form';
|
|
31
31
|
import type { LiteralUnion } from 'type-fest';
|
|
32
32
|
|
|
33
|
+
import { get } from 'lodash';
|
|
33
34
|
import Collapse from '../collapse';
|
|
34
35
|
import CurrencySelect from './currency-select';
|
|
35
36
|
|
|
@@ -109,7 +110,6 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
109
110
|
return names.reduce((prev, curr) => prev?.[curr], errors as any);
|
|
110
111
|
};
|
|
111
112
|
const { settings, livemode } = usePaymentContext();
|
|
112
|
-
|
|
113
113
|
const currencies = useFieldArray({ control, name: getFieldName('currency_options') });
|
|
114
114
|
|
|
115
115
|
const priceLocked = useWatch({ control, name: getFieldName('locked') });
|
|
@@ -117,6 +117,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
117
117
|
const isMetered = useWatch({ control, name: getFieldName('recurring.usage_type') }) === 'metered';
|
|
118
118
|
const isCustomInterval = useWatch({ control, name: getFieldName('recurring.interval_config') }) === 'month_2';
|
|
119
119
|
const model = useWatch({ control, name: getFieldName('model') });
|
|
120
|
+
const intervalSelectValue = useWatch({ control, name: getFieldName('recurring.interval') });
|
|
120
121
|
const quantityPositive = (v: number | undefined) => !v || v.toString().match(/^(0|[1-9]\d*)$/);
|
|
121
122
|
const intervalCountPositive = (v: number) => Number.isInteger(Number(v)) && v > 0;
|
|
122
123
|
|
|
@@ -147,7 +148,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
147
148
|
disabled={isLocked}
|
|
148
149
|
render={({ field }) => (
|
|
149
150
|
<Box sx={{ width: INPUT_WIDTH }}>
|
|
150
|
-
<FormLabel>{t('admin.price.model')}</FormLabel>
|
|
151
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.model')}</FormLabel>
|
|
151
152
|
<Select {...field} fullWidth size="small">
|
|
152
153
|
<MenuItem value="standard">{t('admin.price.models.standard')}</MenuItem>
|
|
153
154
|
<MenuItem value="package" disabled>
|
|
@@ -177,7 +178,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
177
178
|
disabled={isLocked}
|
|
178
179
|
render={({ field }) => (
|
|
179
180
|
<Box>
|
|
180
|
-
<FormLabel>
|
|
181
|
+
<FormLabel sx={{ color: 'text.primary' }}>
|
|
181
182
|
<Stack direction="row" alignItems="center" spacing={0.5}>
|
|
182
183
|
<Typography component="span" color="text.primary">
|
|
183
184
|
{t('admin.price.amount')}
|
|
@@ -325,9 +326,10 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
325
326
|
disabled={isLocked}
|
|
326
327
|
render={({ field }) => (
|
|
327
328
|
<Box>
|
|
328
|
-
<FormLabel>{t('admin.price.recurring.interval')}</FormLabel>
|
|
329
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.recurring.interval')}</FormLabel>
|
|
329
330
|
<Select
|
|
330
331
|
{...field}
|
|
332
|
+
value={field.value || 'month_1'}
|
|
331
333
|
onChange={(e) => {
|
|
332
334
|
const [interval, count] = e.target.value.split('_');
|
|
333
335
|
setValue(getFieldName('recurring.interval'), interval);
|
|
@@ -353,6 +355,14 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
353
355
|
name={getFieldName('recurring.interval_count')}
|
|
354
356
|
control={control}
|
|
355
357
|
disabled={isLocked}
|
|
358
|
+
rules={{
|
|
359
|
+
validate: (v) => {
|
|
360
|
+
if (!intervalCountPositive(v)) {
|
|
361
|
+
return t('admin.price.recurring.intervalCountTip');
|
|
362
|
+
}
|
|
363
|
+
return true;
|
|
364
|
+
},
|
|
365
|
+
}}
|
|
356
366
|
render={({ field }) => (
|
|
357
367
|
<Box ml={2}>
|
|
358
368
|
<FormLabel> </FormLabel>
|
|
@@ -361,13 +371,16 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
361
371
|
type="number"
|
|
362
372
|
size="small"
|
|
363
373
|
sx={{ width: INPUT_WIDTH }}
|
|
364
|
-
error={
|
|
365
|
-
helperText={
|
|
374
|
+
error={!!get(errors, getFieldName('recurring.interval_count'))}
|
|
375
|
+
helperText={get(errors, getFieldName('recurring.interval_count'))?.message as string}
|
|
366
376
|
InputProps={{
|
|
367
377
|
startAdornment: <InputAdornment position="start">{t('common.every')}</InputAdornment>,
|
|
368
378
|
endAdornment: (
|
|
369
379
|
<InputAdornment position="end">
|
|
370
|
-
<select
|
|
380
|
+
<select
|
|
381
|
+
onChange={(e) => setValue(getFieldName('recurring.interval'), e.target.value)}
|
|
382
|
+
value={intervalSelectValue}
|
|
383
|
+
style={{ background: 'none', outline: 'none' }}>
|
|
371
384
|
<option value="day">{t('common.days')}</option>
|
|
372
385
|
<option value="week">{t('common.weeks')}</option>
|
|
373
386
|
<option value="month">{t('common.months')}</option>
|
|
@@ -416,7 +429,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
416
429
|
disabled={isLocked}
|
|
417
430
|
render={({ field }) => (
|
|
418
431
|
<Box>
|
|
419
|
-
<FormLabel>{t('admin.price.recurring.aggregate')}</FormLabel>
|
|
432
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.recurring.aggregate')}</FormLabel>
|
|
420
433
|
<Select {...field} sx={{ width: INPUT_WIDTH }} size="small">
|
|
421
434
|
<MenuItem value="sum">{t('admin.price.aggregate.sum')}</MenuItem>
|
|
422
435
|
<MenuItem value="max">{t('admin.price.aggregate.max')}</MenuItem>
|
|
@@ -435,7 +448,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
435
448
|
control={control}
|
|
436
449
|
render={({ field }) => (
|
|
437
450
|
<Box>
|
|
438
|
-
<FormLabel>{t('admin.price.quantityAvailable.label')}</FormLabel>
|
|
451
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.quantityAvailable.label')}</FormLabel>
|
|
439
452
|
<TextField
|
|
440
453
|
{...field}
|
|
441
454
|
size="small"
|
|
@@ -453,7 +466,9 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
453
466
|
control={control}
|
|
454
467
|
render={({ field }) => (
|
|
455
468
|
<Box>
|
|
456
|
-
<FormLabel
|
|
469
|
+
<FormLabel sx={{ color: 'text.primary' }}>
|
|
470
|
+
{t('admin.price.quantityLimitPerCheckout.label')}
|
|
471
|
+
</FormLabel>
|
|
457
472
|
<TextField
|
|
458
473
|
{...field}
|
|
459
474
|
size="small"
|
|
@@ -471,7 +486,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
471
486
|
control={control}
|
|
472
487
|
render={({ field }) => (
|
|
473
488
|
<Box>
|
|
474
|
-
<FormLabel>{t('admin.price.nickname.label')}</FormLabel>
|
|
489
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.nickname.label')}</FormLabel>
|
|
475
490
|
<TextField {...field} size="small" sx={{ width: INPUT_WIDTH }} />
|
|
476
491
|
</Box>
|
|
477
492
|
)}
|
|
@@ -481,7 +496,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
481
496
|
control={control}
|
|
482
497
|
render={({ field }) => (
|
|
483
498
|
<Box>
|
|
484
|
-
<FormLabel>{t('admin.price.lookup_key.label')}</FormLabel>
|
|
499
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.lookup_key.label')}</FormLabel>
|
|
485
500
|
<TextField {...field} size="small" sx={{ width: INPUT_WIDTH }} />
|
|
486
501
|
</Box>
|
|
487
502
|
)}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
1
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
3
2
|
import { api, formatError, formatPrice, usePaymentContext } from '@blocklet/payment-react';
|
|
4
3
|
import type { TPriceExpanded } from '@blocklet/payment-types';
|
|
@@ -60,12 +59,10 @@ export function UpsellForm({ data, onChange }: { data: TPriceExpanded; onChange:
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
export default function PriceUpsell({ data, onChange }: { data: TPriceExpanded; onChange: Function }) {
|
|
63
|
-
const { t } = useLocaleContext();
|
|
64
|
-
|
|
65
62
|
return (
|
|
66
63
|
<Grid container>
|
|
67
64
|
<Grid item xs={12} md={6}>
|
|
68
|
-
<InfoRow label=
|
|
65
|
+
<InfoRow label="" value={<UpsellForm data={data} onChange={onChange} />} />
|
|
69
66
|
</Grid>
|
|
70
67
|
</Grid>
|
|
71
68
|
);
|
|
@@ -1,30 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useSize } from 'ahooks';
|
|
1
|
+
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
|
2
|
+
import { useFullscreen, useSize } from 'ahooks';
|
|
3
3
|
import IframeResizer from 'iframe-resizer-react';
|
|
4
|
-
import { useRef } from 'react';
|
|
5
|
-
|
|
6
4
|
import Chrome from '../payment-link/chrome';
|
|
7
5
|
|
|
6
|
+
const PricingTablePreview = forwardRef(({ id, version = 1 }: { id: string; version?: number }, ref) => {
|
|
7
|
+
const innerRef = useRef(null);
|
|
8
|
+
const size = useSize(innerRef);
|
|
9
|
+
const scale = (size?.width || 0) / 1280;
|
|
10
|
+
const [fullscreen, { toggleFullscreen }] = useFullscreen(innerRef);
|
|
11
|
+
|
|
12
|
+
useImperativeHandle(ref, () => ({
|
|
13
|
+
ref: innerRef.current,
|
|
14
|
+
fullscreen,
|
|
15
|
+
toggleFullscreen,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div ref={innerRef}>
|
|
20
|
+
{fullscreen ? (
|
|
21
|
+
<div style={{ width: '100%', height: '100%', background: '#fff', border: 'none' }}>
|
|
22
|
+
<IframeResizer
|
|
23
|
+
style={{ width: '100%', height: '100vh', overflow: 'hidden' }}
|
|
24
|
+
src={`${window.blocklet.prefix}checkout/pricing-table/${id}?preview=1&version=${version}`}
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
) : (
|
|
28
|
+
<Chrome sx={{ width: '100%', height: 800 * scale }}>
|
|
29
|
+
<IframeResizer
|
|
30
|
+
style={{
|
|
31
|
+
width: '1280px',
|
|
32
|
+
height: '800px',
|
|
33
|
+
transform: `scale(${scale})`,
|
|
34
|
+
transformOrigin: 'top left',
|
|
35
|
+
border: 'none',
|
|
36
|
+
}}
|
|
37
|
+
src={`${window.blocklet.prefix}checkout/pricing-table/${id}?preview=1&version=${version}`}
|
|
38
|
+
/>
|
|
39
|
+
</Chrome>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
8
45
|
PricingTablePreview.defaultProps = {
|
|
9
46
|
version: 1,
|
|
10
47
|
};
|
|
11
48
|
|
|
12
|
-
export default
|
|
13
|
-
const ref = useRef(null);
|
|
14
|
-
const size = useSize(ref);
|
|
15
|
-
return (
|
|
16
|
-
<Chrome sx={{ width: '100%', maxWidth: 840 }}>
|
|
17
|
-
<Box ref={ref}> </Box>
|
|
18
|
-
<IframeResizer
|
|
19
|
-
style={{
|
|
20
|
-
minWidth: size ? size.width / 0.8 : 1050,
|
|
21
|
-
minHeight: '64vh',
|
|
22
|
-
transform: 'scale(0.8)',
|
|
23
|
-
transformOrigin: 'top left',
|
|
24
|
-
border: 'none',
|
|
25
|
-
}}
|
|
26
|
-
src={`${window.blocklet.prefix}checkout/pricing-table/${id}?preview=1&version=${version}`}
|
|
27
|
-
/>
|
|
28
|
-
</Chrome>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
49
|
+
export default PricingTablePreview;
|
|
@@ -9,7 +9,6 @@ import { useState } from 'react';
|
|
|
9
9
|
import { ProductsProvider, useProductsContext } from '../../contexts/products';
|
|
10
10
|
import { formatProductPrice, isProductCurrenciesMatched } from '../../libs/util';
|
|
11
11
|
import InfoCard from '../info-card';
|
|
12
|
-
import InfoRow from '../info-row';
|
|
13
12
|
import CrossSellSelect from './cross-sell-select';
|
|
14
13
|
|
|
15
14
|
export function CrossSellForm({ data, onChange }: { data: TProductExpanded; onChange: Function }) {
|
|
@@ -84,13 +83,13 @@ export function CrossSellForm({ data, onChange }: { data: TProductExpanded; onCh
|
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
export default function ProductCrossSell({ data, onChange }: { data: TProductExpanded; onChange: Function }) {
|
|
87
|
-
const { t } = useLocaleContext();
|
|
86
|
+
// const { t } = useLocaleContext();
|
|
88
87
|
|
|
89
88
|
return (
|
|
90
89
|
<ProductsProvider>
|
|
91
90
|
<Grid container>
|
|
92
|
-
<Grid item xs={12}
|
|
93
|
-
<
|
|
91
|
+
<Grid item xs={12}>
|
|
92
|
+
<CrossSellForm data={data} onChange={onChange} />
|
|
94
93
|
</Grid>
|
|
95
94
|
</Grid>
|
|
96
95
|
</ProductsProvider>
|
|
@@ -37,7 +37,6 @@ export default function EditPrice({
|
|
|
37
37
|
recurring: price.recurring
|
|
38
38
|
? {
|
|
39
39
|
...price.recurring,
|
|
40
|
-
interval_config: [price.recurring.interval, price.recurring.interval_count].join('_'),
|
|
41
40
|
}
|
|
42
41
|
: DEFAULT_PRICE.recurring,
|
|
43
42
|
currency_options: cloneDeep(price.currency_options).map((x: any) => {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
-
import { Status, api, formatBNStr, formatTime, getPaymentIntentStatusColor } from '@blocklet/payment-react';
|
|
3
|
+
import { Status, api, formatBNStr, formatTime, getPaymentIntentStatusColor, Table } from '@blocklet/payment-react';
|
|
4
4
|
import type { TRefundExpanded } from '@blocklet/payment-types';
|
|
5
5
|
import { CircularProgress, Typography } from '@mui/material';
|
|
6
6
|
import { useLocalStorageState } from 'ahooks';
|
|
7
7
|
import { useEffect, useState } from 'react';
|
|
8
8
|
import { Link } from 'react-router-dom';
|
|
9
9
|
|
|
10
|
+
import { capitalize, toLower } from 'lodash';
|
|
10
11
|
import CustomerLink from '../customer/link';
|
|
11
12
|
import FilterToolbar from '../filter-toolbar';
|
|
12
|
-
import Table from '../table';
|
|
13
13
|
import RefundActions from './actions';
|
|
14
14
|
|
|
15
15
|
const fetchData = (params: Record<string, any> = {}): Promise<{ list: TRefundExpanded[]; count: number }> => {
|
|
@@ -125,7 +125,7 @@ export default function RefundList({
|
|
|
125
125
|
label: t('common.amount'),
|
|
126
126
|
name: 'id',
|
|
127
127
|
align: 'right',
|
|
128
|
-
width:
|
|
128
|
+
width: 80,
|
|
129
129
|
options: {
|
|
130
130
|
customBodyRenderLite: (_: string, index: number) => {
|
|
131
131
|
const item = data.list[index] as TRefundExpanded;
|
|
@@ -178,7 +178,11 @@ export default function RefundList({
|
|
|
178
178
|
options: {
|
|
179
179
|
customBodyRenderLite: (_: string, index: number) => {
|
|
180
180
|
const item = data.list[index] as TRefundExpanded;
|
|
181
|
-
return
|
|
181
|
+
return (
|
|
182
|
+
<Link to={`/admin/payments/${item.id}`}>
|
|
183
|
+
{item.description ? capitalize(toLower(item.description.replace(/_/g, ' '))) : item.id}
|
|
184
|
+
</Link>
|
|
185
|
+
);
|
|
182
186
|
},
|
|
183
187
|
},
|
|
184
188
|
},
|
|
@@ -294,6 +298,7 @@ export default function RefundList({
|
|
|
294
298
|
/>
|
|
295
299
|
)
|
|
296
300
|
}
|
|
301
|
+
emptyNodeText={t('empty.refunds')}
|
|
297
302
|
/>
|
|
298
303
|
);
|
|
299
304
|
}
|
|
@@ -17,8 +17,15 @@ export default function SectionHeader(props: Props) {
|
|
|
17
17
|
alignItems="center"
|
|
18
18
|
flexWrap="wrap"
|
|
19
19
|
gap={1}
|
|
20
|
-
sx={{ mb: props.mb, mt: props.mt, pb: 1
|
|
21
|
-
<Typography
|
|
20
|
+
sx={{ mb: props.mb, mt: props.mt, pb: 1 }}>
|
|
21
|
+
<Typography
|
|
22
|
+
variant="h3"
|
|
23
|
+
sx={{
|
|
24
|
+
fontSize: {
|
|
25
|
+
xs: '18px',
|
|
26
|
+
md: '1.25rem',
|
|
27
|
+
},
|
|
28
|
+
}}>
|
|
22
29
|
{props.title}
|
|
23
30
|
</Typography>
|
|
24
31
|
{props.children}
|
|
@@ -28,6 +35,6 @@ export default function SectionHeader(props: Props) {
|
|
|
28
35
|
|
|
29
36
|
SectionHeader.defaultProps = {
|
|
30
37
|
children: null,
|
|
31
|
-
mb: 1,
|
|
32
|
-
mt: 1,
|
|
38
|
+
mb: 1.5,
|
|
39
|
+
mt: 1.5,
|
|
33
40
|
};
|
|
@@ -5,19 +5,22 @@ import { Stack, Tooltip, Typography } from '@mui/material';
|
|
|
5
5
|
|
|
6
6
|
type Props = {
|
|
7
7
|
subscription: TSubscriptionExpanded;
|
|
8
|
-
variant?: 'body1' | 'h5';
|
|
8
|
+
variant?: 'body1' | 'h5' | 'h4' | 'h3' | 'h2' | 'h1';
|
|
9
|
+
hideSubscription?: boolean;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
export default function SubscriptionDescription({ subscription, variant }: Props) {
|
|
12
|
+
export default function SubscriptionDescription({ subscription, variant, hideSubscription }: Props) {
|
|
12
13
|
if (subscription.description) {
|
|
13
14
|
return (
|
|
14
15
|
<Stack direction="row" alignItems="center" spacing={1}>
|
|
15
|
-
<Typography variant={variant} fontWeight={600}>
|
|
16
|
+
<Typography variant={variant} fontWeight={600} className="subscription-description">
|
|
16
17
|
{subscription.description}
|
|
17
18
|
</Typography>
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
19
|
+
{!hideSubscription && (
|
|
20
|
+
<Tooltip title={formatSubscriptionProduct(subscription.items)}>
|
|
21
|
+
<InfoOutlined sx={{ color: 'text.secondary' }} fontSize="small" />
|
|
22
|
+
</Tooltip>
|
|
23
|
+
)}
|
|
21
24
|
</Stack>
|
|
22
25
|
);
|
|
23
26
|
}
|
|
@@ -31,4 +34,5 @@ export default function SubscriptionDescription({ subscription, variant }: Props
|
|
|
31
34
|
|
|
32
35
|
SubscriptionDescription.defaultProps = {
|
|
33
36
|
variant: 'body1',
|
|
37
|
+
hideSubscription: false,
|
|
34
38
|
};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
-
import { formatPrice } from '@blocklet/payment-react';
|
|
3
|
+
import { formatPrice, Table } from '@blocklet/payment-react';
|
|
4
4
|
import type { TPaymentCurrency, TSubscriptionItemExpanded } from '@blocklet/payment-types';
|
|
5
|
-
import { Stack, Typography } from '@mui/material';
|
|
5
|
+
import { Avatar, Stack, Typography } from '@mui/material';
|
|
6
6
|
|
|
7
7
|
import Copyable from '../../copyable';
|
|
8
|
-
import Table from '../../table';
|
|
9
8
|
import LineItemActions from './actions';
|
|
10
9
|
import UsageRecords from './usage-records';
|
|
11
10
|
|
|
@@ -19,9 +18,10 @@ SubscriptionItemList.defaultProps = {
|
|
|
19
18
|
mode: 'customer',
|
|
20
19
|
};
|
|
21
20
|
|
|
21
|
+
const size = { width: 48, height: 48 };
|
|
22
|
+
|
|
22
23
|
export default function SubscriptionItemList({ data, currency, mode }: ListProps) {
|
|
23
24
|
const { t } = useLocaleContext();
|
|
24
|
-
|
|
25
25
|
const columns = [
|
|
26
26
|
{
|
|
27
27
|
label: t('admin.subscription.product'),
|
|
@@ -30,12 +30,33 @@ export default function SubscriptionItemList({ data, currency, mode }: ListProps
|
|
|
30
30
|
customBodyRenderLite: (_: string, index: number) => {
|
|
31
31
|
const item = data[index] as TSubscriptionItemExpanded;
|
|
32
32
|
return (
|
|
33
|
-
<Stack
|
|
33
|
+
<Stack
|
|
34
|
+
flexDirection="row"
|
|
35
|
+
flexWrap="wrap"
|
|
36
|
+
alignItems="center"
|
|
37
|
+
gap={{
|
|
38
|
+
xs: 1,
|
|
39
|
+
sm: 2,
|
|
40
|
+
}}>
|
|
41
|
+
{item.price.product.images.length > 0 ? (
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
<Avatar
|
|
44
|
+
key={item.price.product_id}
|
|
45
|
+
src={item.price.product.images[0]}
|
|
46
|
+
alt={item.price.product.name}
|
|
47
|
+
variant="rounded"
|
|
48
|
+
sx={size}
|
|
49
|
+
/>
|
|
50
|
+
) : (
|
|
51
|
+
<Avatar key={item.price.product_id} variant="rounded" sx={size}>
|
|
52
|
+
{item.price.product.name.slice(0, 1)}
|
|
53
|
+
</Avatar>
|
|
54
|
+
)}
|
|
34
55
|
<Typography color="text.primary" fontWeight={600}>
|
|
35
56
|
{item?.price.product.name}
|
|
36
57
|
{mode === 'customer' ? '' : ` - ${item?.price_id}`}
|
|
37
58
|
</Typography>
|
|
38
|
-
<Typography color="text.secondary">
|
|
59
|
+
<Typography color="text.secondary" whiteSpace="nowrap">
|
|
39
60
|
{formatPrice(item.price, currency, item?.price.product.unit_label)}
|
|
40
61
|
</Typography>
|
|
41
62
|
</Stack>
|
|
@@ -112,6 +133,7 @@ export default function SubscriptionItemList({ data, currency, mode }: ListProps
|
|
|
112
133
|
page: 0,
|
|
113
134
|
rowsPerPage: 100,
|
|
114
135
|
}}
|
|
136
|
+
emptyNodeText={t('customer.product.empty')}
|
|
115
137
|
/>
|
|
116
138
|
);
|
|
117
139
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
-
import { Status, api, formatTime } from '@blocklet/payment-react';
|
|
3
|
+
import { Status, api, formatTime, Table } from '@blocklet/payment-react';
|
|
4
4
|
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
5
5
|
import { CircularProgress } from '@mui/material';
|
|
6
6
|
import { useLocalStorageState } from 'ahooks';
|
|
@@ -9,7 +9,6 @@ import { Link } from 'react-router-dom';
|
|
|
9
9
|
|
|
10
10
|
import CustomerLink from '../customer/link';
|
|
11
11
|
import FilterToolbar from '../filter-toolbar';
|
|
12
|
-
import Table from '../table';
|
|
13
12
|
import SubscriptionActions from './actions';
|
|
14
13
|
import SubscriptionDescription from './description';
|
|
15
14
|
import SubscriptionStatus from './status';
|
|
@@ -238,6 +237,7 @@ export default function SubscriptionList({ customer_id, features, status }: List
|
|
|
238
237
|
setSearch={setSearch}
|
|
239
238
|
search={search}
|
|
240
239
|
status={['active', 'trialing', 'paused', 'past_due', 'canceled', 'incomplete', 'incomplete_expired']}
|
|
240
|
+
formatStatus={(x) => (x === 'canceled' ? 'ended' : x)}
|
|
241
241
|
/>
|
|
242
242
|
)
|
|
243
243
|
}
|
|
@@ -4,6 +4,7 @@ import type { TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
|
4
4
|
import { useRequest } from 'ahooks';
|
|
5
5
|
|
|
6
6
|
import InfoMetric from '../info-metric';
|
|
7
|
+
import SubscriptionStatus from './status';
|
|
7
8
|
|
|
8
9
|
type Props = {
|
|
9
10
|
subscription: TSubscriptionExpanded;
|
|
@@ -17,15 +18,16 @@ export default function SubscriptionMetrics({ subscription }: Props) {
|
|
|
17
18
|
const { t } = useLocaleContext();
|
|
18
19
|
const { data: upcoming } = useRequest(() => fetchUpcoming(subscription.id));
|
|
19
20
|
|
|
20
|
-
let scheduleToCancelTime = 0;
|
|
21
|
-
if (['active', 'trialing', 'past_due'].includes(subscription.status) && subscription.cancel_at) {
|
|
22
|
-
|
|
23
|
-
} else if (subscription.status !== 'canceled' && subscription.cancel_at_period_end) {
|
|
24
|
-
|
|
25
|
-
}
|
|
21
|
+
// let scheduleToCancelTime = 0;
|
|
22
|
+
// if (['active', 'trialing', 'past_due'].includes(subscription.status) && subscription.cancel_at) {
|
|
23
|
+
// scheduleToCancelTime = subscription.cancel_at * 1000;
|
|
24
|
+
// } else if (subscription.status !== 'canceled' && subscription.cancel_at_period_end) {
|
|
25
|
+
// scheduleToCancelTime = subscription.current_period_end * 1000;
|
|
26
|
+
// }
|
|
26
27
|
|
|
27
28
|
return (
|
|
28
29
|
<>
|
|
30
|
+
<InfoMetric label={t('common.status')} value={<SubscriptionStatus subscription={subscription} />} divider />
|
|
29
31
|
<InfoMetric
|
|
30
32
|
label={t('admin.subscription.startedAt')}
|
|
31
33
|
value={formatTime(subscription.start_date ? subscription.start_date * 1000 : subscription.created_at)}
|
|
@@ -47,9 +49,9 @@ export default function SubscriptionMetrics({ subscription }: Props) {
|
|
|
47
49
|
divider
|
|
48
50
|
/>
|
|
49
51
|
)}
|
|
50
|
-
{scheduleToCancelTime > 0 && (
|
|
52
|
+
{/* {scheduleToCancelTime > 0 && (
|
|
51
53
|
<InfoMetric label={t('admin.subscription.cancel.schedule')} value={formatTime(scheduleToCancelTime)} divider />
|
|
52
|
-
)}
|
|
54
|
+
)} */}
|
|
53
55
|
{subscription.status === 'canceled' && subscription.canceled_at && (
|
|
54
56
|
<InfoMetric
|
|
55
57
|
label={t('admin.subscription.cancel.done')}
|