@blocklet/payment-react 1.13.301 → 1.13.303
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/checkout/donate.js +6 -3
- package/es/libs/util.d.ts +1 -0
- package/es/libs/util.js +15 -0
- package/es/locales/en.js +3 -1
- package/es/locales/zh.js +3 -1
- package/es/payment/form/index.js +20 -3
- package/es/payment/product-item.js +16 -3
- package/lib/checkout/donate.js +15 -3
- package/lib/libs/util.d.ts +1 -0
- package/lib/libs/util.js +16 -0
- package/lib/locales/en.js +3 -1
- package/lib/locales/zh.js +3 -1
- package/lib/payment/form/index.js +11 -1
- package/lib/payment/product-item.js +13 -3
- package/package.json +3 -3
- package/src/checkout/donate.tsx +32 -21
- package/src/libs/util.ts +16 -0
- package/src/locales/en.tsx +2 -0
- package/src/locales/zh.tsx +2 -0
- package/src/payment/form/index.tsx +20 -3
- package/src/payment/product-item.tsx +21 -5
package/es/checkout/donate.js
CHANGED
|
@@ -47,6 +47,9 @@ const fetchSupporters = (target, livemode = true) => {
|
|
|
47
47
|
}
|
|
48
48
|
return supporterCache[target];
|
|
49
49
|
};
|
|
50
|
+
const emojiFont = {
|
|
51
|
+
fontFamily: 'Avenir, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"'
|
|
52
|
+
};
|
|
50
53
|
function SupporterAvatar({ supporters = [], totalAmount = "0", currency, method }) {
|
|
51
54
|
const { t } = useLocaleContext();
|
|
52
55
|
const customers = uniqBy(supporters, "customer_did");
|
|
@@ -68,7 +71,7 @@ function SupporterAvatar({ supporters = [], totalAmount = "0", currency, method
|
|
|
68
71
|
sm: 1
|
|
69
72
|
},
|
|
70
73
|
children: [
|
|
71
|
-
/* @__PURE__ */ jsx(Typography, { component: "p", color: "text.secondary", children: customersNum === 0 ? t("payment.checkout.donation.empty") : t("payment.checkout.donation.summary", {
|
|
74
|
+
/* @__PURE__ */ jsx(Typography, { component: "p", color: "text.secondary", children: customersNum === 0 ? /* @__PURE__ */ jsx("span", { style: emojiFont, children: t("payment.checkout.donation.empty") }) : t("payment.checkout.donation.summary", {
|
|
72
75
|
total: customersNum,
|
|
73
76
|
symbol: currency.symbol,
|
|
74
77
|
totalAmount: formatAmount(totalAmount || "0", currency.decimal)
|
|
@@ -92,7 +95,7 @@ function SupporterTable({ supporters = [], totalAmount = "0", currency, method }
|
|
|
92
95
|
const customers = uniqBy(supporters, "customer_did");
|
|
93
96
|
const customersNum = customers.length;
|
|
94
97
|
return /* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", alignItems: "center", gap: { xs: 0.5, sm: 1 }, children: [
|
|
95
|
-
/* @__PURE__ */ jsx(Typography, { component: "p", color: "text.secondary", children: customersNum === 0 ? t("payment.checkout.donation.empty") : t("payment.checkout.donation.summary", {
|
|
98
|
+
/* @__PURE__ */ jsx(Typography, { component: "p", color: "text.secondary", children: customersNum === 0 ? /* @__PURE__ */ jsx("span", { style: emojiFont, children: t("payment.checkout.donation.empty") }) : t("payment.checkout.donation.summary", {
|
|
96
99
|
total: customersNum,
|
|
97
100
|
symbol: currency.symbol,
|
|
98
101
|
totalAmount: formatAmount(totalAmount || "0", currency.decimal)
|
|
@@ -150,7 +153,7 @@ function SupporterSimple({ supporters = [], totalAmount = "0", currency, method
|
|
|
150
153
|
sm: 1
|
|
151
154
|
},
|
|
152
155
|
children: [
|
|
153
|
-
/* @__PURE__ */ jsx(Typography, { component: "p", color: "text.secondary", children: customersNum === 0 ? t("payment.checkout.donation.empty") : t("payment.checkout.donation.summary", {
|
|
156
|
+
/* @__PURE__ */ jsx(Typography, { component: "p", color: "text.secondary", children: customersNum === 0 ? /* @__PURE__ */ jsx("span", { style: emojiFont, children: t("payment.checkout.donation.empty") }) : t("payment.checkout.donation.summary", {
|
|
154
157
|
total: customersNum,
|
|
155
158
|
symbol: currency.symbol,
|
|
156
159
|
totalAmount: formatAmount(totalAmount || "0", currency.decimal)
|
package/es/libs/util.d.ts
CHANGED
package/es/libs/util.js
CHANGED
|
@@ -672,3 +672,18 @@ export function formatTotalPrice({
|
|
|
672
672
|
totalAmount: unitValue.mul(new BN(quantity)).toString()
|
|
673
673
|
};
|
|
674
674
|
}
|
|
675
|
+
export function formatQuantityInventory(price, quantity, locale = "en") {
|
|
676
|
+
const q = Number(quantity);
|
|
677
|
+
const {
|
|
678
|
+
quantity_available: quantityAvailable = 0,
|
|
679
|
+
quantity_sold: quantitySold = 0,
|
|
680
|
+
quantity_limit_per_checkout: quantityLimitPerCheckout = 0
|
|
681
|
+
} = price || {};
|
|
682
|
+
if (quantityAvailable > 0 && quantitySold + q > quantityAvailable) {
|
|
683
|
+
return t("common.quantityNotEnough", locale);
|
|
684
|
+
}
|
|
685
|
+
if (quantityLimitPerCheckout > 0 && quantityLimitPerCheckout < q) {
|
|
686
|
+
return t("common.quantityLimitPerCheckout", locale);
|
|
687
|
+
}
|
|
688
|
+
return "";
|
|
689
|
+
}
|
package/es/locales/en.js
CHANGED
|
@@ -82,7 +82,9 @@ export default flat({
|
|
|
82
82
|
months: "months",
|
|
83
83
|
years: "years",
|
|
84
84
|
type: "type",
|
|
85
|
-
donation: "Donation"
|
|
85
|
+
donation: "Donation",
|
|
86
|
+
quantityLimitPerCheckout: "Exceed purchase limit",
|
|
87
|
+
quantityNotEnough: "Exceed inventory"
|
|
86
88
|
},
|
|
87
89
|
payment: {
|
|
88
90
|
checkout: {
|
package/es/locales/zh.js
CHANGED
|
@@ -82,7 +82,9 @@ export default flat({
|
|
|
82
82
|
months: "\u6708",
|
|
83
83
|
years: "\u5E74",
|
|
84
84
|
type: "\u7C7B\u578B",
|
|
85
|
-
donation: "\u6253\u8D4F"
|
|
85
|
+
donation: "\u6253\u8D4F",
|
|
86
|
+
quantityLimitPerCheckout: "\u8D85\u51FA\u8D2D\u4E70\u9650\u5236",
|
|
87
|
+
quantityNotEnough: "\u5E93\u5B58\u4E0D\u8DB3"
|
|
86
88
|
},
|
|
87
89
|
payment: {
|
|
88
90
|
checkout: {
|
package/es/payment/form/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { Fade, InputAdornment, Stack, Typography } from "@mui/material";
|
|
|
8
8
|
import { useCreation, useMemoizedFn, useSetState, useSize } from "ahooks";
|
|
9
9
|
import { PhoneNumberUtil } from "google-libphonenumber";
|
|
10
10
|
import pWaitFor from "p-wait-for";
|
|
11
|
-
import { useEffect, useState } from "react";
|
|
11
|
+
import { useEffect, useMemo, useState } from "react";
|
|
12
12
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
|
13
13
|
import { joinURL } from "ufo";
|
|
14
14
|
import { dispatch } from "use-bus";
|
|
@@ -18,7 +18,14 @@ import FormInput from "../../components/input.js";
|
|
|
18
18
|
import { usePaymentContext } from "../../contexts/payment.js";
|
|
19
19
|
import { useSubscription } from "../../hooks/subscription.js";
|
|
20
20
|
import api from "../../libs/api.js";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
flattenPaymentMethods,
|
|
23
|
+
formatError,
|
|
24
|
+
formatQuantityInventory,
|
|
25
|
+
getPrefix,
|
|
26
|
+
getQueryParams,
|
|
27
|
+
getStatementDescriptor
|
|
28
|
+
} from "../../libs/util.js";
|
|
22
29
|
import UserButtons from "./addon.js";
|
|
23
30
|
import AddressForm from "./address.js";
|
|
24
31
|
import CurrencySelector from "./currency.js";
|
|
@@ -61,6 +68,16 @@ export default function PaymentForm({
|
|
|
61
68
|
const { session, connect } = usePaymentContext();
|
|
62
69
|
const subscription = useSubscription("events");
|
|
63
70
|
const { control, getValues, setValue, handleSubmit } = useFormContext();
|
|
71
|
+
const quantityInventoryStatus = useMemo(() => {
|
|
72
|
+
let status = true;
|
|
73
|
+
for (const item of checkoutSession.line_items) {
|
|
74
|
+
if (formatQuantityInventory(item.price, item.quantity)) {
|
|
75
|
+
status = false;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return status;
|
|
80
|
+
}, [checkoutSession]);
|
|
64
81
|
const [state, setState] = useSetState({
|
|
65
82
|
submitting: false,
|
|
66
83
|
paying: false,
|
|
@@ -386,7 +403,7 @@ export default function PaymentForm({
|
|
|
386
403
|
className: "cko-submit-button",
|
|
387
404
|
onClick: onAction,
|
|
388
405
|
fullWidth: true,
|
|
389
|
-
disabled: state.submitting || state.paying || state.stripePaying,
|
|
406
|
+
disabled: state.submitting || state.paying || state.stripePaying || !quantityInventoryStatus,
|
|
390
407
|
loading: state.submitting || state.paying,
|
|
391
408
|
children: state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
|
|
392
409
|
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
3
|
-
import { Stack, Typography } from "@mui/material";
|
|
3
|
+
import { Box, Stack, Typography } from "@mui/material";
|
|
4
4
|
import Status from "../components/status.js";
|
|
5
5
|
import Switch from "../components/switch-button.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
formatLineItemPricing,
|
|
8
|
+
formatPrice,
|
|
9
|
+
formatQuantityInventory,
|
|
10
|
+
formatRecurring,
|
|
11
|
+
formatUpsellSaving
|
|
12
|
+
} from "../libs/util.js";
|
|
7
13
|
import ProductCard from "./product-card.js";
|
|
8
14
|
ProductItem.defaultProps = {
|
|
9
15
|
mode: "normal",
|
|
@@ -41,7 +47,14 @@ export default function ProductItem({
|
|
|
41
47
|
logo: item.price.product?.images[0],
|
|
42
48
|
name: item.price.product?.name,
|
|
43
49
|
description: item.price.product?.description,
|
|
44
|
-
extra:
|
|
50
|
+
extra: /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", children: [
|
|
51
|
+
item.price.type === "recurring" && item.price.recurring ? [pricing.quantity, t("common.billed", { rule: `${formatRecurring(item.upsell_price?.recurring || item.price.recurring, true, "per", locale)} ${metered}` })].filter(Boolean).join(", ") : pricing.quantity,
|
|
52
|
+
formatQuantityInventory(item.price, item.quantity, locale) ? /* @__PURE__ */ jsxs(Typography, { sx: { fontSize: "0.85rem", color: "red" }, children: [
|
|
53
|
+
"\uFF08",
|
|
54
|
+
formatQuantityInventory(item.price, item.quantity, locale),
|
|
55
|
+
"\uFF09"
|
|
56
|
+
] }) : ""
|
|
57
|
+
] })
|
|
45
58
|
}
|
|
46
59
|
),
|
|
47
60
|
/* @__PURE__ */ jsxs(Stack, { direction: "column", alignItems: "flex-end", flex: 1, children: [
|
package/lib/checkout/donate.js
CHANGED
|
@@ -44,6 +44,9 @@ const fetchSupporters = (target, livemode = true) => {
|
|
|
44
44
|
}
|
|
45
45
|
return supporterCache[target];
|
|
46
46
|
};
|
|
47
|
+
const emojiFont = {
|
|
48
|
+
fontFamily: 'Avenir, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"'
|
|
49
|
+
};
|
|
47
50
|
function SupporterAvatar({
|
|
48
51
|
supporters = [],
|
|
49
52
|
totalAmount = "0",
|
|
@@ -72,7 +75,10 @@ function SupporterAvatar({
|
|
|
72
75
|
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
73
76
|
component: "p",
|
|
74
77
|
color: "text.secondary",
|
|
75
|
-
children: customersNum === 0 ?
|
|
78
|
+
children: customersNum === 0 ? /* @__PURE__ */(0, _jsxRuntime.jsx)("span", {
|
|
79
|
+
style: emojiFont,
|
|
80
|
+
children: t("payment.checkout.donation.empty")
|
|
81
|
+
}) : t("payment.checkout.donation.summary", {
|
|
76
82
|
total: customersNum,
|
|
77
83
|
symbol: currency.symbol,
|
|
78
84
|
totalAmount: (0, _util.formatAmount)(totalAmount || "0", currency.decimal)
|
|
@@ -114,7 +120,10 @@ function SupporterTable({
|
|
|
114
120
|
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
115
121
|
component: "p",
|
|
116
122
|
color: "text.secondary",
|
|
117
|
-
children: customersNum === 0 ?
|
|
123
|
+
children: customersNum === 0 ? /* @__PURE__ */(0, _jsxRuntime.jsx)("span", {
|
|
124
|
+
style: emojiFont,
|
|
125
|
+
children: t("payment.checkout.donation.empty")
|
|
126
|
+
}) : t("payment.checkout.donation.summary", {
|
|
118
127
|
total: customersNum,
|
|
119
128
|
symbol: currency.symbol,
|
|
120
129
|
totalAmount: (0, _util.formatAmount)(totalAmount || "0", currency.decimal)
|
|
@@ -216,7 +225,10 @@ function SupporterSimple({
|
|
|
216
225
|
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
217
226
|
component: "p",
|
|
218
227
|
color: "text.secondary",
|
|
219
|
-
children: customersNum === 0 ?
|
|
228
|
+
children: customersNum === 0 ? /* @__PURE__ */(0, _jsxRuntime.jsx)("span", {
|
|
229
|
+
style: emojiFont,
|
|
230
|
+
children: t("payment.checkout.donation.empty")
|
|
231
|
+
}) : t("payment.checkout.donation.summary", {
|
|
220
232
|
total: customersNum,
|
|
221
233
|
symbol: currency.symbol,
|
|
222
234
|
totalAmount: (0, _util.formatAmount)(totalAmount || "0", currency.decimal)
|
package/lib/libs/util.d.ts
CHANGED
package/lib/libs/util.js
CHANGED
|
@@ -15,6 +15,7 @@ exports.formatLineItemPricing = formatLineItemPricing;
|
|
|
15
15
|
exports.formatLocale = void 0;
|
|
16
16
|
exports.formatNumber = formatNumber;
|
|
17
17
|
exports.formatPriceAmount = exports.formatPrice = exports.formatPrettyMsLocale = void 0;
|
|
18
|
+
exports.formatQuantityInventory = formatQuantityInventory;
|
|
18
19
|
exports.formatRecurring = formatRecurring;
|
|
19
20
|
exports.formatSubscriptionProduct = formatSubscriptionProduct;
|
|
20
21
|
exports.formatTime = formatTime;
|
|
@@ -805,4 +806,19 @@ function formatTotalPrice({
|
|
|
805
806
|
}),
|
|
806
807
|
totalAmount: unitValue.mul(new _util.BN(quantity)).toString()
|
|
807
808
|
};
|
|
809
|
+
}
|
|
810
|
+
function formatQuantityInventory(price, quantity, locale = "en") {
|
|
811
|
+
const q = Number(quantity);
|
|
812
|
+
const {
|
|
813
|
+
quantity_available: quantityAvailable = 0,
|
|
814
|
+
quantity_sold: quantitySold = 0,
|
|
815
|
+
quantity_limit_per_checkout: quantityLimitPerCheckout = 0
|
|
816
|
+
} = price || {};
|
|
817
|
+
if (quantityAvailable > 0 && quantitySold + q > quantityAvailable) {
|
|
818
|
+
return (0, _locales.t)("common.quantityNotEnough", locale);
|
|
819
|
+
}
|
|
820
|
+
if (quantityLimitPerCheckout > 0 && quantityLimitPerCheckout < q) {
|
|
821
|
+
return (0, _locales.t)("common.quantityLimitPerCheckout", locale);
|
|
822
|
+
}
|
|
823
|
+
return "";
|
|
808
824
|
}
|
package/lib/locales/en.js
CHANGED
|
@@ -89,7 +89,9 @@ module.exports = (0, _flat.default)({
|
|
|
89
89
|
months: "months",
|
|
90
90
|
years: "years",
|
|
91
91
|
type: "type",
|
|
92
|
-
donation: "Donation"
|
|
92
|
+
donation: "Donation",
|
|
93
|
+
quantityLimitPerCheckout: "Exceed purchase limit",
|
|
94
|
+
quantityNotEnough: "Exceed inventory"
|
|
93
95
|
},
|
|
94
96
|
payment: {
|
|
95
97
|
checkout: {
|
package/lib/locales/zh.js
CHANGED
|
@@ -89,7 +89,9 @@ module.exports = (0, _flat.default)({
|
|
|
89
89
|
months: "\u6708",
|
|
90
90
|
years: "\u5E74",
|
|
91
91
|
type: "\u7C7B\u578B",
|
|
92
|
-
donation: "\u6253\u8D4F"
|
|
92
|
+
donation: "\u6253\u8D4F",
|
|
93
|
+
quantityLimitPerCheckout: "\u8D85\u51FA\u8D2D\u4E70\u9650\u5236",
|
|
94
|
+
quantityNotEnough: "\u5E93\u5B58\u4E0D\u8DB3"
|
|
93
95
|
},
|
|
94
96
|
payment: {
|
|
95
97
|
checkout: {
|
|
@@ -80,6 +80,16 @@ function PaymentForm({
|
|
|
80
80
|
setValue,
|
|
81
81
|
handleSubmit
|
|
82
82
|
} = (0, _reactHookForm.useFormContext)();
|
|
83
|
+
const quantityInventoryStatus = (0, _react.useMemo)(() => {
|
|
84
|
+
let status = true;
|
|
85
|
+
for (const item of checkoutSession.line_items) {
|
|
86
|
+
if ((0, _util.formatQuantityInventory)(item.price, item.quantity)) {
|
|
87
|
+
status = false;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return status;
|
|
92
|
+
}, [checkoutSession]);
|
|
83
93
|
const [state, setState] = (0, _ahooks.useSetState)({
|
|
84
94
|
submitting: false,
|
|
85
95
|
paying: false,
|
|
@@ -472,7 +482,7 @@ function PaymentForm({
|
|
|
472
482
|
className: "cko-submit-button",
|
|
473
483
|
onClick: onAction,
|
|
474
484
|
fullWidth: true,
|
|
475
|
-
disabled: state.submitting || state.paying || state.stripePaying,
|
|
485
|
+
disabled: state.submitting || state.paying || state.stripePaying || !quantityInventoryStatus,
|
|
476
486
|
loading: state.submitting || state.paying,
|
|
477
487
|
children: state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
|
|
478
488
|
}), ["subscription", "setup"].includes(checkoutSession.mode) && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
@@ -59,9 +59,19 @@ function ProductItem({
|
|
|
59
59
|
logo: item.price.product?.images[0],
|
|
60
60
|
name: item.price.product?.name,
|
|
61
61
|
description: item.price.product?.description,
|
|
62
|
-
extra:
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
extra: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
|
|
63
|
+
display: "flex",
|
|
64
|
+
alignItems: "center",
|
|
65
|
+
children: [item.price.type === "recurring" && item.price.recurring ? [pricing.quantity, t("common.billed", {
|
|
66
|
+
rule: `${(0, _util.formatRecurring)(item.upsell_price?.recurring || item.price.recurring, true, "per", locale)} ${metered}`
|
|
67
|
+
})].filter(Boolean).join(", ") : pricing.quantity, (0, _util.formatQuantityInventory)(item.price, item.quantity, locale) ? /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
|
|
68
|
+
sx: {
|
|
69
|
+
fontSize: "0.85rem",
|
|
70
|
+
color: "red"
|
|
71
|
+
},
|
|
72
|
+
children: ["\uFF08", (0, _util.formatQuantityInventory)(item.price, item.quantity, locale), "\uFF09"]
|
|
73
|
+
}) : ""]
|
|
74
|
+
})
|
|
65
75
|
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
66
76
|
direction: "column",
|
|
67
77
|
alignItems: "flex-end",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/payment-react",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.303",
|
|
4
4
|
"description": "Reusable react components for payment kit v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"@babel/core": "^7.24.7",
|
|
92
92
|
"@babel/preset-env": "^7.24.7",
|
|
93
93
|
"@babel/preset-react": "^7.24.7",
|
|
94
|
-
"@blocklet/payment-types": "1.13.
|
|
94
|
+
"@blocklet/payment-types": "1.13.303",
|
|
95
95
|
"@storybook/addon-essentials": "^7.6.19",
|
|
96
96
|
"@storybook/addon-interactions": "^7.6.19",
|
|
97
97
|
"@storybook/addon-links": "^7.6.19",
|
|
@@ -120,5 +120,5 @@
|
|
|
120
120
|
"vite-plugin-babel": "^1.2.0",
|
|
121
121
|
"vite-plugin-node-polyfills": "^0.21.0"
|
|
122
122
|
},
|
|
123
|
-
"gitHead": "
|
|
123
|
+
"gitHead": "d1275a66f2a2ab2e6adc5854818c7a9dbb5a153a"
|
|
124
124
|
}
|
package/src/checkout/donate.tsx
CHANGED
|
@@ -92,6 +92,11 @@ const fetchSupporters = (target: string, livemode: boolean = true): Promise<Dona
|
|
|
92
92
|
return supporterCache[target];
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
+
const emojiFont = {
|
|
96
|
+
fontFamily:
|
|
97
|
+
'Avenir, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
|
|
98
|
+
};
|
|
99
|
+
|
|
95
100
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
96
101
|
function SupporterAvatar({ supporters = [], totalAmount = '0', currency, method }: DonateHistory) {
|
|
97
102
|
const { t } = useLocaleContext();
|
|
@@ -113,13 +118,15 @@ function SupporterAvatar({ supporters = [], totalAmount = '0', currency, method
|
|
|
113
118
|
sm: 1,
|
|
114
119
|
}}>
|
|
115
120
|
<Typography component="p" color="text.secondary">
|
|
116
|
-
{customersNum === 0
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
{customersNum === 0 ? (
|
|
122
|
+
<span style={emojiFont}>{t('payment.checkout.donation.empty')}</span>
|
|
123
|
+
) : (
|
|
124
|
+
t('payment.checkout.donation.summary', {
|
|
125
|
+
total: customersNum,
|
|
126
|
+
symbol: currency.symbol,
|
|
127
|
+
totalAmount: formatAmount(totalAmount || '0', currency.decimal),
|
|
128
|
+
})
|
|
129
|
+
)}
|
|
123
130
|
</Typography>
|
|
124
131
|
<AvatarGroup total={customersNum} max={20}>
|
|
125
132
|
{customers.map((x) => (
|
|
@@ -144,13 +151,15 @@ function SupporterTable({ supporters = [], totalAmount = '0', currency, method }
|
|
|
144
151
|
return (
|
|
145
152
|
<Box display="flex" flexDirection="column" alignItems="center" gap={{ xs: 0.5, sm: 1 }}>
|
|
146
153
|
<Typography component="p" color="text.secondary">
|
|
147
|
-
{customersNum === 0
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
+
{customersNum === 0 ? (
|
|
155
|
+
<span style={emojiFont}>{t('payment.checkout.donation.empty')}</span>
|
|
156
|
+
) : (
|
|
157
|
+
t('payment.checkout.donation.summary', {
|
|
158
|
+
total: customersNum,
|
|
159
|
+
symbol: currency.symbol,
|
|
160
|
+
totalAmount: formatAmount(totalAmount || '0', currency.decimal),
|
|
161
|
+
})
|
|
162
|
+
)}
|
|
154
163
|
</Typography>
|
|
155
164
|
<Table size="small" sx={{ width: '100%', overflow: 'hidden' }}>
|
|
156
165
|
<TableBody>
|
|
@@ -220,13 +229,15 @@ function SupporterSimple({ supporters = [], totalAmount = '0', currency, method
|
|
|
220
229
|
sm: 1,
|
|
221
230
|
}}>
|
|
222
231
|
<Typography component="p" color="text.secondary">
|
|
223
|
-
{customersNum === 0
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
{customersNum === 0 ? (
|
|
233
|
+
<span style={emojiFont}>{t('payment.checkout.donation.empty')}</span>
|
|
234
|
+
) : (
|
|
235
|
+
t('payment.checkout.donation.summary', {
|
|
236
|
+
total: customersNum,
|
|
237
|
+
symbol: currency.symbol,
|
|
238
|
+
totalAmount: formatAmount(totalAmount || '0', currency.decimal),
|
|
239
|
+
})
|
|
240
|
+
)}
|
|
230
241
|
</Typography>
|
|
231
242
|
<AvatarGroup total={customersNum} max={10}>
|
|
232
243
|
{customers.map((x) => (
|
package/src/libs/util.ts
CHANGED
|
@@ -872,3 +872,19 @@ export function formatTotalPrice({
|
|
|
872
872
|
totalAmount: unitValue.mul(new BN(quantity)).toString(),
|
|
873
873
|
};
|
|
874
874
|
}
|
|
875
|
+
|
|
876
|
+
export function formatQuantityInventory(price: TPrice, quantity: string | number, locale = 'en') {
|
|
877
|
+
const q = Number(quantity);
|
|
878
|
+
const {
|
|
879
|
+
quantity_available: quantityAvailable = 0,
|
|
880
|
+
quantity_sold: quantitySold = 0,
|
|
881
|
+
quantity_limit_per_checkout: quantityLimitPerCheckout = 0,
|
|
882
|
+
} = price || {};
|
|
883
|
+
if (quantityAvailable > 0 && quantitySold + q > quantityAvailable) {
|
|
884
|
+
return t('common.quantityNotEnough', locale);
|
|
885
|
+
}
|
|
886
|
+
if (quantityLimitPerCheckout > 0 && quantityLimitPerCheckout < q) {
|
|
887
|
+
return t('common.quantityLimitPerCheckout', locale);
|
|
888
|
+
}
|
|
889
|
+
return '';
|
|
890
|
+
}
|
package/src/locales/en.tsx
CHANGED
package/src/locales/zh.tsx
CHANGED
|
@@ -15,7 +15,7 @@ import { Fade, InputAdornment, Stack, Typography } from '@mui/material';
|
|
|
15
15
|
import { useCreation, useMemoizedFn, useSetState, useSize } from 'ahooks';
|
|
16
16
|
import { PhoneNumberUtil } from 'google-libphonenumber';
|
|
17
17
|
import pWaitFor from 'p-wait-for';
|
|
18
|
-
import { useEffect, useState } from 'react';
|
|
18
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
19
19
|
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
|
20
20
|
import { joinURL } from 'ufo';
|
|
21
21
|
import { dispatch } from 'use-bus';
|
|
@@ -26,7 +26,14 @@ import FormInput from '../../components/input';
|
|
|
26
26
|
import { usePaymentContext } from '../../contexts/payment';
|
|
27
27
|
import { useSubscription } from '../../hooks/subscription';
|
|
28
28
|
import api from '../../libs/api';
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
flattenPaymentMethods,
|
|
31
|
+
formatError,
|
|
32
|
+
formatQuantityInventory,
|
|
33
|
+
getPrefix,
|
|
34
|
+
getQueryParams,
|
|
35
|
+
getStatementDescriptor,
|
|
36
|
+
} from '../../libs/util';
|
|
30
37
|
import { CheckoutCallbacks, CheckoutContext } from '../../types';
|
|
31
38
|
import UserButtons from './addon';
|
|
32
39
|
import AddressForm from './address';
|
|
@@ -94,6 +101,16 @@ export default function PaymentForm({
|
|
|
94
101
|
const { session, connect } = usePaymentContext();
|
|
95
102
|
const subscription = useSubscription('events');
|
|
96
103
|
const { control, getValues, setValue, handleSubmit } = useFormContext();
|
|
104
|
+
const quantityInventoryStatus = useMemo(() => {
|
|
105
|
+
let status = true;
|
|
106
|
+
for (const item of checkoutSession.line_items) {
|
|
107
|
+
if (formatQuantityInventory(item.price, item.quantity)) {
|
|
108
|
+
status = false;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return status;
|
|
113
|
+
}, [checkoutSession]);
|
|
97
114
|
const [state, setState] = useSetState<{
|
|
98
115
|
submitting: boolean;
|
|
99
116
|
paying: boolean;
|
|
@@ -455,7 +472,7 @@ export default function PaymentForm({
|
|
|
455
472
|
className="cko-submit-button"
|
|
456
473
|
onClick={onAction}
|
|
457
474
|
fullWidth
|
|
458
|
-
disabled={state.submitting || state.paying || state.stripePaying}
|
|
475
|
+
disabled={state.submitting || state.paying || state.stripePaying || !quantityInventoryStatus}
|
|
459
476
|
loading={state.submitting || state.paying}>
|
|
460
477
|
{state.submitting || state.paying ? t('payment.checkout.processing') : buttonText}
|
|
461
478
|
</LoadingButton>
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
2
|
import type { PriceRecurring, TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
|
|
3
|
-
import { Stack, Typography } from '@mui/material';
|
|
3
|
+
import { Box, Stack, Typography } from '@mui/material';
|
|
4
4
|
|
|
5
5
|
import Status from '../components/status';
|
|
6
6
|
import Switch from '../components/switch-button';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
formatLineItemPricing,
|
|
9
|
+
formatPrice,
|
|
10
|
+
formatQuantityInventory,
|
|
11
|
+
formatRecurring,
|
|
12
|
+
formatUpsellSaving,
|
|
13
|
+
} from '../libs/util';
|
|
8
14
|
import ProductCard from './product-card';
|
|
9
15
|
|
|
10
16
|
type Props = {
|
|
@@ -52,9 +58,19 @@ export default function ProductItem({
|
|
|
52
58
|
name={item.price.product?.name}
|
|
53
59
|
description={item.price.product?.description}
|
|
54
60
|
extra={
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
<Box display="flex" alignItems="center">
|
|
62
|
+
{item.price.type === 'recurring' && item.price.recurring
|
|
63
|
+
? [pricing.quantity, t('common.billed', { rule: `${formatRecurring(item.upsell_price?.recurring || item.price.recurring, true, 'per', locale)} ${metered}` })].filter(Boolean).join(', ') // prettier-ignore
|
|
64
|
+
: pricing.quantity}
|
|
65
|
+
|
|
66
|
+
{formatQuantityInventory(item.price, item.quantity, locale) ? (
|
|
67
|
+
<Typography sx={{ fontSize: '0.85rem', color: 'red' }}>
|
|
68
|
+
({formatQuantityInventory(item.price, item.quantity, locale)})
|
|
69
|
+
</Typography>
|
|
70
|
+
) : (
|
|
71
|
+
''
|
|
72
|
+
)}
|
|
73
|
+
</Box>
|
|
58
74
|
}
|
|
59
75
|
/>
|
|
60
76
|
<Stack direction="column" alignItems="flex-end" flex={1}>
|