@blocklet/payment-react 1.19.0 → 1.19.2
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/es/components/blockchain/tx.d.ts +1 -1
- package/es/components/blockchain/tx.js +9 -11
- package/es/components/country-select.d.ts +1 -1
- package/es/components/date-range-picker.d.ts +13 -0
- package/es/components/date-range-picker.js +279 -0
- package/es/components/input.d.ts +5 -2
- package/es/components/input.js +6 -2
- package/es/components/label.d.ts +7 -0
- package/es/components/label.js +50 -0
- package/es/components/loading-button.d.ts +1 -1
- package/es/history/credit/grants-list.d.ts +14 -0
- package/es/history/credit/grants-list.js +215 -0
- package/es/history/credit/transactions-list.d.ts +13 -0
- package/es/history/credit/transactions-list.js +254 -0
- package/es/history/invoice/list.js +21 -1
- package/es/index.d.ts +5 -1
- package/es/index.js +10 -1
- package/es/libs/util.d.ts +2 -0
- package/es/libs/util.js +12 -0
- package/es/locales/en.js +20 -2
- package/es/locales/zh.js +20 -2
- package/es/payment/form/address.js +2 -1
- package/es/payment/form/index.js +46 -7
- package/es/payment/index.js +18 -3
- package/es/payment/product-item.d.ts +8 -1
- package/es/payment/product-item.js +137 -5
- package/es/payment/summary.d.ts +3 -1
- package/es/payment/summary.js +9 -0
- package/lib/components/blockchain/tx.d.ts +1 -1
- package/lib/components/blockchain/tx.js +9 -8
- package/lib/components/country-select.d.ts +1 -1
- package/lib/components/date-range-picker.d.ts +13 -0
- package/lib/components/date-range-picker.js +329 -0
- package/lib/components/input.d.ts +5 -2
- package/lib/components/input.js +8 -4
- package/lib/components/label.d.ts +7 -0
- package/lib/components/label.js +62 -0
- package/lib/components/loading-button.d.ts +1 -1
- package/lib/history/credit/grants-list.d.ts +14 -0
- package/lib/history/credit/grants-list.js +277 -0
- package/lib/history/credit/transactions-list.d.ts +13 -0
- package/lib/history/credit/transactions-list.js +300 -0
- package/lib/history/invoice/list.js +24 -0
- package/lib/index.d.ts +5 -1
- package/lib/index.js +39 -0
- package/lib/libs/util.d.ts +2 -0
- package/lib/libs/util.js +14 -0
- package/lib/locales/en.js +20 -2
- package/lib/locales/zh.js +20 -2
- package/lib/payment/form/address.js +6 -5
- package/lib/payment/form/index.js +49 -9
- package/lib/payment/index.js +20 -2
- package/lib/payment/product-item.d.ts +8 -1
- package/lib/payment/product-item.js +144 -4
- package/lib/payment/summary.d.ts +3 -1
- package/lib/payment/summary.js +9 -0
- package/package.json +3 -3
- package/src/components/blockchain/tx.tsx +9 -15
- package/src/components/country-select.tsx +2 -2
- package/src/components/date-range-picker.tsx +310 -0
- package/src/components/input.tsx +14 -3
- package/src/components/label.tsx +59 -0
- package/src/components/loading-button.tsx +1 -1
- package/src/history/credit/grants-list.tsx +276 -0
- package/src/history/credit/transactions-list.tsx +316 -0
- package/src/history/invoice/list.tsx +18 -1
- package/src/index.ts +9 -0
- package/src/libs/util.ts +14 -0
- package/src/locales/en.tsx +20 -0
- package/src/locales/zh.tsx +19 -0
- package/src/payment/form/address.tsx +4 -3
- package/src/payment/form/index.tsx +112 -53
- package/src/payment/index.tsx +17 -1
- package/src/payment/product-item.tsx +152 -4
- package/src/payment/summary.tsx +13 -2
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
2
|
import type { PriceRecurring, TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
|
|
3
|
-
import { Box, Stack, Typography } from '@mui/material';
|
|
3
|
+
import { Box, Stack, Typography, IconButton, TextField, Alert } from '@mui/material';
|
|
4
|
+
import { Add, Remove } from '@mui/icons-material';
|
|
4
5
|
|
|
5
|
-
import React, { useMemo } from 'react';
|
|
6
|
+
import React, { useMemo, useState } from 'react';
|
|
6
7
|
import Status from '../components/status';
|
|
7
8
|
import Switch from '../components/switch-button';
|
|
8
9
|
import {
|
|
10
|
+
findCurrency,
|
|
9
11
|
formatLineItemPricing,
|
|
10
12
|
formatPrice,
|
|
11
13
|
formatQuantityInventory,
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
} from '../libs/util';
|
|
15
17
|
import ProductCard from './product-card';
|
|
16
18
|
import dayjs from '../libs/dayjs';
|
|
19
|
+
import { usePaymentContext } from '../contexts/payment';
|
|
17
20
|
|
|
18
21
|
type Props = {
|
|
19
22
|
item: TLineItemExpanded;
|
|
@@ -25,6 +28,14 @@ type Props = {
|
|
|
25
28
|
onDownsell: Function;
|
|
26
29
|
mode?: 'normal' | 'cross-sell';
|
|
27
30
|
children?: React.ReactNode;
|
|
31
|
+
// 数量调整相关
|
|
32
|
+
adjustableQuantity?: {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
minimum?: number;
|
|
35
|
+
maximum?: number;
|
|
36
|
+
};
|
|
37
|
+
onQuantityChange?: (itemId: string, quantity: number) => void;
|
|
38
|
+
completed?: boolean;
|
|
28
39
|
};
|
|
29
40
|
|
|
30
41
|
export default function ProductItem({
|
|
@@ -37,12 +48,90 @@ export default function ProductItem({
|
|
|
37
48
|
children = null,
|
|
38
49
|
onUpsell,
|
|
39
50
|
onDownsell,
|
|
51
|
+
completed = false,
|
|
52
|
+
adjustableQuantity = { enabled: false },
|
|
53
|
+
onQuantityChange = () => {},
|
|
40
54
|
}: Props) {
|
|
41
55
|
const { t, locale } = useLocaleContext();
|
|
56
|
+
const { settings } = usePaymentContext();
|
|
42
57
|
const pricing = formatLineItemPricing(item, currency, { trialEnd, trialInDays }, locale);
|
|
43
58
|
const saving = formatUpsellSaving(items, currency);
|
|
44
59
|
const metered = item.price?.recurring?.usage_type === 'metered' ? t('common.metered') : '';
|
|
45
60
|
const canUpsell = mode === 'normal' && items.length === 1;
|
|
61
|
+
|
|
62
|
+
const isCreditProduct = item.price.product?.type === 'credit' && item.price.metadata?.credit_config?.credit_amount;
|
|
63
|
+
const creditAmount = isCreditProduct ? Number(item.price.metadata.credit_config.credit_amount) : 0;
|
|
64
|
+
const creditCurrency = isCreditProduct
|
|
65
|
+
? findCurrency(settings.paymentMethods, item.price.metadata?.credit_config?.currency_id ?? '')
|
|
66
|
+
: null;
|
|
67
|
+
const validDuration = item.price.metadata?.credit_config?.valid_duration_value;
|
|
68
|
+
const validDurationUnit = item.price.metadata?.credit_config?.valid_duration_unit || 'days';
|
|
69
|
+
|
|
70
|
+
const [localQuantity, setLocalQuantity] = useState(item.quantity);
|
|
71
|
+
const canAdjustQuantity = adjustableQuantity.enabled && mode === 'normal';
|
|
72
|
+
const minQuantity = Math.max(adjustableQuantity.minimum || 1, 1);
|
|
73
|
+
const quantityAvailable = Math.min(item.price.quantity_limit_per_checkout, item.price.quantity_available);
|
|
74
|
+
const maxQuantity = Math.min(adjustableQuantity.maximum || 999, quantityAvailable || 999);
|
|
75
|
+
|
|
76
|
+
const handleQuantityChange = (newQuantity: number) => {
|
|
77
|
+
if (newQuantity >= minQuantity && newQuantity <= maxQuantity) {
|
|
78
|
+
setLocalQuantity(newQuantity);
|
|
79
|
+
if (formatQuantityInventory(item.price, newQuantity, locale)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
onQuantityChange(item.price_id, newQuantity);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleQuantityIncrease = () => {
|
|
87
|
+
if (localQuantity < maxQuantity) {
|
|
88
|
+
handleQuantityChange(localQuantity + 1);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleQuantityDecrease = () => {
|
|
93
|
+
if (localQuantity > minQuantity) {
|
|
94
|
+
handleQuantityChange(localQuantity - 1);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleQuantityInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
99
|
+
const value = parseInt(event.target.value, 10);
|
|
100
|
+
if (!Number.isNaN(value)) {
|
|
101
|
+
handleQuantityChange(value);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Credit 信息格式化
|
|
106
|
+
const formatCreditInfo = () => {
|
|
107
|
+
if (!isCreditProduct) return null;
|
|
108
|
+
|
|
109
|
+
const isRecurring = item.price.type === 'recurring';
|
|
110
|
+
const totalCredit = creditAmount * localQuantity;
|
|
111
|
+
|
|
112
|
+
let message = '';
|
|
113
|
+
if (isRecurring) {
|
|
114
|
+
message = t('payment.checkout.credit.recurringInfo', {
|
|
115
|
+
amount: totalCredit,
|
|
116
|
+
period: formatRecurring(item.price.recurring!, true, 'per', locale),
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
message = t('payment.checkout.credit.oneTimeInfo', {
|
|
120
|
+
amount: totalCredit,
|
|
121
|
+
symbol: creditCurrency?.symbol || 'Credits',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (validDuration && validDuration > 0) {
|
|
126
|
+
message += `,${t('payment.checkout.credit.expiresIn', {
|
|
127
|
+
duration: validDuration,
|
|
128
|
+
unit: t(`common.${validDurationUnit}`),
|
|
129
|
+
})}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return message;
|
|
133
|
+
};
|
|
134
|
+
|
|
46
135
|
const primaryText = useMemo(() => {
|
|
47
136
|
const price = item.upsell_price || item.price || {};
|
|
48
137
|
const isRecurring = price?.type === 'recurring' && price?.recurring;
|
|
@@ -53,6 +142,8 @@ export default function ProductItem({
|
|
|
53
142
|
return pricing.primary;
|
|
54
143
|
}, [trialInDays, trialEnd, pricing, item, locale]);
|
|
55
144
|
|
|
145
|
+
const quantityInventoryError = formatQuantityInventory(item.price, localQuantity, locale);
|
|
146
|
+
|
|
56
147
|
return (
|
|
57
148
|
<Stack
|
|
58
149
|
direction="column"
|
|
@@ -108,9 +199,9 @@ export default function ProductItem({
|
|
|
108
199
|
)}
|
|
109
200
|
</Stack>
|
|
110
201
|
</Stack>
|
|
111
|
-
{
|
|
202
|
+
{quantityInventoryError ? (
|
|
112
203
|
<Status
|
|
113
|
-
label={
|
|
204
|
+
label={quantityInventoryError}
|
|
114
205
|
variant="outlined"
|
|
115
206
|
sx={{
|
|
116
207
|
mt: 1,
|
|
@@ -120,6 +211,63 @@ export default function ProductItem({
|
|
|
120
211
|
}}
|
|
121
212
|
/>
|
|
122
213
|
) : null}
|
|
214
|
+
|
|
215
|
+
{/* 数量调整器 */}
|
|
216
|
+
{canAdjustQuantity && !completed && (
|
|
217
|
+
<Box sx={{ mt: 1, p: 1 }}>
|
|
218
|
+
<Stack
|
|
219
|
+
direction="row"
|
|
220
|
+
spacing={1}
|
|
221
|
+
sx={{
|
|
222
|
+
alignItems: 'center',
|
|
223
|
+
}}>
|
|
224
|
+
<Typography
|
|
225
|
+
variant="body2"
|
|
226
|
+
sx={{
|
|
227
|
+
color: 'text.secondary',
|
|
228
|
+
minWidth: 'fit-content',
|
|
229
|
+
}}>
|
|
230
|
+
{t('common.quantity')}:
|
|
231
|
+
</Typography>
|
|
232
|
+
<IconButton
|
|
233
|
+
size="small"
|
|
234
|
+
onClick={handleQuantityDecrease}
|
|
235
|
+
disabled={localQuantity <= minQuantity}
|
|
236
|
+
sx={{ minWidth: 32, width: 32, height: 32 }}>
|
|
237
|
+
<Remove fontSize="small" />
|
|
238
|
+
</IconButton>
|
|
239
|
+
<TextField
|
|
240
|
+
size="small"
|
|
241
|
+
value={localQuantity}
|
|
242
|
+
onChange={handleQuantityInputChange}
|
|
243
|
+
sx={{ width: 60 }}
|
|
244
|
+
type="number"
|
|
245
|
+
slotProps={{
|
|
246
|
+
htmlInput: {
|
|
247
|
+
min: minQuantity,
|
|
248
|
+
max: maxQuantity,
|
|
249
|
+
style: { textAlign: 'center', padding: '4px' },
|
|
250
|
+
},
|
|
251
|
+
}}
|
|
252
|
+
/>
|
|
253
|
+
<IconButton
|
|
254
|
+
size="small"
|
|
255
|
+
onClick={handleQuantityIncrease}
|
|
256
|
+
disabled={localQuantity >= maxQuantity}
|
|
257
|
+
sx={{ minWidth: 32, width: 32, height: 32 }}>
|
|
258
|
+
<Add fontSize="small" />
|
|
259
|
+
</IconButton>
|
|
260
|
+
</Stack>
|
|
261
|
+
</Box>
|
|
262
|
+
)}
|
|
263
|
+
|
|
264
|
+
{/* Credit 信息展示 */}
|
|
265
|
+
{isCreditProduct && (
|
|
266
|
+
<Alert severity="info" sx={{ mt: 1, fontSize: '0.875rem' }} icon={false}>
|
|
267
|
+
{formatCreditInfo()}
|
|
268
|
+
</Alert>
|
|
269
|
+
)}
|
|
270
|
+
|
|
123
271
|
{children}
|
|
124
272
|
</Stack>
|
|
125
273
|
{canUpsell && !item.upsell_price_id && item.price.upsell?.upsells_to && (
|
package/src/payment/summary.tsx
CHANGED
|
@@ -62,6 +62,7 @@ type Props = {
|
|
|
62
62
|
showStaking?: boolean;
|
|
63
63
|
onUpsell?: Function;
|
|
64
64
|
onDownsell?: Function;
|
|
65
|
+
onQuantityChange?: Function;
|
|
65
66
|
onChangeAmount?: Function;
|
|
66
67
|
onApplyCrossSell?: Function;
|
|
67
68
|
onCancelCrossSell?: Function;
|
|
@@ -69,6 +70,7 @@ type Props = {
|
|
|
69
70
|
crossSellBehavior?: string;
|
|
70
71
|
donationSettings?: DonationSettings; // only include backend part
|
|
71
72
|
action?: string;
|
|
73
|
+
completed?: boolean;
|
|
72
74
|
};
|
|
73
75
|
|
|
74
76
|
async function fetchCrossSell(id: string) {
|
|
@@ -118,7 +120,6 @@ function getStakingSetup(items: TLineItemExpanded[], currency: TPaymentCurrency,
|
|
|
118
120
|
|
|
119
121
|
return '0';
|
|
120
122
|
}
|
|
121
|
-
|
|
122
123
|
export default function PaymentSummary({
|
|
123
124
|
items,
|
|
124
125
|
currency,
|
|
@@ -126,6 +127,7 @@ export default function PaymentSummary({
|
|
|
126
127
|
billingThreshold,
|
|
127
128
|
onUpsell = noop,
|
|
128
129
|
onDownsell = noop,
|
|
130
|
+
onQuantityChange = noop,
|
|
129
131
|
onApplyCrossSell = noop,
|
|
130
132
|
onCancelCrossSell = noop,
|
|
131
133
|
onChangeAmount = noop,
|
|
@@ -135,6 +137,7 @@ export default function PaymentSummary({
|
|
|
135
137
|
donationSettings = undefined,
|
|
136
138
|
action = '',
|
|
137
139
|
trialEnd = 0,
|
|
140
|
+
completed = false,
|
|
138
141
|
...rest
|
|
139
142
|
}: Props) {
|
|
140
143
|
const { t, locale } = useLocaleContext();
|
|
@@ -167,6 +170,11 @@ export default function PaymentSummary({
|
|
|
167
170
|
runAsync();
|
|
168
171
|
};
|
|
169
172
|
|
|
173
|
+
const handleQuantityChange = async (itemId: string, quantity: number) => {
|
|
174
|
+
await onQuantityChange!(itemId, quantity);
|
|
175
|
+
runAsync();
|
|
176
|
+
};
|
|
177
|
+
|
|
170
178
|
const handleDownsell = async (from: string) => {
|
|
171
179
|
await onDownsell!(from);
|
|
172
180
|
runAsync();
|
|
@@ -222,7 +230,10 @@ export default function PaymentSummary({
|
|
|
222
230
|
trialEnd={trialEnd}
|
|
223
231
|
currency={currency}
|
|
224
232
|
onUpsell={handleUpsell}
|
|
225
|
-
onDownsell={handleDownsell}
|
|
233
|
+
onDownsell={handleDownsell}
|
|
234
|
+
adjustableQuantity={x.adjustable_quantity}
|
|
235
|
+
completed={completed}
|
|
236
|
+
onQuantityChange={handleQuantityChange}>
|
|
226
237
|
{x.cross_sell && (
|
|
227
238
|
<Stack
|
|
228
239
|
direction="row"
|