payment-kit 1.14.21 → 1.14.23
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 +28 -15
- 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,12 +148,10 @@ 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
|
-
<MenuItem value="package"
|
|
154
|
-
{t('admin.price.models.package')}
|
|
155
|
-
</MenuItem>
|
|
154
|
+
<MenuItem value="package">{t('admin.price.models.package')}</MenuItem>
|
|
156
155
|
<MenuItem value="graduated" disabled>
|
|
157
156
|
{t('admin.price.models.graduated')}
|
|
158
157
|
</MenuItem>
|
|
@@ -177,7 +176,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
177
176
|
disabled={isLocked}
|
|
178
177
|
render={({ field }) => (
|
|
179
178
|
<Box>
|
|
180
|
-
<FormLabel>
|
|
179
|
+
<FormLabel sx={{ color: 'text.primary' }}>
|
|
181
180
|
<Stack direction="row" alignItems="center" spacing={0.5}>
|
|
182
181
|
<Typography component="span" color="text.primary">
|
|
183
182
|
{t('admin.price.amount')}
|
|
@@ -325,9 +324,10 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
325
324
|
disabled={isLocked}
|
|
326
325
|
render={({ field }) => (
|
|
327
326
|
<Box>
|
|
328
|
-
<FormLabel>{t('admin.price.recurring.interval')}</FormLabel>
|
|
327
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.recurring.interval')}</FormLabel>
|
|
329
328
|
<Select
|
|
330
329
|
{...field}
|
|
330
|
+
value={field.value || 'month_1'}
|
|
331
331
|
onChange={(e) => {
|
|
332
332
|
const [interval, count] = e.target.value.split('_');
|
|
333
333
|
setValue(getFieldName('recurring.interval'), interval);
|
|
@@ -353,6 +353,14 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
353
353
|
name={getFieldName('recurring.interval_count')}
|
|
354
354
|
control={control}
|
|
355
355
|
disabled={isLocked}
|
|
356
|
+
rules={{
|
|
357
|
+
validate: (v) => {
|
|
358
|
+
if (!intervalCountPositive(v)) {
|
|
359
|
+
return t('admin.price.recurring.intervalCountTip');
|
|
360
|
+
}
|
|
361
|
+
return true;
|
|
362
|
+
},
|
|
363
|
+
}}
|
|
356
364
|
render={({ field }) => (
|
|
357
365
|
<Box ml={2}>
|
|
358
366
|
<FormLabel> </FormLabel>
|
|
@@ -361,13 +369,16 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
361
369
|
type="number"
|
|
362
370
|
size="small"
|
|
363
371
|
sx={{ width: INPUT_WIDTH }}
|
|
364
|
-
error={
|
|
365
|
-
helperText={
|
|
372
|
+
error={!!get(errors, getFieldName('recurring.interval_count'))}
|
|
373
|
+
helperText={get(errors, getFieldName('recurring.interval_count'))?.message as string}
|
|
366
374
|
InputProps={{
|
|
367
375
|
startAdornment: <InputAdornment position="start">{t('common.every')}</InputAdornment>,
|
|
368
376
|
endAdornment: (
|
|
369
377
|
<InputAdornment position="end">
|
|
370
|
-
<select
|
|
378
|
+
<select
|
|
379
|
+
onChange={(e) => setValue(getFieldName('recurring.interval'), e.target.value)}
|
|
380
|
+
value={intervalSelectValue}
|
|
381
|
+
style={{ background: 'none', outline: 'none' }}>
|
|
371
382
|
<option value="day">{t('common.days')}</option>
|
|
372
383
|
<option value="week">{t('common.weeks')}</option>
|
|
373
384
|
<option value="month">{t('common.months')}</option>
|
|
@@ -416,7 +427,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
416
427
|
disabled={isLocked}
|
|
417
428
|
render={({ field }) => (
|
|
418
429
|
<Box>
|
|
419
|
-
<FormLabel>{t('admin.price.recurring.aggregate')}</FormLabel>
|
|
430
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.recurring.aggregate')}</FormLabel>
|
|
420
431
|
<Select {...field} sx={{ width: INPUT_WIDTH }} size="small">
|
|
421
432
|
<MenuItem value="sum">{t('admin.price.aggregate.sum')}</MenuItem>
|
|
422
433
|
<MenuItem value="max">{t('admin.price.aggregate.max')}</MenuItem>
|
|
@@ -435,7 +446,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
435
446
|
control={control}
|
|
436
447
|
render={({ field }) => (
|
|
437
448
|
<Box>
|
|
438
|
-
<FormLabel>{t('admin.price.quantityAvailable.label')}</FormLabel>
|
|
449
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.quantityAvailable.label')}</FormLabel>
|
|
439
450
|
<TextField
|
|
440
451
|
{...field}
|
|
441
452
|
size="small"
|
|
@@ -453,7 +464,9 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
453
464
|
control={control}
|
|
454
465
|
render={({ field }) => (
|
|
455
466
|
<Box>
|
|
456
|
-
<FormLabel
|
|
467
|
+
<FormLabel sx={{ color: 'text.primary' }}>
|
|
468
|
+
{t('admin.price.quantityLimitPerCheckout.label')}
|
|
469
|
+
</FormLabel>
|
|
457
470
|
<TextField
|
|
458
471
|
{...field}
|
|
459
472
|
size="small"
|
|
@@ -471,7 +484,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
471
484
|
control={control}
|
|
472
485
|
render={({ field }) => (
|
|
473
486
|
<Box>
|
|
474
|
-
<FormLabel>{t('admin.price.nickname.label')}</FormLabel>
|
|
487
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.nickname.label')}</FormLabel>
|
|
475
488
|
<TextField {...field} size="small" sx={{ width: INPUT_WIDTH }} />
|
|
476
489
|
</Box>
|
|
477
490
|
)}
|
|
@@ -481,7 +494,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
481
494
|
control={control}
|
|
482
495
|
render={({ field }) => (
|
|
483
496
|
<Box>
|
|
484
|
-
<FormLabel>{t('admin.price.lookup_key.label')}</FormLabel>
|
|
497
|
+
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.lookup_key.label')}</FormLabel>
|
|
485
498
|
<TextField {...field} size="small" sx={{ width: INPUT_WIDTH }} />
|
|
486
499
|
</Box>
|
|
487
500
|
)}
|
|
@@ -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')}
|