payment-kit 1.13.25 → 1.13.27
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/index.ts +8 -1
- package/api/src/integrations/blockchain/nft.ts +125 -0
- package/api/src/integrations/blockchain/stake.ts +55 -0
- package/api/src/integrations/blocklet/notification.ts +101 -0
- package/api/src/integrations/blocklet/passport.ts +139 -0
- package/api/src/integrations/stripe/handlers/invoice.ts +1 -1
- package/api/src/integrations/stripe/resource.ts +7 -7
- package/api/src/integrations/stripe/setup.ts +1 -1
- package/api/src/jobs/checkout-session.ts +23 -0
- package/api/src/jobs/payment.ts +1 -2
- package/api/src/libs/audit.ts +44 -2
- package/api/src/libs/payment.ts +3 -4
- package/api/src/locales/en.ts +9 -1
- package/api/src/locales/zh.ts +9 -1
- package/api/src/routes/checkout-sessions.ts +44 -14
- package/api/src/routes/connect/collect.ts +1 -2
- package/api/src/routes/connect/pay.ts +1 -2
- package/api/src/routes/connect/setup.ts +1 -2
- package/api/src/routes/connect/shared.ts +7 -3
- package/api/src/routes/connect/subscribe.ts +2 -3
- package/api/src/routes/index.ts +4 -0
- package/api/src/routes/integrations/stripe.ts +1 -1
- package/api/src/routes/passports.ts +74 -0
- package/api/src/routes/payment-links.ts +12 -2
- package/api/src/routes/pricing-table.ts +17 -3
- package/api/src/routes/products.ts +3 -3
- package/api/src/routes/redirect.ts +18 -0
- package/api/src/routes/subscriptions.ts +2 -5
- package/api/src/store/migrations/20231021-nft.ts +22 -0
- package/api/src/store/models/checkout-session.ts +76 -20
- package/api/src/store/models/invoice.ts +2 -0
- package/api/src/store/models/payment-intent.ts +2 -0
- package/api/src/store/models/payment-link.ts +26 -15
- package/api/src/store/models/payment-method.ts +22 -1
- package/api/src/store/models/price.ts +2 -0
- package/api/src/store/models/subscription.ts +26 -4
- package/api/src/store/models/types.ts +32 -1
- package/api/third.d.ts +2 -0
- package/blocklet.yml +1 -1
- package/package.json +7 -5
- package/src/components/customer/actions.tsx +15 -17
- package/src/components/customer/form.tsx +1 -1
- package/src/components/invoice/list.tsx +2 -1
- package/src/components/passport/actions.tsx +62 -0
- package/src/components/passport/assign.tsx +82 -0
- package/src/components/payment-intent/list.tsx +5 -1
- package/src/components/payment-link/actions.tsx +14 -1
- package/src/components/payment-link/after-pay.tsx +33 -1
- package/src/components/payment-link/preview.tsx +3 -6
- package/src/components/price/form.tsx +22 -23
- package/src/components/pricing-table/actions.tsx +14 -1
- package/src/components/pricing-table/payment-settings.tsx +33 -1
- package/src/components/pricing-table/preview.tsx +3 -7
- package/src/components/pricing-table/product-settings.tsx +4 -0
- package/src/components/pricing-table/product-skeleton.tsx +39 -0
- package/src/components/product/actions.tsx +14 -1
- package/src/components/status.tsx +1 -1
- package/src/components/subscription/status.tsx +3 -3
- package/src/components/table.tsx +14 -4
- package/src/global.css +7 -5
- package/src/libs/util.ts +6 -0
- package/src/locales/en.tsx +53 -2
- package/src/locales/zh.tsx +272 -116
- package/src/pages/admin/payments/links/create.tsx +4 -0
- package/src/pages/admin/payments/links/detail.tsx +9 -4
- package/src/pages/admin/products/index.tsx +2 -0
- package/src/pages/admin/products/passports/index.tsx +154 -0
- package/src/pages/admin/products/pricing-tables/create.tsx +1 -1
- package/src/pages/admin/settings/index.tsx +1 -1
- package/src/pages/admin/settings/payment-methods/index.tsx +17 -7
- package/src/pages/checkout/pay.tsx +15 -13
- package/src/pages/checkout/pricing-table.tsx +127 -91
- package/api/src/libs/chain/arcblock.ts +0 -13
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
+
import Toast from '@arcblock/ux/lib/Toast';
|
|
3
|
+
import { Alert, Box, CircularProgress, MenuItem, Select, Typography } from '@mui/material';
|
|
4
|
+
import { useRequest } from 'ahooks';
|
|
5
|
+
import { useEffect, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import api from '../../libs/api';
|
|
8
|
+
import ConfirmDialog from '../confirm';
|
|
9
|
+
|
|
10
|
+
const fetchData = (): Promise<any[]> => {
|
|
11
|
+
return api.get('/api/passports').then((res) => res.data);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default function AssignPassportDialog(props: { id: string; onCancel: any }) {
|
|
15
|
+
const { t } = useLocaleContext();
|
|
16
|
+
const { loading, error, data } = useRequest(fetchData);
|
|
17
|
+
const [selected, setSelected] = useState('');
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (data && data.length && !selected) {
|
|
21
|
+
setSelected(data[0].name);
|
|
22
|
+
}
|
|
23
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
24
|
+
}, [data]);
|
|
25
|
+
|
|
26
|
+
const onConfirm = async () => {
|
|
27
|
+
if (!selected) {
|
|
28
|
+
Toast.error(t('admin.passport.assignError'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await api.put('/api/passports/assign', { name: selected, id: props.id });
|
|
33
|
+
Toast.success(t('common.saved'));
|
|
34
|
+
props.onCancel();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (error) {
|
|
38
|
+
return (
|
|
39
|
+
<ConfirmDialog
|
|
40
|
+
title={t('admin.passport.assign')}
|
|
41
|
+
message={<Alert severity="error">{error.message}</Alert>}
|
|
42
|
+
onConfirm={onConfirm}
|
|
43
|
+
onCancel={props.onCancel}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (loading || !data) {
|
|
49
|
+
return (
|
|
50
|
+
<ConfirmDialog
|
|
51
|
+
title={t('admin.passport.assign')}
|
|
52
|
+
message={<CircularProgress />}
|
|
53
|
+
onConfirm={onConfirm}
|
|
54
|
+
onCancel={props.onCancel}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const onRoleChange = (e: any) => {
|
|
60
|
+
setSelected(e.target.value);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<ConfirmDialog
|
|
65
|
+
title={t('admin.passport.assign')}
|
|
66
|
+
message={
|
|
67
|
+
<Box>
|
|
68
|
+
<Typography>{t('admin.passport.assignTip')}</Typography>
|
|
69
|
+
<Select value={selected} onChange={onRoleChange} size="small">
|
|
70
|
+
{data.map((x) => (
|
|
71
|
+
<MenuItem key={x.name} value={x.name}>
|
|
72
|
+
<Typography color={selected ? 'text.primary' : 'text.secondary'}>{x.title}</Typography>
|
|
73
|
+
</MenuItem>
|
|
74
|
+
))}
|
|
75
|
+
</Select>
|
|
76
|
+
</Box>
|
|
77
|
+
}
|
|
78
|
+
onConfirm={onConfirm}
|
|
79
|
+
onCancel={props.onCancel}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -92,11 +92,13 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
92
92
|
{
|
|
93
93
|
label: t('common.amount'),
|
|
94
94
|
name: 'id',
|
|
95
|
+
align: 'right',
|
|
96
|
+
width: 60,
|
|
95
97
|
options: {
|
|
96
98
|
customBodyRenderLite: (_: string, index: number) => {
|
|
97
99
|
const item = data.list[index] as TPaymentIntentExpanded;
|
|
98
100
|
return (
|
|
99
|
-
<Typography component="
|
|
101
|
+
<Typography component="strong" fontWeight={600}>
|
|
100
102
|
{fromUnitToToken(item?.amount, item?.paymentCurrency.decimal)}
|
|
101
103
|
{item?.paymentCurrency.symbol}
|
|
102
104
|
</Typography>
|
|
@@ -107,7 +109,9 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
107
109
|
{
|
|
108
110
|
label: t('common.status'),
|
|
109
111
|
name: 'status',
|
|
112
|
+
width: 60,
|
|
110
113
|
options: {
|
|
114
|
+
filter: true,
|
|
111
115
|
customBodyRenderLite: (_: string, index: number) => {
|
|
112
116
|
const item = data.list[index] as TPaymentIntentExpanded;
|
|
113
117
|
return <Status label={item.status} color={getPaymentIntentStatusColor(item.status)} />;
|
|
@@ -11,6 +11,7 @@ import { formatError } from '../../libs/util';
|
|
|
11
11
|
import Actions from '../actions';
|
|
12
12
|
import ClickBoundary from '../click-boundary';
|
|
13
13
|
import ConfirmDialog from '../confirm';
|
|
14
|
+
import AssignPassportDialog from '../passport/assign';
|
|
14
15
|
import RenamePaymentLink from './rename';
|
|
15
16
|
|
|
16
17
|
type Props = {
|
|
@@ -92,7 +93,18 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
|
|
|
92
93
|
},
|
|
93
94
|
{ label: t('admin.paymentLink.rename'), handler: () => setState({ action: 'rename' }), color: 'primary' },
|
|
94
95
|
{ label: t('admin.paymentLink.archive'), handler: () => setState({ action: 'archive' }), color: 'primary' },
|
|
95
|
-
{
|
|
96
|
+
{
|
|
97
|
+
label: t('admin.paymentLink.remove'),
|
|
98
|
+
handler: () => setState({ action: 'remove' }),
|
|
99
|
+
color: 'error',
|
|
100
|
+
divider: true,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
label: t('admin.passport.assign'),
|
|
104
|
+
handler: () => setState({ action: 'assign' }),
|
|
105
|
+
color: 'primary',
|
|
106
|
+
disabled: !data.active,
|
|
107
|
+
},
|
|
96
108
|
]}
|
|
97
109
|
/>
|
|
98
110
|
{state.action === 'rename' && (
|
|
@@ -103,6 +115,7 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
|
|
|
103
115
|
onCancel={() => setState({ action: '' })}
|
|
104
116
|
/>
|
|
105
117
|
)}
|
|
118
|
+
{state.action === 'assign' && <AssignPassportDialog id={data.id} onCancel={() => setState({ action: '' })} />}
|
|
106
119
|
{state.action === 'archive' && (
|
|
107
120
|
<ConfirmDialog
|
|
108
121
|
onConfirm={onArchive}
|
|
@@ -7,6 +7,7 @@ export default function AfterPay() {
|
|
|
7
7
|
const { t } = useLocaleContext();
|
|
8
8
|
const { control, setValue, getValues } = useFormContext();
|
|
9
9
|
const type = useWatch({ control, name: 'after_completion.type' });
|
|
10
|
+
const nftMintEnabled = useWatch({ control, name: 'nft_mint_settings.enabled' });
|
|
10
11
|
|
|
11
12
|
return (
|
|
12
13
|
<Stack spacing={2} sx={{ width: '100%' }}>
|
|
@@ -34,7 +35,13 @@ export default function AfterPay() {
|
|
|
34
35
|
name="after_completion.hosted_confirmation.custom_message"
|
|
35
36
|
control={control}
|
|
36
37
|
render={({ field }) => (
|
|
37
|
-
<TextField
|
|
38
|
+
<TextField
|
|
39
|
+
{...field}
|
|
40
|
+
placeholder={t('admin.paymentLink.customMessage')}
|
|
41
|
+
helperText={t('admin.paymentLink.customMessageTip')}
|
|
42
|
+
fullWidth
|
|
43
|
+
size="small"
|
|
44
|
+
/>
|
|
38
45
|
)}
|
|
39
46
|
/>
|
|
40
47
|
)}
|
|
@@ -82,6 +89,31 @@ export default function AfterPay() {
|
|
|
82
89
|
/>
|
|
83
90
|
)}
|
|
84
91
|
/>
|
|
92
|
+
<Controller
|
|
93
|
+
name="nft_mint_settings.enabled"
|
|
94
|
+
control={control}
|
|
95
|
+
render={({ field }) => (
|
|
96
|
+
<FormControlLabel
|
|
97
|
+
control={
|
|
98
|
+
<Checkbox
|
|
99
|
+
checked={getValues().nft_mint_settings.enabled}
|
|
100
|
+
{...field}
|
|
101
|
+
onChange={(_, checked) => setValue(field.name, checked)}
|
|
102
|
+
/>
|
|
103
|
+
}
|
|
104
|
+
label={t('admin.paymentLink.mintNft')}
|
|
105
|
+
/>
|
|
106
|
+
)}
|
|
107
|
+
/>
|
|
108
|
+
{nftMintEnabled && (
|
|
109
|
+
<Controller
|
|
110
|
+
name="nft_mint_settings.factory"
|
|
111
|
+
control={control}
|
|
112
|
+
render={({ field }) => (
|
|
113
|
+
<TextField {...field} placeholder={t('admin.paymentLink.mintNftFrom')} fullWidth size="small" />
|
|
114
|
+
)}
|
|
115
|
+
/>
|
|
116
|
+
)}
|
|
85
117
|
</Stack>
|
|
86
118
|
);
|
|
87
119
|
}
|
|
@@ -14,14 +14,11 @@ export default function PaymentLinkPreview({ id, version }: { id: string; versio
|
|
|
14
14
|
const ref = useRef(null);
|
|
15
15
|
const size = useSize(ref);
|
|
16
16
|
return (
|
|
17
|
-
<Chrome>
|
|
18
|
-
<Box ref={ref}
|
|
19
|
-
|
|
20
|
-
</Box>
|
|
17
|
+
<Chrome sx={{ width: '100%', maxWidth: 840 }}>
|
|
18
|
+
<Box ref={ref}> </Box>
|
|
21
19
|
<IframeResizer
|
|
22
20
|
style={{
|
|
23
|
-
|
|
24
|
-
minWidth: size?.width / 0.8,
|
|
21
|
+
minWidth: size ? size.width / 0.8 : 1050,
|
|
25
22
|
minHeight: '64vh',
|
|
26
23
|
transform: 'scale(0.8)',
|
|
27
24
|
transformOrigin: 'top left',
|
|
@@ -78,7 +78,6 @@ const hasMoreCurrency = (methods: TPaymentMethodExpanded[]) => {
|
|
|
78
78
|
return methods.every((method) => method.payment_currencies.length > 1) || methods.length > 1;
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
-
// FIXME: @wangshijun i18n
|
|
82
81
|
export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
83
82
|
const getFieldName = (name: string) => (prefix ? `${prefix}.${name}` : name);
|
|
84
83
|
|
|
@@ -106,16 +105,16 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
106
105
|
<Box sx={{ width: INPUT_WIDTH }}>
|
|
107
106
|
<FormLabel>{t('admin.price.model')}</FormLabel>
|
|
108
107
|
<Select {...field} fullWidth size="small">
|
|
109
|
-
<MenuItem value="standard">
|
|
110
|
-
<MenuItem value="package">
|
|
108
|
+
<MenuItem value="standard">{t('admin.price.models.standard')}</MenuItem>
|
|
109
|
+
<MenuItem value="package">{t('admin.price.models.package')}</MenuItem>
|
|
111
110
|
<MenuItem value="graduated" disabled>
|
|
112
|
-
|
|
111
|
+
{t('admin.price.models.graduated')}
|
|
113
112
|
</MenuItem>
|
|
114
113
|
<MenuItem value="volume" disabled>
|
|
115
|
-
|
|
114
|
+
{t('admin.price.models.volume')}
|
|
116
115
|
</MenuItem>
|
|
117
116
|
<MenuItem value="custom" disabled>
|
|
118
|
-
|
|
117
|
+
{t('admin.price.models.custom')}
|
|
119
118
|
</MenuItem>
|
|
120
119
|
</Select>
|
|
121
120
|
</Box>
|
|
@@ -235,8 +234,8 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
235
234
|
disabled={isLocked}
|
|
236
235
|
render={({ field }) => (
|
|
237
236
|
<ToggleButtonGroup {...field} onChange={(_, value: string) => setValue(field.name, value)} exclusive>
|
|
238
|
-
<ToggleButton value="recurring">
|
|
239
|
-
<ToggleButton value="one_time">
|
|
237
|
+
<ToggleButton value="recurring">{t('admin.price.types.recurring')}</ToggleButton>
|
|
238
|
+
<ToggleButton value="one_time">{t('admin.price.types.onetime')}</ToggleButton>
|
|
240
239
|
</ToggleButtonGroup>
|
|
241
240
|
)}
|
|
242
241
|
/>
|
|
@@ -259,14 +258,14 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
259
258
|
}}
|
|
260
259
|
sx={{ width: INPUT_WIDTH }}
|
|
261
260
|
size="small">
|
|
262
|
-
{!livemode && <MenuItem value="hour_1">
|
|
263
|
-
<MenuItem value="day_1">
|
|
264
|
-
<MenuItem value="week_1">
|
|
265
|
-
<MenuItem value="month_1">
|
|
266
|
-
<MenuItem value="month_3">
|
|
267
|
-
<MenuItem value="month_6">
|
|
268
|
-
<MenuItem value="year_1">
|
|
269
|
-
<MenuItem value="month_2">
|
|
261
|
+
{!livemode && <MenuItem value="hour_1">{t('common.hourly')}</MenuItem>}
|
|
262
|
+
<MenuItem value="day_1">{t('common.daily')}</MenuItem>
|
|
263
|
+
<MenuItem value="week_1">{t('common.weekly')}</MenuItem>
|
|
264
|
+
<MenuItem value="month_1">{t('common.monthly')}</MenuItem>
|
|
265
|
+
<MenuItem value="month_3">{t('common.month3')}</MenuItem>
|
|
266
|
+
<MenuItem value="month_6">{t('common.month6')}</MenuItem>
|
|
267
|
+
<MenuItem value="year_1">{t('common.yearly')}</MenuItem>
|
|
268
|
+
<MenuItem value="month_2">{t('common.custom')}</MenuItem>
|
|
270
269
|
</Select>
|
|
271
270
|
</Box>
|
|
272
271
|
)}
|
|
@@ -289,9 +288,9 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
289
288
|
endAdornment: (
|
|
290
289
|
<InputAdornment position="end">
|
|
291
290
|
<select onChange={(e) => setValue(getFieldName('recurring.interval'), e.target.value)}>
|
|
292
|
-
<option value="day">days</option>
|
|
293
|
-
<option value="week">weeks</option>
|
|
294
|
-
<option value="month">months</option>
|
|
291
|
+
<option value="day">{t('common.days')}</option>
|
|
292
|
+
<option value="week">{t('common.weeks')}</option>
|
|
293
|
+
<option value="month">{t('common.months')}</option>
|
|
295
294
|
</select>
|
|
296
295
|
</InputAdornment>
|
|
297
296
|
),
|
|
@@ -339,10 +338,10 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
339
338
|
<Box>
|
|
340
339
|
<FormLabel>{t('admin.price.recurring.aggregate')}</FormLabel>
|
|
341
340
|
<Select {...field} sx={{ width: INPUT_WIDTH }} size="small">
|
|
342
|
-
<MenuItem value="sum">
|
|
343
|
-
<MenuItem value="max">
|
|
344
|
-
<MenuItem value="last_ever">
|
|
345
|
-
<MenuItem value="last_during_period">
|
|
341
|
+
<MenuItem value="sum">{t('admin.price.aggregate.sum')}</MenuItem>
|
|
342
|
+
<MenuItem value="max">{t('admin.price.aggregate.max')}</MenuItem>
|
|
343
|
+
<MenuItem value="last_ever">{t('admin.price.aggregate.last_ever')}</MenuItem>
|
|
344
|
+
<MenuItem value="last_during_period">{t('admin.price.aggregate.last_during_period')}</MenuItem>
|
|
346
345
|
</Select>
|
|
347
346
|
</Box>
|
|
348
347
|
)}
|
|
@@ -11,6 +11,7 @@ import { formatError } from '../../libs/util';
|
|
|
11
11
|
import Actions from '../actions';
|
|
12
12
|
import ClickBoundary from '../click-boundary';
|
|
13
13
|
import ConfirmDialog from '../confirm';
|
|
14
|
+
import AssignPassportDialog from '../passport/assign';
|
|
14
15
|
import RenamePricingTable from './rename';
|
|
15
16
|
|
|
16
17
|
type Props = {
|
|
@@ -92,7 +93,18 @@ export default function PricingTableActions({ data, variant, onChange }: Props)
|
|
|
92
93
|
},
|
|
93
94
|
{ label: t('admin.pricingTable.rename'), handler: () => setState({ action: 'rename' }), color: 'primary' },
|
|
94
95
|
{ label: t('admin.pricingTable.archive'), handler: () => setState({ action: 'archive' }), color: 'primary' },
|
|
95
|
-
{
|
|
96
|
+
{
|
|
97
|
+
label: t('admin.pricingTable.remove'),
|
|
98
|
+
handler: () => setState({ action: 'remove' }),
|
|
99
|
+
color: 'error',
|
|
100
|
+
divider: true,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
label: t('admin.passport.assign'),
|
|
104
|
+
handler: () => setState({ action: 'assign' }),
|
|
105
|
+
color: 'primary',
|
|
106
|
+
disabled: !data.active,
|
|
107
|
+
},
|
|
96
108
|
]}
|
|
97
109
|
/>
|
|
98
110
|
{state.action === 'rename' && (
|
|
@@ -103,6 +115,7 @@ export default function PricingTableActions({ data, variant, onChange }: Props)
|
|
|
103
115
|
onCancel={() => setState({ action: '' })}
|
|
104
116
|
/>
|
|
105
117
|
)}
|
|
118
|
+
{state.action === 'assign' && <AssignPassportDialog id={data.id} onCancel={() => setState({ action: '' })} />}
|
|
106
119
|
{state.action === 'archive' && (
|
|
107
120
|
<ConfirmDialog
|
|
108
121
|
onConfirm={onArchive}
|
|
@@ -17,6 +17,7 @@ export function PricePaymentSettings({ index }: { index: number }) {
|
|
|
17
17
|
const { t } = useLocaleContext();
|
|
18
18
|
const { control, setValue, getValues } = useFormContext();
|
|
19
19
|
const type = useWatch({ control, name: getFieldName('after_completion.type') });
|
|
20
|
+
const nftMintEnabled = useWatch({ control, name: 'nft_mint_settings.enabled' });
|
|
20
21
|
|
|
21
22
|
const values = getValues();
|
|
22
23
|
|
|
@@ -91,7 +92,13 @@ export function PricePaymentSettings({ index }: { index: number }) {
|
|
|
91
92
|
name={getFieldName('after_completion.hosted_confirmation.custom_message')}
|
|
92
93
|
control={control}
|
|
93
94
|
render={({ field }) => (
|
|
94
|
-
<TextField
|
|
95
|
+
<TextField
|
|
96
|
+
{...field}
|
|
97
|
+
placeholder={t('admin.paymentLink.customMessage')}
|
|
98
|
+
helperText={t('admin.paymentLink.customMessageTip')}
|
|
99
|
+
fullWidth
|
|
100
|
+
size="small"
|
|
101
|
+
/>
|
|
95
102
|
)}
|
|
96
103
|
/>
|
|
97
104
|
)}
|
|
@@ -120,6 +127,31 @@ export function PricePaymentSettings({ index }: { index: number }) {
|
|
|
120
127
|
)}
|
|
121
128
|
/>
|
|
122
129
|
)}
|
|
130
|
+
<Controller
|
|
131
|
+
name={getFieldName('nft_mint_settings.enabled')}
|
|
132
|
+
control={control}
|
|
133
|
+
render={({ field }) => (
|
|
134
|
+
<FormControlLabel
|
|
135
|
+
control={
|
|
136
|
+
<Checkbox
|
|
137
|
+
checked={get(values, 'nft_mint_settings.enabled')}
|
|
138
|
+
{...field}
|
|
139
|
+
onChange={(_, checked) => setValue(field.name, checked)}
|
|
140
|
+
/>
|
|
141
|
+
}
|
|
142
|
+
label={t('admin.paymentLink.mintNft')}
|
|
143
|
+
/>
|
|
144
|
+
)}
|
|
145
|
+
/>
|
|
146
|
+
{nftMintEnabled && (
|
|
147
|
+
<Controller
|
|
148
|
+
name={getFieldName('nft_mint_settings.factory')}
|
|
149
|
+
control={control}
|
|
150
|
+
render={({ field }) => (
|
|
151
|
+
<TextField {...field} placeholder={t('admin.paymentLink.mintNftFrom')} fullWidth size="small" />
|
|
152
|
+
)}
|
|
153
|
+
/>
|
|
154
|
+
)}
|
|
123
155
|
</Stack>
|
|
124
156
|
);
|
|
125
157
|
}
|
|
@@ -13,15 +13,11 @@ export default function PricingTablePreview({ id, version }: { id: string; versi
|
|
|
13
13
|
const ref = useRef(null);
|
|
14
14
|
const size = useSize(ref);
|
|
15
15
|
return (
|
|
16
|
-
<Chrome>
|
|
17
|
-
<Box ref={ref}
|
|
18
|
-
|
|
19
|
-
</Box>
|
|
16
|
+
<Chrome sx={{ width: '100%', maxWidth: 840 }}>
|
|
17
|
+
<Box ref={ref}> </Box>
|
|
20
18
|
<IframeResizer
|
|
21
19
|
style={{
|
|
22
|
-
|
|
23
|
-
// eslint-disable-next-line no-unsafe-optional-chaining
|
|
24
|
-
minWidth: size?.width / 0.8,
|
|
20
|
+
minWidth: size ? size.width / 0.8 : 1050,
|
|
25
21
|
minHeight: '64vh',
|
|
26
22
|
transform: 'scale(0.8)',
|
|
27
23
|
transformOrigin: 'top left',
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Fade, Skeleton, Stack, Typography } from '@mui/material';
|
|
2
|
+
|
|
3
|
+
export default function ProductSkeleton({ count }: { count: number }) {
|
|
4
|
+
return (
|
|
5
|
+
<Fade in>
|
|
6
|
+
<Stack
|
|
7
|
+
direction="column"
|
|
8
|
+
alignItems="center"
|
|
9
|
+
padding={4}
|
|
10
|
+
spacing={1}
|
|
11
|
+
sx={{
|
|
12
|
+
width: 320,
|
|
13
|
+
border: '1px solid #eee',
|
|
14
|
+
borderRadius: 1,
|
|
15
|
+
transition: 'border-color 0.3s ease 0s, box-shadow 0.3s ease 0s',
|
|
16
|
+
boxShadow: '0 4px 8px rgba(0, 0, 0, 20%)',
|
|
17
|
+
'&:hover': {
|
|
18
|
+
borderColor: '#ddd',
|
|
19
|
+
boxShadow: '0 8px 16px rgba(0, 0, 0, 20%)',
|
|
20
|
+
},
|
|
21
|
+
}}>
|
|
22
|
+
<Typography component="div" variant="h4" sx={{ width: '50%' }}>
|
|
23
|
+
<Skeleton />
|
|
24
|
+
</Typography>
|
|
25
|
+
<Skeleton variant="text" sx={{ fontSize: '1rem', width: '60%' }} />
|
|
26
|
+
<Typography component="div" variant="h3" sx={{ width: '60%' }}>
|
|
27
|
+
<Skeleton />
|
|
28
|
+
</Typography>
|
|
29
|
+
<Typography component="div" variant="h3" sx={{ width: '100%' }}>
|
|
30
|
+
<Skeleton />
|
|
31
|
+
</Typography>
|
|
32
|
+
{Array.from({ length: count }).map((_, i) => (
|
|
33
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
34
|
+
<Skeleton key={i} variant="text" sx={{ fontSize: '1rem', width: '60%' }} />
|
|
35
|
+
))}
|
|
36
|
+
</Stack>
|
|
37
|
+
</Fade>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -9,6 +9,7 @@ import { formatError } from '../../libs/util';
|
|
|
9
9
|
import Actions from '../actions';
|
|
10
10
|
import ClickBoundary from '../click-boundary';
|
|
11
11
|
import ConfirmDialog from '../confirm';
|
|
12
|
+
import AssignPassportDialog from '../passport/assign';
|
|
12
13
|
import EditProduct from './edit';
|
|
13
14
|
|
|
14
15
|
type ProductActionProps = {
|
|
@@ -83,7 +84,18 @@ export default function ProductActions({ data, variant, onChange }: ProductActio
|
|
|
83
84
|
data.active
|
|
84
85
|
? { label: t('admin.product.archive'), handler: () => setState({ action: 'archive' }), color: 'primary' }
|
|
85
86
|
: { label: t('admin.product.unarchive'), handler: () => setState({ action: 'unarchive' }), color: 'primary' }, // prettier-ignore
|
|
86
|
-
{
|
|
87
|
+
{
|
|
88
|
+
label: t('admin.product.remove'),
|
|
89
|
+
handler: () => setState({ action: 'remove' }),
|
|
90
|
+
color: 'error',
|
|
91
|
+
divider: true,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: t('admin.passport.assign'),
|
|
95
|
+
handler: () => setState({ action: 'assign' }),
|
|
96
|
+
color: 'primary',
|
|
97
|
+
disabled: !data.active,
|
|
98
|
+
},
|
|
87
99
|
].filter(Boolean)}
|
|
88
100
|
/>
|
|
89
101
|
{state.action === 'edit' && (
|
|
@@ -121,6 +133,7 @@ export default function ProductActions({ data, variant, onChange }: ProductActio
|
|
|
121
133
|
loading={state.loading}
|
|
122
134
|
/>
|
|
123
135
|
)}
|
|
136
|
+
{state.action === 'assign' && <AssignPassportDialog id={data.id} onCancel={() => setState({ action: '' })} />}
|
|
124
137
|
</ClickBoundary>
|
|
125
138
|
);
|
|
126
139
|
}
|
|
@@ -6,7 +6,7 @@ export default function Status(props: ChipProps) {
|
|
|
6
6
|
{...props}
|
|
7
7
|
size="small"
|
|
8
8
|
variant="outlined"
|
|
9
|
-
sx={{ ...(props.sx || {}), borderRadius: '4px', textTransform: 'capitalize' }}
|
|
9
|
+
sx={{ ...(props.sx || {}), borderRadius: '4px', height: 20, lineHeight: 20, textTransform: 'capitalize' }}
|
|
10
10
|
/>
|
|
11
11
|
);
|
|
12
12
|
}
|
|
@@ -13,7 +13,7 @@ export default function SubscriptionStatus({
|
|
|
13
13
|
[key: string]: any;
|
|
14
14
|
}) {
|
|
15
15
|
const { t } = useLocaleContext();
|
|
16
|
-
if (subscription.cancel_at_period_end) {
|
|
16
|
+
if (subscription.cancel_at_period_end && subscription.current_period_end > Date.now() / 1000) {
|
|
17
17
|
return (
|
|
18
18
|
<Status
|
|
19
19
|
icon={<AccessTimeOutlined />}
|
|
@@ -24,7 +24,7 @@ export default function SubscriptionStatus({
|
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
if (subscription.cancel_at) {
|
|
27
|
+
if (subscription.cancel_at && subscription.cancel_at >= Date.now() / 1000) {
|
|
28
28
|
return (
|
|
29
29
|
<Status
|
|
30
30
|
icon={<AccessTimeOutlined />}
|
|
@@ -36,7 +36,7 @@ export default function SubscriptionStatus({
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (subscription.pause_collection) {
|
|
39
|
-
if (subscription.pause_collection.resumes_at) {
|
|
39
|
+
if (subscription.pause_collection.resumes_at && subscription.pause_collection.resumes_at > Date.now() / 1000) {
|
|
40
40
|
return (
|
|
41
41
|
<Status
|
|
42
42
|
icon={<AccessTimeOutlined />}
|
package/src/components/table.tsx
CHANGED
|
@@ -8,16 +8,16 @@ function EmptyStub() {
|
|
|
8
8
|
return null;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export default function Table({ options, toolbar = true, footer = true, ...rest }: any) {
|
|
11
|
+
export default function Table({ options, columns, toolbar = true, footer = true, ...rest }: any) {
|
|
12
12
|
const { isMobile } = useMobile();
|
|
13
13
|
const { locale } = useLocaleContext();
|
|
14
14
|
const defaultOptions = {
|
|
15
15
|
print: false,
|
|
16
16
|
download: false,
|
|
17
|
-
filter:
|
|
17
|
+
filter: true,
|
|
18
18
|
selectableRows: 'none',
|
|
19
|
-
rowsPerPage:
|
|
20
|
-
rowsPerPageOptions: [20, 50, 100],
|
|
19
|
+
rowsPerPage: 10,
|
|
20
|
+
rowsPerPageOptions: [10, 20, 50, 100],
|
|
21
21
|
searchDebounceTime: 300,
|
|
22
22
|
tableBodyHeight: '100%',
|
|
23
23
|
};
|
|
@@ -34,6 +34,12 @@ export default function Table({ options, toolbar = true, footer = true, ...rest
|
|
|
34
34
|
<Wrapped
|
|
35
35
|
locale={locale}
|
|
36
36
|
options={{ ...defaultOptions, ...options }}
|
|
37
|
+
columns={columns.map((x: any) => {
|
|
38
|
+
x.options = x.options || {};
|
|
39
|
+
x.options.filter = x.options.filter || false;
|
|
40
|
+
x.options.sort = x.options.sort || false;
|
|
41
|
+
return x;
|
|
42
|
+
})}
|
|
37
43
|
{...rest}
|
|
38
44
|
components={components}
|
|
39
45
|
isMobile={isMobile}
|
|
@@ -54,6 +60,10 @@ const Wrapped = styled(Datatable)`
|
|
|
54
60
|
background: #f5f5f5;
|
|
55
61
|
}
|
|
56
62
|
|
|
63
|
+
> div {
|
|
64
|
+
overflow: visible;
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
${(props) => {
|
|
58
68
|
if (props.isMobile) {
|
|
59
69
|
return '';
|
package/src/global.css
CHANGED
|
@@ -62,11 +62,17 @@ a:visited {
|
|
|
62
62
|
|
|
63
63
|
.MuiTableCell-root {
|
|
64
64
|
font-size: 1rem !important;
|
|
65
|
+
padding-top: 8px;
|
|
66
|
+
padding-bottom: 8px;
|
|
65
67
|
padding-right: 24px;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
th.MuiTableCell-head {
|
|
69
|
-
padding: 8px 0;
|
|
71
|
+
padding: 8px 24px 8px 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.MuiTableRow-hover {
|
|
75
|
+
cursor: pointer;
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
.MuiRadio-root {
|
|
@@ -91,7 +97,3 @@ th.MuiTableCell-head {
|
|
|
91
97
|
max-height: 100% !important;
|
|
92
98
|
overflow: auto !important;
|
|
93
99
|
}
|
|
94
|
-
|
|
95
|
-
.MuiTableRow-hover {
|
|
96
|
-
cursor: pointer;
|
|
97
|
-
}
|
package/src/libs/util.ts
CHANGED