payment-kit 1.14.27 → 1.14.29
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/routes/prices.ts +2 -2
- package/blocklet.yml +1 -1
- package/package.json +4 -4
- package/src/components/copyable.tsx +8 -4
- package/src/components/filter-toolbar.tsx +57 -49
- package/src/components/info-card.tsx +6 -1
- package/src/components/info-row.tsx +7 -1
- package/src/components/invoice/list.tsx +1 -1
- package/src/components/payment-intent/actions.tsx +2 -1
- package/src/components/payment-link/before-pay.tsx +13 -1
- package/src/components/pricing-table/preview.tsx +2 -2
- package/src/components/pricing-table/product-settings.tsx +12 -1
- package/src/components/subscription/portal/list.tsx +42 -18
- package/src/components/uploader.tsx +3 -1
- package/src/locales/en.tsx +3 -0
- package/src/locales/zh.tsx +3 -0
- package/src/pages/admin/billing/invoices/detail.tsx +1 -1
- package/src/pages/admin/billing/subscriptions/detail.tsx +1 -1
- package/src/pages/admin/customers/customers/detail.tsx +1 -1
- package/src/pages/admin/payments/intents/detail.tsx +1 -1
- package/src/pages/admin/payments/payouts/detail.tsx +1 -1
- package/src/pages/admin/payments/refunds/detail.tsx +1 -1
- package/src/pages/admin/products/links/create.tsx +11 -2
- package/src/pages/admin/products/links/detail.tsx +1 -1
- package/src/pages/admin/products/prices/actions.tsx +2 -2
- package/src/pages/admin/products/prices/detail.tsx +1 -1
- package/src/pages/admin/products/pricing-tables/create.tsx +11 -5
- package/src/pages/admin/products/pricing-tables/detail.tsx +1 -1
- package/src/pages/admin/products/products/detail.tsx +1 -1
- package/src/pages/customer/index.tsx +26 -14
- package/src/pages/customer/invoice/detail.tsx +1 -1
- package/src/pages/customer/refund/list.tsx +2 -0
- package/src/pages/home.tsx +1 -1
package/api/src/routes/prices.ts
CHANGED
|
@@ -176,12 +176,12 @@ const priceAmountSchema = Joi.object({
|
|
|
176
176
|
currency_options: Joi.array()
|
|
177
177
|
.items(
|
|
178
178
|
Joi.object({
|
|
179
|
-
unit_amount: Joi.number().greater(0).
|
|
179
|
+
unit_amount: Joi.number().greater(0).optional(),
|
|
180
180
|
// 其他属性
|
|
181
181
|
}).unknown(true)
|
|
182
182
|
)
|
|
183
183
|
.optional(),
|
|
184
|
-
unit_amount: Joi.number().greater(0).
|
|
184
|
+
unit_amount: Joi.number().greater(0).optional(),
|
|
185
185
|
});
|
|
186
186
|
|
|
187
187
|
// FIXME: @wangshijun use schema validation
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.29",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@arcblock/validator": "^1.18.128",
|
|
53
53
|
"@blocklet/js-sdk": "1.16.28",
|
|
54
54
|
"@blocklet/logger": "1.16.28",
|
|
55
|
-
"@blocklet/payment-react": "1.14.
|
|
55
|
+
"@blocklet/payment-react": "1.14.29",
|
|
56
56
|
"@blocklet/sdk": "1.16.28",
|
|
57
57
|
"@blocklet/ui-react": "^2.10.16",
|
|
58
58
|
"@blocklet/uploader": "^0.1.20",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"devDependencies": {
|
|
119
119
|
"@abtnode/types": "1.16.28",
|
|
120
120
|
"@arcblock/eslint-config-ts": "^0.3.2",
|
|
121
|
-
"@blocklet/payment-types": "1.14.
|
|
121
|
+
"@blocklet/payment-types": "1.14.29",
|
|
122
122
|
"@types/cookie-parser": "^1.4.7",
|
|
123
123
|
"@types/cors": "^2.8.17",
|
|
124
124
|
"@types/debug": "^4.1.12",
|
|
@@ -160,5 +160,5 @@
|
|
|
160
160
|
"parser": "typescript"
|
|
161
161
|
}
|
|
162
162
|
},
|
|
163
|
-
"gitHead": "
|
|
163
|
+
"gitHead": "104f4189d9ef96c1e332ffa90824ebd3063c989d"
|
|
164
164
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CopyButton } from '@arcblock/ux/lib/ClickToCopy';
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
+
import { getWordBreakStyle } from '@blocklet/payment-react';
|
|
3
4
|
import { Box, Stack, Typography } from '@mui/material';
|
|
4
5
|
|
|
5
6
|
export default function Copyable({ text, children, style }: { text: string; children?: React.ReactNode; style?: any }) {
|
|
@@ -18,10 +19,13 @@ export default function Copyable({ text, children, style }: { text: string; chil
|
|
|
18
19
|
sx={{
|
|
19
20
|
mr: 0.5,
|
|
20
21
|
maxWidth: 480,
|
|
21
|
-
overflow: 'hidden',
|
|
22
|
-
fontSize: 'inherit',
|
|
23
|
-
textOverflow: 'ellipsis',
|
|
24
|
-
whiteSpace: 'nowrap',
|
|
22
|
+
// overflow: 'hidden',
|
|
23
|
+
// fontSize: 'inherit',
|
|
24
|
+
// textOverflow: 'ellipsis',
|
|
25
|
+
// whiteSpace: 'nowrap',
|
|
26
|
+
wordBreak: getWordBreakStyle(text),
|
|
27
|
+
whiteSpace: 'break-spaces',
|
|
28
|
+
minWidth: '60px',
|
|
25
29
|
}}>
|
|
26
30
|
{text}
|
|
27
31
|
</Typography>
|
|
@@ -2,7 +2,7 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
|
2
2
|
import { api, useMobile, usePaymentContext } from '@blocklet/payment-react';
|
|
3
3
|
import type { TCustomer } from '@blocklet/payment-types';
|
|
4
4
|
import { Add, Close } from '@mui/icons-material';
|
|
5
|
-
import { Button,
|
|
5
|
+
import { Button, Menu, MenuItem } from '@mui/material';
|
|
6
6
|
import { Box, styled } from '@mui/system';
|
|
7
7
|
import { useEffect, useState } from 'react';
|
|
8
8
|
|
|
@@ -54,7 +54,12 @@ export default function FilterToolbar(props: Props) {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
return (
|
|
57
|
-
<Root
|
|
57
|
+
<Root
|
|
58
|
+
sx={{
|
|
59
|
+
'.MuiTouchRipple-root': {
|
|
60
|
+
display: 'none !important',
|
|
61
|
+
},
|
|
62
|
+
}}>
|
|
58
63
|
<Box className="table-toolbar-left">
|
|
59
64
|
<SearchStatus setSearch={handleSearch} search={search} status={status} formatStatus={formatStatus} />
|
|
60
65
|
{!isMobile && (
|
|
@@ -308,7 +313,7 @@ function SearchCustomers({ setSearch, search }: Pick<Props, 'setSearch' | 'searc
|
|
|
308
313
|
}
|
|
309
314
|
|
|
310
315
|
function SearchProducts({ setSearch }: Pick<Props, 'setSearch'>) {
|
|
311
|
-
const [show, setShow] = useState(
|
|
316
|
+
const [show, setShow] = useState(null);
|
|
312
317
|
const [price, setPrice] = useState({} as any);
|
|
313
318
|
const [display, setDisplay] = useState('');
|
|
314
319
|
const isSubscription = window.location.pathname.includes('subscriptions');
|
|
@@ -334,51 +339,53 @@ function SearchProducts({ setSearch }: Pick<Props, 'setSearch'>) {
|
|
|
334
339
|
}, [price]);
|
|
335
340
|
|
|
336
341
|
return (
|
|
337
|
-
<
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
342
|
+
<section>
|
|
343
|
+
<Button
|
|
344
|
+
className="option-btn"
|
|
345
|
+
variant="text"
|
|
346
|
+
onClick={(e) => {
|
|
347
|
+
setShow(e.currentTarget as any);
|
|
348
|
+
}}>
|
|
349
|
+
{display ? (
|
|
350
|
+
<Close
|
|
351
|
+
sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
|
|
352
|
+
onClick={(e) => {
|
|
353
|
+
e.stopPropagation();
|
|
354
|
+
setSearch({
|
|
355
|
+
q: '',
|
|
356
|
+
});
|
|
357
|
+
setDisplay('');
|
|
358
|
+
setShow(null);
|
|
359
|
+
}}
|
|
360
|
+
/>
|
|
361
|
+
) : (
|
|
362
|
+
<Add sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }} />
|
|
363
|
+
)}
|
|
364
|
+
{t('admin.subscription.product')}
|
|
365
|
+
<span>{display}</span>
|
|
366
|
+
</Button>
|
|
367
|
+
<Menu
|
|
368
|
+
anchorEl={show}
|
|
369
|
+
open={Boolean(show)}
|
|
370
|
+
className="status-options"
|
|
371
|
+
sx={{ height: 300 }}
|
|
372
|
+
onClose={(e: any) => {
|
|
373
|
+
e.stopPropagation();
|
|
374
|
+
setShow(null);
|
|
375
|
+
}}>
|
|
376
|
+
<ProductsProvider>
|
|
377
|
+
<ProductSelect
|
|
378
|
+
mode="inline"
|
|
379
|
+
hasSelected={() => false}
|
|
380
|
+
onSelect={(p: any) => {
|
|
381
|
+
setPrice(p);
|
|
382
|
+
setDisplay(`${p.productName}(${p.displayPrice})`);
|
|
383
|
+
setShow(null);
|
|
384
|
+
}}
|
|
385
|
+
/>
|
|
386
|
+
</ProductsProvider>
|
|
387
|
+
</Menu>
|
|
388
|
+
</section>
|
|
382
389
|
);
|
|
383
390
|
}
|
|
384
391
|
|
|
@@ -458,12 +465,13 @@ const Root = styled(Box)`
|
|
|
458
465
|
color: #555;
|
|
459
466
|
font-size: 14px;
|
|
460
467
|
line-height: 14px;
|
|
461
|
-
overflow:
|
|
468
|
+
overflow: visible;
|
|
462
469
|
}
|
|
463
470
|
|
|
464
471
|
.option-btn span {
|
|
465
472
|
color: #3773f2;
|
|
466
473
|
padding: 0 3px;
|
|
474
|
+
overflow: visible;
|
|
467
475
|
}
|
|
468
476
|
|
|
469
477
|
.status-options {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getWordBreakStyle } from '@blocklet/payment-react';
|
|
1
2
|
import { Avatar, Stack, SxProps, Typography } from '@mui/material';
|
|
2
3
|
import type { LiteralUnion } from 'type-fest';
|
|
3
4
|
|
|
@@ -22,7 +23,11 @@ export default function InfoCard(props: Props) {
|
|
|
22
23
|
{props.name.slice(0, 1)}
|
|
23
24
|
</Avatar>
|
|
24
25
|
)}
|
|
25
|
-
<Stack
|
|
26
|
+
<Stack
|
|
27
|
+
direction="column"
|
|
28
|
+
alignItems="flex-start"
|
|
29
|
+
justifyContent="space-around"
|
|
30
|
+
sx={{ wordBreak: getWordBreakStyle(props.name), minWidth: 140 }}>
|
|
26
31
|
<Typography variant="body1" color="text.primary">
|
|
27
32
|
{props.name}
|
|
28
33
|
</Typography>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
2
|
import { Box, Stack, SxProps } from '@mui/material';
|
|
3
3
|
import type { ReactNode } from 'react';
|
|
4
|
+
import { getWordBreakStyle } from '@blocklet/payment-react';
|
|
4
5
|
import { isEmptyExceptNumber } from '../libs/util';
|
|
5
6
|
|
|
6
7
|
type Props = {
|
|
@@ -49,7 +50,12 @@ export default function InfoRow(props: Props) {
|
|
|
49
50
|
}}>
|
|
50
51
|
{props.label}
|
|
51
52
|
</Box>
|
|
52
|
-
<Box
|
|
53
|
+
<Box
|
|
54
|
+
flex={sizes[1]}
|
|
55
|
+
color={isNone ? 'text.disabled' : 'text.secondary'}
|
|
56
|
+
className="info-row-value"
|
|
57
|
+
fontSize={14}
|
|
58
|
+
sx={{ wordBreak: getWordBreakStyle(props.value) }}>
|
|
53
59
|
{isNone ? t('common.none') : props.value}
|
|
54
60
|
</Box>
|
|
55
61
|
</Stack>
|
|
@@ -141,6 +141,7 @@ function RefundForm({ data, refundMaxAmount }: { data: TPaymentIntentExpanded; r
|
|
|
141
141
|
control={control}
|
|
142
142
|
rules={{
|
|
143
143
|
required: t('common.required'),
|
|
144
|
+
validate: (value) => value.trim() !== '' || t('common.required'),
|
|
144
145
|
}}
|
|
145
146
|
render={({ field }) => (
|
|
146
147
|
<TextField
|
|
@@ -205,7 +206,7 @@ export function PaymentIntentActionsInner({ data, variant, onChange }: Props) {
|
|
|
205
206
|
reset();
|
|
206
207
|
const curAmount = formatBNStr(res?.amount, data.paymentCurrency.decimal);
|
|
207
208
|
if (Number(curAmount) <= 0) {
|
|
208
|
-
Toast.info(t('admin.paymentIntent.
|
|
209
|
+
Toast.info(t('admin.paymentIntent.refundForm.empty'));
|
|
209
210
|
return;
|
|
210
211
|
}
|
|
211
212
|
setValue('refund.amount', curAmount);
|
|
@@ -12,7 +12,11 @@ import CreateProduct from '../product/create';
|
|
|
12
12
|
import LineItem from './item';
|
|
13
13
|
import ProductSelect from './product-select';
|
|
14
14
|
|
|
15
|
-
export default function BeforePay(
|
|
15
|
+
export default function BeforePay({
|
|
16
|
+
triggerError = () => {},
|
|
17
|
+
}: {
|
|
18
|
+
triggerError: (keys: { [key: string]: boolean }) => void;
|
|
19
|
+
}) {
|
|
16
20
|
const { t } = useLocaleContext();
|
|
17
21
|
const [params, setParams] = useSearchParams();
|
|
18
22
|
const { products, refresh } = useProductsContext();
|
|
@@ -69,6 +73,14 @@ export default function BeforePay() {
|
|
|
69
73
|
refresh();
|
|
70
74
|
};
|
|
71
75
|
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const hasDifferentCurrencyOrRecurring = items.fields.some((_, index) => {
|
|
78
|
+
const priceAlignResult = isPriceAligned(items.fields as any[], products, index);
|
|
79
|
+
return !priceAlignResult.currency || !priceAlignResult.recurring;
|
|
80
|
+
});
|
|
81
|
+
triggerError({ line_items: hasDifferentCurrencyOrRecurring });
|
|
82
|
+
}, [items.fields, products]);
|
|
83
|
+
|
|
72
84
|
return (
|
|
73
85
|
<Stack spacing={2} sx={{ width: '100%' }}>
|
|
74
86
|
<Typography variant="h6" sx={{ fontWeight: 600 }}>
|
|
@@ -18,9 +18,9 @@ const PricingTablePreview = forwardRef(({ id, version = 1 }: { id: string; versi
|
|
|
18
18
|
return (
|
|
19
19
|
<div ref={innerRef}>
|
|
20
20
|
{fullscreen ? (
|
|
21
|
-
<div style={{ width: '100%', height: '100%', background: '#fff'
|
|
21
|
+
<div style={{ width: '100%', height: '100%', background: '#fff' }}>
|
|
22
22
|
<IframeResizer
|
|
23
|
-
style={{ width: '100%', height: '100vh', overflow: 'hidden' }}
|
|
23
|
+
style={{ width: '100%', height: '100vh', overflow: 'hidden', border: 'none' }}
|
|
24
24
|
src={`${window.blocklet.prefix}checkout/pricing-table/${id}?preview=1&version=${version}`}
|
|
25
25
|
/>
|
|
26
26
|
</div>
|
|
@@ -11,7 +11,11 @@ import ProductSelect from '../payment-link/product-select';
|
|
|
11
11
|
import CreateProduct from '../product/create';
|
|
12
12
|
import ProductItem from './product-item';
|
|
13
13
|
|
|
14
|
-
export default function PricingTableProductSettings(
|
|
14
|
+
export default function PricingTableProductSettings({
|
|
15
|
+
triggerError = () => {},
|
|
16
|
+
}: {
|
|
17
|
+
triggerError: (keys: { [key: string]: boolean }) => void;
|
|
18
|
+
}) {
|
|
15
19
|
const { t } = useLocaleContext();
|
|
16
20
|
const [params, setParams] = useSearchParams();
|
|
17
21
|
const { products, refresh } = useProductsContext();
|
|
@@ -98,6 +102,13 @@ export default function PricingTableProductSettings() {
|
|
|
98
102
|
|
|
99
103
|
const grouped = groupPricingTableItems(items.fields);
|
|
100
104
|
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
const hasDifferentCurrency = items.fields.some(
|
|
107
|
+
(_, index) => !isPriceCurrencyAligned(items.fields as any[], products, index)
|
|
108
|
+
);
|
|
109
|
+
triggerError({ items: hasDifferentCurrency });
|
|
110
|
+
}, [items.fields, products]);
|
|
111
|
+
|
|
101
112
|
return (
|
|
102
113
|
<Stack spacing={2} alignItems="flex-start">
|
|
103
114
|
<Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
+
import Empty from '@arcblock/ux/lib/Empty';
|
|
3
4
|
import {
|
|
4
5
|
Status,
|
|
5
6
|
api,
|
|
@@ -29,11 +30,21 @@ type Props = {
|
|
|
29
30
|
status: string;
|
|
30
31
|
onChange?: (action?: string) => any | Promise<any>;
|
|
31
32
|
onClickSubscription: (subscription: TSubscriptionExpanded) => void | Promise<void>;
|
|
33
|
+
onlyActive?: boolean;
|
|
34
|
+
changeActive?: (active: boolean) => void;
|
|
32
35
|
} & Omit<StackProps, 'onChange'>;
|
|
33
36
|
|
|
34
37
|
const pageSize = 4;
|
|
35
38
|
|
|
36
|
-
export default function CurrentSubscriptions({
|
|
39
|
+
export default function CurrentSubscriptions({
|
|
40
|
+
id,
|
|
41
|
+
status,
|
|
42
|
+
onChange,
|
|
43
|
+
onClickSubscription,
|
|
44
|
+
onlyActive,
|
|
45
|
+
changeActive = () => {},
|
|
46
|
+
...rest
|
|
47
|
+
}: Props) {
|
|
37
48
|
const { t } = useLocaleContext();
|
|
38
49
|
const { isMobile } = useMobile();
|
|
39
50
|
|
|
@@ -184,30 +195,43 @@ export default function CurrentSubscriptions({ id, status, onChange, onClickSubs
|
|
|
184
195
|
</Stack>
|
|
185
196
|
);
|
|
186
197
|
})}
|
|
198
|
+
<Box>
|
|
199
|
+
{hasMore && (
|
|
200
|
+
<Button variant="text" type="button" color="inherit" onClick={loadMore} disabled={loadingMore}>
|
|
201
|
+
{loadingMore
|
|
202
|
+
? t('common.loadingMore', { resource: t('admin.subscriptions') })
|
|
203
|
+
: t('common.loadMore', { resource: t('admin.subscriptions') })}
|
|
204
|
+
</Button>
|
|
205
|
+
)}
|
|
206
|
+
{!hasMore && data.count > pageSize && (
|
|
207
|
+
<Typography color="text.secondary">
|
|
208
|
+
{t('common.noMore', { resource: t('admin.subscriptions') })}
|
|
209
|
+
</Typography>
|
|
210
|
+
)}
|
|
211
|
+
</Box>
|
|
187
212
|
</>
|
|
188
213
|
) : (
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
214
|
+
<Empty>
|
|
215
|
+
{onlyActive ? (
|
|
216
|
+
<Box sx={{ textAlign: 'center' }}>
|
|
217
|
+
<Typography>{t('admin.subscription.noActiveEmpty')}</Typography>
|
|
218
|
+
{changeActive && (
|
|
219
|
+
<Button onClick={() => changeActive(false)} variant="text" sx={{ color: 'text.link' }}>
|
|
220
|
+
{t('admin.subscription.viewAll')}
|
|
221
|
+
</Button>
|
|
222
|
+
)}
|
|
223
|
+
</Box>
|
|
224
|
+
) : (
|
|
225
|
+
t('admin.subscription.empty')
|
|
226
|
+
)}
|
|
227
|
+
</Empty>
|
|
193
228
|
)}
|
|
194
|
-
|
|
195
|
-
<Box>
|
|
196
|
-
{hasMore && (
|
|
197
|
-
<Button variant="text" type="button" color="inherit" onClick={loadMore} disabled={loadingMore}>
|
|
198
|
-
{loadingMore
|
|
199
|
-
? t('common.loadingMore', { resource: t('admin.subscriptions') })
|
|
200
|
-
: t('common.loadMore', { resource: t('admin.subscriptions') })}
|
|
201
|
-
</Button>
|
|
202
|
-
)}
|
|
203
|
-
{!hasMore && data.count > pageSize && (
|
|
204
|
-
<Typography color="text.secondary">{t('common.noMore', { resource: t('admin.subscriptions') })}</Typography>
|
|
205
|
-
)}
|
|
206
|
-
</Box>
|
|
207
229
|
</Stack>
|
|
208
230
|
);
|
|
209
231
|
}
|
|
210
232
|
|
|
211
233
|
CurrentSubscriptions.defaultProps = {
|
|
212
234
|
onChange: null,
|
|
235
|
+
onlyActive: false,
|
|
236
|
+
changeActive: null,
|
|
213
237
|
};
|
|
@@ -27,7 +27,9 @@ export default function Uploader({ onUploaded, preview, maxFileSize, maxNumberOf
|
|
|
27
27
|
useEffect(() => {
|
|
28
28
|
if (uploaderRef.current) {
|
|
29
29
|
const uploader = uploaderRef.current.getUploader();
|
|
30
|
-
uploader.onUploadSuccess((result: any) =>
|
|
30
|
+
uploader.onUploadSuccess((result: any) => {
|
|
31
|
+
onUploaded({ url: result.response.data.url });
|
|
32
|
+
});
|
|
31
33
|
}
|
|
32
34
|
}, [onUploaded]);
|
|
33
35
|
|
package/src/locales/en.tsx
CHANGED
|
@@ -422,6 +422,8 @@ export default flat({
|
|
|
422
422
|
view: 'View subscription',
|
|
423
423
|
name: 'Subscription',
|
|
424
424
|
empty: 'No subscription',
|
|
425
|
+
viewAll: 'View all subscriptions',
|
|
426
|
+
noActiveEmpty: 'You currently have no active subscriptions. You can choose to view your subscription history.',
|
|
425
427
|
attention: 'Past due subscriptions',
|
|
426
428
|
product: 'Product',
|
|
427
429
|
collectionMethod: 'Billing',
|
|
@@ -513,6 +515,7 @@ export default flat({
|
|
|
513
515
|
spent: 'Spent',
|
|
514
516
|
due: 'Due',
|
|
515
517
|
stake: 'Stake',
|
|
518
|
+
stats: 'Stats',
|
|
516
519
|
balance: 'Balance',
|
|
517
520
|
},
|
|
518
521
|
address: {
|
package/src/locales/zh.tsx
CHANGED
|
@@ -414,6 +414,8 @@ export default flat({
|
|
|
414
414
|
view: '查看订阅',
|
|
415
415
|
name: '订阅',
|
|
416
416
|
empty: '没有订阅',
|
|
417
|
+
viewAll: '查看历史订阅',
|
|
418
|
+
noActiveEmpty: '您当前没有服务中的订阅,您可以选择查看历史订阅',
|
|
417
419
|
product: '产品',
|
|
418
420
|
attention: '将过期的订阅',
|
|
419
421
|
collectionMethod: '计费',
|
|
@@ -504,6 +506,7 @@ export default flat({
|
|
|
504
506
|
spent: '花费金额',
|
|
505
507
|
due: '欠款金额',
|
|
506
508
|
stake: '质押金额',
|
|
509
|
+
stats: '统计',
|
|
507
510
|
balance: '余额',
|
|
508
511
|
},
|
|
509
512
|
address: {
|
|
@@ -11,6 +11,7 @@ import { FormProvider, useForm } from 'react-hook-form';
|
|
|
11
11
|
import { useSearchParams } from 'react-router-dom';
|
|
12
12
|
import { dispatch } from 'use-bus';
|
|
13
13
|
|
|
14
|
+
import { useSetState } from 'ahooks';
|
|
14
15
|
import DrawerForm from '../../../../components/drawer-form';
|
|
15
16
|
import AfterPay from '../../../../components/payment-link/after-pay';
|
|
16
17
|
import BeforePay from '../../../../components/payment-link/before-pay';
|
|
@@ -30,6 +31,7 @@ export default function CreatePaymentLink() {
|
|
|
30
31
|
const [stashed, setStashed] = useState(0);
|
|
31
32
|
const { settings } = usePaymentContext();
|
|
32
33
|
const fullScreenRef = useRef(null);
|
|
34
|
+
const [errors, triggerError] = useSetState({});
|
|
33
35
|
|
|
34
36
|
const methods = useForm<PaymentLink>({
|
|
35
37
|
shouldUnregister: false,
|
|
@@ -92,7 +94,11 @@ export default function CreatePaymentLink() {
|
|
|
92
94
|
}, [JSON.stringify(changes)]);
|
|
93
95
|
|
|
94
96
|
const tabs = [
|
|
95
|
-
{
|
|
97
|
+
{
|
|
98
|
+
label: t('admin.paymentLink.beforePay'),
|
|
99
|
+
value: 'beforePay',
|
|
100
|
+
component: BeforePay,
|
|
101
|
+
},
|
|
96
102
|
{ label: t('admin.paymentLink.afterPay'), value: 'afterPay', component: AfterPay },
|
|
97
103
|
];
|
|
98
104
|
const TabComponent = tabs.find((x) => x.value === current)?.component || BeforePay;
|
|
@@ -112,6 +118,9 @@ export default function CreatePaymentLink() {
|
|
|
112
118
|
return;
|
|
113
119
|
}
|
|
114
120
|
|
|
121
|
+
if (Object.values(errors).some((v) => v)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
115
124
|
api
|
|
116
125
|
.post('/api/payment-links', data)
|
|
117
126
|
.then(() => {
|
|
@@ -152,7 +161,7 @@ export default function CreatePaymentLink() {
|
|
|
152
161
|
scrollButtons="auto"
|
|
153
162
|
/>
|
|
154
163
|
<ProductsProvider>
|
|
155
|
-
<TabComponent />
|
|
164
|
+
<TabComponent triggerError={triggerError} />
|
|
156
165
|
</ProductsProvider>
|
|
157
166
|
</Stack>
|
|
158
167
|
<Stack flex={1} sx={{ maxWidth: 680 }}>
|
|
@@ -81,7 +81,7 @@ export default function PriceActions({ data, onChange, variant, setAsDefault }:
|
|
|
81
81
|
try {
|
|
82
82
|
setState({ loading: true });
|
|
83
83
|
await api.put(`/api/products/${data.product_id}`, { default_price_id: data.id }).then((res) => res.data);
|
|
84
|
-
Toast.success(t('common.
|
|
84
|
+
Toast.success(t('common.saveAsDefaultPriceSuccess'));
|
|
85
85
|
onChange(state.action);
|
|
86
86
|
} catch (err) {
|
|
87
87
|
console.error(err);
|
|
@@ -135,7 +135,7 @@ export default function PriceActions({ data, onChange, variant, setAsDefault }:
|
|
|
135
135
|
{ label: t('admin.pricingTable.add'), handler: onCreatePricingTable, color: 'primary', disabled: !data.recurring },
|
|
136
136
|
];
|
|
137
137
|
|
|
138
|
-
if (setAsDefault) {
|
|
138
|
+
if (!setAsDefault) {
|
|
139
139
|
actions.splice(4, 0, {
|
|
140
140
|
label: t('admin.price.setAsDefault'),
|
|
141
141
|
handler: onSetAsDefault,
|
|
@@ -10,8 +10,9 @@ import { FormProvider, useForm } from 'react-hook-form';
|
|
|
10
10
|
import { useSearchParams } from 'react-router-dom';
|
|
11
11
|
import { dispatch } from 'use-bus';
|
|
12
12
|
|
|
13
|
+
import { useSetState } from 'ahooks';
|
|
13
14
|
import DrawerForm from '../../../../components/drawer-form';
|
|
14
|
-
import PricingTableCustomerSettings from '../../../../components/pricing-table/customer-settings';
|
|
15
|
+
// import PricingTableCustomerSettings from '../../../../components/pricing-table/customer-settings';
|
|
15
16
|
import PricingTablePaymentSettings from '../../../../components/pricing-table/payment-settings';
|
|
16
17
|
import PricingTablePreview from '../../../../components/pricing-table/preview';
|
|
17
18
|
import PricingTableProductSettings from '../../../../components/pricing-table/product-settings';
|
|
@@ -25,6 +26,7 @@ export default function CreatePricingTable() {
|
|
|
25
26
|
const [step, setStep] = useState(0); // ['products', 'payment', 'portal']
|
|
26
27
|
const [stashed, setStashed] = useState(0);
|
|
27
28
|
const fullScreenRef = useRef(null);
|
|
29
|
+
const [errors, triggerError] = useSetState({});
|
|
28
30
|
|
|
29
31
|
const methods = useForm<TPricingTable & any>({
|
|
30
32
|
shouldUnregister: false,
|
|
@@ -64,6 +66,7 @@ export default function CreatePricingTable() {
|
|
|
64
66
|
.then(() => {
|
|
65
67
|
Toast.success(t('admin.pricingTable.saved'));
|
|
66
68
|
methods.reset();
|
|
69
|
+
setStep(0);
|
|
67
70
|
dispatch('drawer.submitted');
|
|
68
71
|
dispatch('pricingTable.created');
|
|
69
72
|
})
|
|
@@ -80,7 +83,10 @@ export default function CreatePricingTable() {
|
|
|
80
83
|
};
|
|
81
84
|
|
|
82
85
|
const onContinue = () => {
|
|
83
|
-
if (
|
|
86
|
+
if (Object.values(errors).some((v) => v)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (step < 1) {
|
|
84
90
|
setStep(step + 1);
|
|
85
91
|
} else {
|
|
86
92
|
methods.handleSubmit(async (formData: any) => {
|
|
@@ -107,9 +113,9 @@ export default function CreatePricingTable() {
|
|
|
107
113
|
<Box flex={2} sx={{ borderRight: '1px solid #eee' }} position="relative">
|
|
108
114
|
<Stack height="100%" spacing={2}>
|
|
109
115
|
<Box overflow="auto" sx={{ pr: 2 }}>
|
|
110
|
-
{step === 0 && <PricingTableProductSettings />}
|
|
116
|
+
{step === 0 && <PricingTableProductSettings triggerError={triggerError} />}
|
|
111
117
|
{step === 1 && <PricingTablePaymentSettings />}
|
|
112
|
-
{step === 2 && <PricingTableCustomerSettings />}
|
|
118
|
+
{/* {step === 2 && <PricingTableCustomerSettings />} */}
|
|
113
119
|
</Box>
|
|
114
120
|
<Stack
|
|
115
121
|
padding={2}
|
|
@@ -124,7 +130,7 @@ export default function CreatePricingTable() {
|
|
|
124
130
|
{t('common.previous')}
|
|
125
131
|
</Button>
|
|
126
132
|
<Button variant="contained" color="primary" onClick={onContinue}>
|
|
127
|
-
{step ===
|
|
133
|
+
{step === 1 ? t('common.save') : t('common.continue')}
|
|
128
134
|
</Button>
|
|
129
135
|
</Stack>
|
|
130
136
|
</Stack>
|
|
@@ -187,6 +187,8 @@ export default function CustomerHome() {
|
|
|
187
187
|
<Box className="section-body">
|
|
188
188
|
<CurrentSubscriptions
|
|
189
189
|
id={data.id}
|
|
190
|
+
onlyActive={state.onlyActive}
|
|
191
|
+
changeActive={(v) => setState({ onlyActive: v })}
|
|
190
192
|
status={state.onlyActive ? 'active,trialing' : 'active,trialing,paused,past_due,canceled'}
|
|
191
193
|
style={{
|
|
192
194
|
cursor: 'pointer',
|
|
@@ -203,7 +205,7 @@ export default function CustomerHome() {
|
|
|
203
205
|
const SummaryCard = (
|
|
204
206
|
<Box className="base-card section section-summary">
|
|
205
207
|
<Box className="section-header">
|
|
206
|
-
<Typography variant="h3">{t('admin.customer.summary.
|
|
208
|
+
<Typography variant="h3">{t('admin.customer.summary.stats')}</Typography>
|
|
207
209
|
<FormControl
|
|
208
210
|
sx={{
|
|
209
211
|
'.MuiInputBase-root': {
|
|
@@ -222,31 +224,41 @@ export default function CustomerHome() {
|
|
|
222
224
|
inputProps={{ 'aria-label': 'Without label' }}>
|
|
223
225
|
{currencies.map((c) => (
|
|
224
226
|
<MenuItem key={c.id} value={c.id}>
|
|
225
|
-
{
|
|
227
|
+
<Box alignItems="center" display="flex" gap={1}>
|
|
228
|
+
<Avatar src={c?.logo} alt={c?.name} sx={{ width: 18, height: 18 }} />
|
|
229
|
+
<Typography
|
|
230
|
+
variant="h5"
|
|
231
|
+
component="div"
|
|
232
|
+
sx={{ fontSize: '16px', color: 'text.primary', fontWeight: '500' }}>
|
|
233
|
+
{c?.name}
|
|
234
|
+
</Typography>
|
|
235
|
+
<Typography sx={{ fontSize: 12 }} color="text.lighter">
|
|
236
|
+
{c?.symbol}
|
|
237
|
+
</Typography>
|
|
238
|
+
</Box>
|
|
239
|
+
{/* {c.symbol} */}
|
|
226
240
|
</MenuItem>
|
|
227
241
|
))}
|
|
228
242
|
</Select>
|
|
229
243
|
</FormControl>
|
|
230
244
|
</Box>
|
|
231
|
-
<Box alignItems="center" display="flex" gap={1}>
|
|
232
|
-
<Avatar src={currency?.logo} alt={currency?.name} sx={{ width: 18, height: 18 }} />
|
|
233
|
-
<Typography variant="h5" component="div" sx={{ fontSize: '16px', color: 'text.primary', fontWeight: '500' }}>
|
|
234
|
-
{currency?.name}
|
|
235
|
-
</Typography>
|
|
236
|
-
<Typography sx={{ fontSize: 12 }} color="text.lighter">
|
|
237
|
-
{currency?.symbol}
|
|
238
|
-
</Typography>
|
|
239
|
-
</Box>
|
|
240
|
-
<Typography variant="h3" color="text.primary" gutterBottom>
|
|
241
|
-
{formatBNStr(data.summary.token?.[currency?.id], currency.decimal, 6, false)}
|
|
242
|
-
</Typography>
|
|
243
245
|
<Stack
|
|
244
246
|
className="section-body"
|
|
245
247
|
sx={{
|
|
246
248
|
display: 'grid',
|
|
247
249
|
gridTemplateColumns: '1fr 1fr',
|
|
248
250
|
gap: 2,
|
|
251
|
+
mt: 1.5,
|
|
249
252
|
}}>
|
|
253
|
+
<CurrencyCard
|
|
254
|
+
label={t('admin.customer.summary.balance')}
|
|
255
|
+
data={data.summary.token || {}}
|
|
256
|
+
currency={currency}
|
|
257
|
+
sx={{
|
|
258
|
+
background: 'var(--tags-tag-orange-bg, #B7FEE3)',
|
|
259
|
+
color: 'var(--tags-tag-orange-text, #007C52)',
|
|
260
|
+
}}
|
|
261
|
+
/>
|
|
250
262
|
<CurrencyCard
|
|
251
263
|
label={t('admin.customer.summary.spent')}
|
|
252
264
|
data={data.summary.paid || {}}
|
|
@@ -285,7 +285,7 @@ export default function CustomerInvoiceDetail() {
|
|
|
285
285
|
<Divider />
|
|
286
286
|
<Box className="section">
|
|
287
287
|
<Typography variant="h3" mb={1.5} className="section-header">
|
|
288
|
-
{t('payment.customer.
|
|
288
|
+
{t('payment.customer.products')}
|
|
289
289
|
</Typography>
|
|
290
290
|
<InvoiceTable invoice={data} simple />
|
|
291
291
|
</Box>
|
|
@@ -66,6 +66,8 @@ const RefundTable = memo(({ invoice_id }: Props) => {
|
|
|
66
66
|
{
|
|
67
67
|
label: t('common.amount'),
|
|
68
68
|
name: 'id',
|
|
69
|
+
width: 80,
|
|
70
|
+
align: 'right',
|
|
69
71
|
options: {
|
|
70
72
|
customBodyRenderLite: (_: string, index: number) => {
|
|
71
73
|
const item = data.list[index] as TRefundExpanded;
|
package/src/pages/home.tsx
CHANGED
|
@@ -17,7 +17,7 @@ function Home() {
|
|
|
17
17
|
/>
|
|
18
18
|
<Stack alignItems="center" justifyContent="center" sx={{ height: '60vh', width: '100vw' }}>
|
|
19
19
|
<Stack maxWidth="sm" direction="column" alignItems="center" spacing={3}>
|
|
20
|
-
<Avatar src={window.blocklet.appLogo} sx={{ width: 80, height: 80 }} />
|
|
20
|
+
<Avatar src={window.blocklet.appLogo} sx={{ width: 80, height: 80 }} variant="square" />
|
|
21
21
|
<Stack direction="column" alignItems="center" spacing={1}>
|
|
22
22
|
<Typography variant="h4">Payment Kit</Typography>
|
|
23
23
|
<Typography variant="h5" color="text.secondary" fontWeight="normal">
|