@blocklet/payment-react 1.26.1 → 1.26.3

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.
Files changed (73) hide show
  1. package/es/checkout-v2/components/dialogs/checkout-dialogs.js +2 -0
  2. package/es/checkout-v2/components/left/cross-sell-card.js +3 -3
  3. package/es/checkout-v2/components/left/product-item-card.js +9 -3
  4. package/es/checkout-v2/components/left/promotion-input.d.ts +4 -1
  5. package/es/checkout-v2/components/left/promotion-input.js +8 -13
  6. package/es/checkout-v2/components/right/customer-info-card.d.ts +2 -0
  7. package/es/checkout-v2/components/right/customer-info-card.js +22 -14
  8. package/es/checkout-v2/components/right/status-feedback.js +1 -1
  9. package/es/checkout-v2/components/right/submit-button.js +3 -1
  10. package/es/checkout-v2/layouts/checkout-layout.js +13 -3
  11. package/es/checkout-v2/panels/left/composite-panel.js +27 -6
  12. package/es/checkout-v2/panels/right/payment-panel.js +40 -9
  13. package/es/checkout-v2/utils/format.d.ts +1 -1
  14. package/es/checkout-v2/utils/format.js +1 -0
  15. package/es/checkout-v2/views/error-view.d.ts +1 -1
  16. package/es/checkout-v2/views/error-view.js +9 -0
  17. package/es/checkout-v2/views/success-view.js +3 -1
  18. package/es/components/over-due-invoice-payment.js +5 -3
  19. package/es/components/service-suspended-dialog.d.ts +4 -0
  20. package/es/components/service-suspended-dialog.js +61 -0
  21. package/es/libs/util.d.ts +8 -0
  22. package/es/libs/util.js +3 -0
  23. package/es/locales/en.js +4 -0
  24. package/es/locales/zh.js +4 -0
  25. package/es/payment/form/index.js +17 -0
  26. package/es/payment/index.js +15 -4
  27. package/lib/checkout-v2/components/dialogs/checkout-dialogs.js +4 -0
  28. package/lib/checkout-v2/components/left/cross-sell-card.js +2 -2
  29. package/lib/checkout-v2/components/left/product-item-card.js +9 -2
  30. package/lib/checkout-v2/components/left/promotion-input.d.ts +4 -1
  31. package/lib/checkout-v2/components/left/promotion-input.js +12 -19
  32. package/lib/checkout-v2/components/right/customer-info-card.d.ts +2 -0
  33. package/lib/checkout-v2/components/right/customer-info-card.js +19 -13
  34. package/lib/checkout-v2/components/right/status-feedback.js +1 -1
  35. package/lib/checkout-v2/components/right/submit-button.js +3 -1
  36. package/lib/checkout-v2/layouts/checkout-layout.js +28 -5
  37. package/lib/checkout-v2/panels/left/composite-panel.js +20 -5
  38. package/lib/checkout-v2/panels/right/payment-panel.js +46 -7
  39. package/lib/checkout-v2/utils/format.d.ts +1 -1
  40. package/lib/checkout-v2/utils/format.js +7 -0
  41. package/lib/checkout-v2/views/error-view.d.ts +1 -1
  42. package/lib/checkout-v2/views/error-view.js +9 -0
  43. package/lib/checkout-v2/views/success-view.js +2 -0
  44. package/lib/components/over-due-invoice-payment.js +12 -2
  45. package/lib/components/service-suspended-dialog.d.ts +4 -0
  46. package/lib/components/service-suspended-dialog.js +97 -0
  47. package/lib/libs/util.d.ts +8 -0
  48. package/lib/libs/util.js +4 -0
  49. package/lib/locales/en.js +4 -0
  50. package/lib/locales/zh.js +4 -0
  51. package/lib/payment/form/index.js +23 -0
  52. package/lib/payment/index.js +15 -4
  53. package/package.json +4 -4
  54. package/src/checkout-v2/components/dialogs/checkout-dialogs.tsx +4 -0
  55. package/src/checkout-v2/components/left/cross-sell-card.tsx +3 -3
  56. package/src/checkout-v2/components/left/product-item-card.tsx +18 -8
  57. package/src/checkout-v2/components/left/promotion-input.tsx +17 -17
  58. package/src/checkout-v2/components/right/customer-info-card.tsx +29 -16
  59. package/src/checkout-v2/components/right/status-feedback.tsx +2 -2
  60. package/src/checkout-v2/components/right/submit-button.tsx +2 -0
  61. package/src/checkout-v2/layouts/checkout-layout.tsx +25 -10
  62. package/src/checkout-v2/panels/left/composite-panel.tsx +28 -6
  63. package/src/checkout-v2/panels/right/payment-panel.tsx +32 -5
  64. package/src/checkout-v2/utils/format.ts +2 -0
  65. package/src/checkout-v2/views/error-view.tsx +11 -1
  66. package/src/checkout-v2/views/success-view.tsx +3 -1
  67. package/src/components/over-due-invoice-payment.tsx +6 -3
  68. package/src/components/service-suspended-dialog.tsx +64 -0
  69. package/src/libs/util.ts +7 -0
  70. package/src/locales/en.tsx +4 -0
  71. package/src/locales/zh.tsx +4 -0
  72. package/src/payment/form/index.tsx +20 -0
  73. package/src/payment/index.tsx +26 -4
@@ -1,4 +1,5 @@
1
1
  import type { TPaymentCurrency } from '@blocklet/payment-types';
2
+ export { primaryContrastColor } from '../../libs/util';
2
3
  export declare const INTERVAL_LOCALE_KEY: Record<string, string>;
3
4
  export declare function countryCodeToFlag(code: string): string;
4
5
  export declare function formatTokenAmount(unitAmount: string | number | bigint, currency: TPaymentCurrency | null): string;
@@ -56,4 +57,3 @@ interface ItemMeta {
56
57
  * Works for the "primary product" header above the item list.
57
58
  */
58
59
  export declare function getSessionHeaderMeta(t: TFn, session: any, product: any, items: any[]): ItemMeta;
59
- export {};
@@ -10,9 +10,16 @@ exports.formatTokenAmount = formatTokenAmount;
10
10
  exports.formatTrialText = formatTrialText;
11
11
  exports.getSessionHeaderMeta = getSessionHeaderMeta;
12
12
  exports.getUnitAmountForCurrency = getUnitAmountForCurrency;
13
+ Object.defineProperty(exports, "primaryContrastColor", {
14
+ enumerable: true,
15
+ get: function () {
16
+ return _util2.primaryContrastColor;
17
+ }
18
+ });
13
19
  exports.tSafe = tSafe;
14
20
  exports.whiteTooltipSx = void 0;
15
21
  var _util = require("@ocap/util");
22
+ var _util2 = require("../../libs/util");
16
23
  const INTERVAL_LOCALE_KEY = exports.INTERVAL_LOCALE_KEY = {
17
24
  day: "common.daily",
18
25
  week: "common.weekly",
@@ -1,6 +1,6 @@
1
1
  interface ErrorViewProps {
2
2
  error: string;
3
- errorCode?: 'SESSION_EXPIRED' | 'EMPTY_LINE_ITEMS' | null;
3
+ errorCode?: 'SESSION_EXPIRED' | 'EMPTY_LINE_ITEMS' | 'STOP_ACCEPTING_ORDERS' | null;
4
4
  mode?: string;
5
5
  }
6
6
  export default function ErrorView({ error, errorCode, mode }: ErrorViewProps): import("react").JSX.Element;
@@ -10,6 +10,7 @@ var _styles = require("@mui/material/styles");
10
10
  var _ArrowBack = _interopRequireDefault(require("@mui/icons-material/ArrowBack"));
11
11
  var _Header = _interopRequireDefault(require("@blocklet/ui-react/lib/Header"));
12
12
  var _context = require("@arcblock/ux/lib/Locale/context");
13
+ var _format = require("../utils/format");
13
14
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
15
  function GeometricDecoration() {
15
16
  const theme = (0, _styles.useTheme)();
@@ -149,6 +150,13 @@ function getErrorConfig(errorCode, error, t) {
149
150
  color: "#94a3b8"
150
151
  };
151
152
  }
153
+ if (errorCode === "STOP_ACCEPTING_ORDERS") {
154
+ return {
155
+ title: t("payment.checkout.stopAcceptingOrders.title"),
156
+ description: t("payment.checkout.stopAcceptingOrders.description"),
157
+ color: "#f59e0b"
158
+ };
159
+ }
152
160
  return {
153
161
  title: t("payment.checkout.error.title"),
154
162
  description: error,
@@ -223,6 +231,7 @@ function ErrorContent({
223
231
  fontWeight: 600,
224
232
  fontSize: 16,
225
233
  letterSpacing: "0.02em",
234
+ color: th => (0, _format.primaryContrastColor)(th),
226
235
  boxShadow: `0 8px 32px -4px ${(0, _styles.alpha)(primaryColor, 0.3)}`,
227
236
  "&:hover": {
228
237
  boxShadow: `0 12px 40px -4px ${(0, _styles.alpha)(primaryColor, 0.4)}`,
@@ -537,6 +537,7 @@ function SubscriptionLinks({
537
537
  md: 17
538
538
  },
539
539
  letterSpacing: "0.02em",
540
+ color: theme => (0, _format.primaryContrastColor)(theme),
540
541
  boxShadow: "0 8px 24px -4px rgba(59,130,246,0.25)",
541
542
  "&:hover": {
542
543
  boxShadow: "0 12px 28px -4px rgba(59,130,246,0.35)"
@@ -577,6 +578,7 @@ function InvoiceLink({
577
578
  md: 17
578
579
  },
579
580
  letterSpacing: "0.02em",
581
+ color: theme => (0, _format.primaryContrastColor)(theme),
580
582
  boxShadow: "0 8px 24px -4px rgba(59,130,246,0.25)",
581
583
  "&:hover": {
582
584
  boxShadow: "0 12px 28px -4px rgba(59,130,246,0.35)"
@@ -335,11 +335,15 @@ function OverdueInvoicePayment({
335
335
  } = item;
336
336
  const inProcess = payLoading && selectCurrencyId === currency.id;
337
337
  const status = paymentStatus[currency.id] || "idle";
338
+ const containedColorSx = (options?.variant || "contained") === "contained" ? {
339
+ color: th => (0, _util.primaryContrastColor)(th)
340
+ } : {};
338
341
  if (status === "success") {
339
342
  return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
340
343
  variant: options?.variant || "contained",
341
344
  size: "small",
342
345
  onClick: () => checkAndHandleInvoicePaid(currency.id),
346
+ sx: containedColorSx,
343
347
  ...(primaryButton ? {} : {
344
348
  color: "success",
345
349
  startIcon: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.CheckCircle, {})
@@ -378,7 +382,10 @@ function OverdueInvoicePayment({
378
382
  disabled: paying || status === "processing",
379
383
  loading: paying || status === "processing",
380
384
  onClick: onPay,
381
- sx: options?.sx,
385
+ sx: {
386
+ ...containedColorSx,
387
+ ...(options?.sx || {})
388
+ },
382
389
  children: buttonText
383
390
  })
384
391
  });
@@ -389,7 +396,10 @@ function OverdueInvoicePayment({
389
396
  disabled: inProcess,
390
397
  loading: inProcess,
391
398
  onClick: () => handlePay(item),
392
- sx: options?.sx,
399
+ sx: {
400
+ ...containedColorSx,
401
+ ...(options?.sx || {})
402
+ },
393
403
  children: status === "error" ? t("payment.subscription.overdue.retry") : t("payment.subscription.overdue.payNow")
394
404
  });
395
405
  };
@@ -0,0 +1,4 @@
1
+ export default function ServiceSuspendedDialog({ open, onClose }: {
2
+ open: boolean;
3
+ onClose: () => void;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = ServiceSuspendedDialog;
7
+ var _jsxRuntime = require("react/jsx-runtime");
8
+ var _material = require("@mui/material");
9
+ var _styles = require("@mui/material/styles");
10
+ var _PauseCircleOutline = _interopRequireDefault(require("@mui/icons-material/PauseCircleOutline"));
11
+ var _context = require("@arcblock/ux/lib/Locale/context");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ function ServiceSuspendedDialog({
14
+ open,
15
+ onClose
16
+ }) {
17
+ const {
18
+ t
19
+ } = (0, _context.useLocaleContext)();
20
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Dialog, {
21
+ open,
22
+ onClose,
23
+ PaperProps: {
24
+ sx: {
25
+ borderRadius: 3,
26
+ maxWidth: 400,
27
+ mx: "auto",
28
+ overflow: "hidden"
29
+ }
30
+ },
31
+ children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.DialogContent, {
32
+ sx: {
33
+ p: 0
34
+ },
35
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
36
+ alignItems: "center",
37
+ sx: {
38
+ pt: 4,
39
+ pb: 3,
40
+ px: 4,
41
+ textAlign: "center"
42
+ },
43
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
44
+ sx: {
45
+ width: 64,
46
+ height: 64,
47
+ borderRadius: "50%",
48
+ display: "flex",
49
+ alignItems: "center",
50
+ justifyContent: "center",
51
+ bgcolor: theme => (0, _styles.alpha)(theme.palette.warning.main, 0.1),
52
+ mb: 2.5
53
+ },
54
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_PauseCircleOutline.default, {
55
+ sx: {
56
+ fontSize: 36,
57
+ color: "warning.main"
58
+ }
59
+ })
60
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
61
+ sx: {
62
+ fontWeight: 700,
63
+ fontSize: 18,
64
+ mb: 1,
65
+ color: "text.primary"
66
+ },
67
+ children: t("payment.checkout.stopAcceptingOrders.title")
68
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
69
+ sx: {
70
+ color: "text.secondary",
71
+ fontSize: 14,
72
+ lineHeight: 1.6
73
+ },
74
+ children: t("payment.checkout.stopAcceptingOrders.description")
75
+ })]
76
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
77
+ sx: {
78
+ px: 4,
79
+ pb: 3
80
+ },
81
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
82
+ fullWidth: true,
83
+ variant: "contained",
84
+ disableElevation: true,
85
+ onClick: onClose,
86
+ sx: {
87
+ borderRadius: 2,
88
+ textTransform: "none",
89
+ fontWeight: 600,
90
+ py: 1
91
+ },
92
+ children: t("common.know")
93
+ })
94
+ })]
95
+ })
96
+ });
97
+ }
@@ -194,3 +194,11 @@ export declare function getTokenBalanceLink(method: TPaymentMethod, address: str
194
194
  export declare function isCreditMetered(price: TPrice): boolean;
195
195
  export declare function showStaking(method: TPaymentMethod, currency: TPaymentCurrency, noStake: boolean): boolean;
196
196
  export declare function formatLinkWithLocale(url: string, locale?: string): string;
197
+ export declare function primaryContrastColor(theme: {
198
+ palette: {
199
+ primary: {
200
+ main: string;
201
+ };
202
+ getContrastText: (bg: string) => string;
203
+ };
204
+ }): string;
package/lib/libs/util.js CHANGED
@@ -75,6 +75,7 @@ exports.lazyLoad = lazyLoad;
75
75
  exports.mergeExtraParams = void 0;
76
76
  exports.openDonationSettings = openDonationSettings;
77
77
  exports.parseMarkedText = parseMarkedText;
78
+ exports.primaryContrastColor = primaryContrastColor;
78
79
  exports.showStaking = showStaking;
79
80
  exports.sleep = sleep;
80
81
  exports.stopEvent = stopEvent;
@@ -1644,4 +1645,7 @@ function formatLinkWithLocale(url, locale) {
1644
1645
  const separator = url.includes("?") ? "&" : "?";
1645
1646
  return `${url}${separator}locale=${locale}`;
1646
1647
  }
1648
+ }
1649
+ function primaryContrastColor(theme) {
1650
+ return theme.palette.getContrastText(theme.palette.primary.main);
1647
1651
  }
package/lib/locales/en.js CHANGED
@@ -387,6 +387,10 @@ module.exports = (0, _flat.default)({
387
387
  title: "Nothing to show here",
388
388
  description: "It seems this checkout session is not configured properly"
389
389
  },
390
+ stopAcceptingOrders: {
391
+ title: "Service Suspended",
392
+ description: "New order placement is temporarily unavailable due to a system-level service suspension."
393
+ },
390
394
  error: {
391
395
  title: "Something went wrong"
392
396
  },
package/lib/locales/zh.js CHANGED
@@ -416,6 +416,10 @@ module.exports = (0, _flat.default)({
416
416
  title: "\u6CA1\u6709\u4EFB\u4F55\u8D2D\u4E70\u9879\u76EE",
417
417
  description: "\u53EF\u80FD\u8FD9\u4E2A\u4ED8\u6B3E\u94FE\u63A5\u6CA1\u6709\u6B63\u786E\u914D\u7F6E"
418
418
  },
419
+ stopAcceptingOrders: {
420
+ title: "\u6682\u505C\u670D\u52A1",
421
+ description: "\u56E0\u7CFB\u7EDF\u7B56\u7565\u8C03\u6574\uFF0C\u5F53\u524D\u5DF2\u6682\u505C\u65B0\u8BA2\u5355\u670D\u52A1\u3002"
422
+ },
419
423
  error: {
420
424
  title: "\u51FA\u4E86\u70B9\u95EE\u9898"
421
425
  },
@@ -40,6 +40,7 @@ var _loadingButton = _interopRequireDefault(require("../../components/loading-bu
40
40
  var _overDueInvoicePayment = _interopRequireDefault(require("../../components/over-due-invoice-payment"));
41
41
  var _currency2 = require("../../libs/currency");
42
42
  var _confirm = _interopRequireDefault(require("../../components/confirm"));
43
+ var _serviceSuspendedDialog = _interopRequireDefault(require("../../components/service-suspended-dialog"));
43
44
  var _priceChangeConfirm = _interopRequireDefault(require("../../components/price-change-confirm"));
44
45
  var _validator = require("../../libs/validator");
45
46
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -181,6 +182,7 @@ function PaymentForm({
181
182
  stripeContext: void 0,
182
183
  customer,
183
184
  customerLimited: false,
185
+ serviceSuspended: false,
184
186
  stripePaying: false,
185
187
  fastCheckoutInfo: null,
186
188
  creditInsufficientInfo: null,
@@ -1039,6 +1041,12 @@ function PaymentForm({
1039
1041
  customerLimited: true
1040
1042
  });
1041
1043
  }
1044
+ if (errorCode === "STOP_ACCEPTING_ORDERS") {
1045
+ shouldToast = false;
1046
+ setState({
1047
+ serviceSuspended: true
1048
+ });
1049
+ }
1042
1050
  }
1043
1051
  if (shouldToast) {
1044
1052
  _Toast.default.error((0, _util2.formatError)(err));
@@ -1307,6 +1315,11 @@ function PaymentForm({
1307
1315
  }),
1308
1316
  title: t("payment.customer.pastDue.alert.title")
1309
1317
  }
1318
+ }), state.serviceSuspended && /* @__PURE__ */(0, _jsxRuntime.jsx)(_serviceSuspendedDialog.default, {
1319
+ open: true,
1320
+ onClose: () => setState({
1321
+ serviceSuspended: false
1322
+ })
1310
1323
  }), FastCheckoutConfirmDialog, CreditInsufficientDialog, PriceUpdatedDialog, state.priceChangeConfirm?.open && /* @__PURE__ */(0, _jsxRuntime.jsx)(_priceChangeConfirm.default, {
1311
1324
  open: true,
1312
1325
  changePercent: state.priceChangeConfirm.changePercent,
@@ -1617,6 +1630,16 @@ function PaymentForm({
1617
1630
  }),
1618
1631
  title: t("payment.customer.pastDue.alert.title")
1619
1632
  }
1633
+ }), state.serviceSuspended && /* @__PURE__ */(0, _jsxRuntime.jsx)(_confirm.default, {
1634
+ onConfirm: () => setState({
1635
+ serviceSuspended: false
1636
+ }),
1637
+ onCancel: () => setState({
1638
+ serviceSuspended: false
1639
+ }),
1640
+ title: t("payment.checkout.stopAcceptingOrders.title"),
1641
+ message: t("payment.checkout.stopAcceptingOrders.description"),
1642
+ confirm: t("common.confirm")
1620
1643
  }), FastCheckoutConfirmDialog, CreditInsufficientDialog, PriceUpdatedDialog, state.priceChangeConfirm?.open && /* @__PURE__ */(0, _jsxRuntime.jsx)(_priceChangeConfirm.default, {
1621
1644
  open: true,
1622
1645
  changePercent: state.priceChangeConfirm.changePercent,
@@ -87,6 +87,10 @@ function PaymentInner({
87
87
  return Array.from(currencyIds);
88
88
  }, [paymentMethods]);
89
89
  const defaultCurrencyId = (0, _react.useMemo)(() => {
90
+ const hasAppliedDiscount = Boolean(state.checkoutSession?.discounts?.length);
91
+ if (hasAppliedDiscount && state.checkoutSession.currency_id && availableCurrencyIds.includes(state.checkoutSession.currency_id)) {
92
+ return state.checkoutSession.currency_id;
93
+ }
90
94
  if (query.currencyId && availableCurrencyIds.includes(query.currencyId)) {
91
95
  return query.currencyId;
92
96
  }
@@ -104,7 +108,7 @@ function PaymentInner({
104
108
  return state.checkoutSession.currency_id;
105
109
  }
106
110
  return availableCurrencyIds?.[0];
107
- }, [query.currencyId, availableCurrencyIds, session?.user, state.checkoutSession.currency_id, paymentMethods]);
111
+ }, [query.currencyId, availableCurrencyIds, session?.user, state.checkoutSession, paymentMethods]);
108
112
  const defaultMethodId = paymentMethods.find(m => m.payment_currencies.some(c => c.id === defaultCurrencyId))?.id;
109
113
  const hideSummaryCard = mode.endsWith("-minimal") || !showCheckoutSummary;
110
114
  const methods = (0, _reactHookForm.useForm)({
@@ -127,13 +131,20 @@ function PaymentInner({
127
131
  }
128
132
  });
129
133
  (0, _react.useEffect)(() => {
134
+ const hasAppliedDiscount = Boolean(state.checkoutSession?.discounts?.length);
135
+ const currentCurrency = methods.getValues("payment_currency");
136
+ const currentMethod = methods.getValues("payment_method");
130
137
  if (defaultCurrencyId) {
131
- methods.setValue("payment_currency", defaultCurrencyId);
138
+ if (!hasAppliedDiscount || !currentCurrency) {
139
+ methods.setValue("payment_currency", defaultCurrencyId);
140
+ }
132
141
  }
133
142
  if (defaultMethodId) {
134
- methods.setValue("payment_method", defaultMethodId);
143
+ if (!hasAppliedDiscount || !currentMethod) {
144
+ methods.setValue("payment_method", defaultMethodId);
145
+ }
135
146
  }
136
- }, [defaultCurrencyId, defaultMethodId]);
147
+ }, [defaultCurrencyId, defaultMethodId, state.checkoutSession.discounts]);
137
148
  (0, _react.useEffect)(() => {
138
149
  if (!(0, _util2.isMobileSafari)()) {
139
150
  return () => {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.26.1",
3
+ "version": "1.26.3",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -59,7 +59,7 @@
59
59
  "@arcblock/react-hooks": "^3.5.1",
60
60
  "@arcblock/ux": "^3.5.1",
61
61
  "@arcblock/ws": "^1.28.5",
62
- "@blocklet/payment-react-headless": "1.26.1",
62
+ "@blocklet/payment-react-headless": "1.26.3",
63
63
  "@blocklet/theme": "^3.5.1",
64
64
  "@blocklet/ui-react": "^3.5.1",
65
65
  "@mui/icons-material": "^7.1.2",
@@ -97,7 +97,7 @@
97
97
  "@babel/core": "^7.27.4",
98
98
  "@babel/preset-env": "^7.27.2",
99
99
  "@babel/preset-react": "^7.27.1",
100
- "@blocklet/payment-types": "1.26.1",
100
+ "@blocklet/payment-types": "1.26.3",
101
101
  "@storybook/addon-essentials": "^7.6.20",
102
102
  "@storybook/addon-interactions": "^7.6.20",
103
103
  "@storybook/addon-links": "^7.6.20",
@@ -128,5 +128,5 @@
128
128
  "vite-plugin-babel": "^1.3.1",
129
129
  "vite-plugin-node-polyfills": "^0.23.0"
130
130
  },
131
- "gitHead": "1ba42f376f040b1214d992420cda37053fc14288"
131
+ "gitHead": "18c5d045139c572b52465e15c4c63b3e327efab5"
132
132
  }
@@ -9,6 +9,7 @@ import {
9
9
  import StripeForm from '../../../payment/form/stripe';
10
10
  import ConfirmDialog from '../../../components/confirm';
11
11
  import PriceChangeConfirm from '../../../components/price-change-confirm';
12
+ import ServiceSuspendedDialog from '../../../components/service-suspended-dialog';
12
13
  import { formatTokenAmount } from '../../utils/format';
13
14
 
14
15
  function getRedirectUrl(session: any): string | undefined {
@@ -119,6 +120,9 @@ export default function CheckoutDialogs() {
119
120
  />
120
121
  )}
121
122
 
123
+ {/* Service Suspended Dialog */}
124
+ {(submit.context as any)?.type === 'service_suspended' && <ServiceSuspendedDialog open onClose={submit.cancel} />}
125
+
122
126
  {/* Credit Insufficient Dialog (matches V1 ConfirmDialog) */}
123
127
  {submit.status === 'credit_insufficient' && submit.context?.type === 'credit_insufficient' && (
124
128
  <ConfirmDialog
@@ -3,7 +3,7 @@ import ShoppingCartCheckoutIcon from '@mui/icons-material/ShoppingCartCheckout';
3
3
  import { Avatar, Box, Button, Chip, Stack, Typography } from '@mui/material';
4
4
  import type { TPaymentCurrency, TPrice } from '@blocklet/payment-types';
5
5
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
6
- import { formatDynamicUnitPrice, tSafe, INTERVAL_LOCALE_KEY } from '../../utils/format';
6
+ import { formatDynamicUnitPrice, tSafe, INTERVAL_LOCALE_KEY, primaryContrastColor } from '../../utils/format';
7
7
 
8
8
  interface CrossSellCardProps {
9
9
  crossSellItem: TPrice;
@@ -53,7 +53,7 @@ export default function CrossSellCard({
53
53
  fontWeight: 900,
54
54
  letterSpacing: '0.12em',
55
55
  bgcolor: 'primary.main',
56
- color: '#fff',
56
+ color: (theme: any) => primaryContrastColor(theme),
57
57
  boxShadow: '0 4px 12px rgba(45,124,243,0.2)',
58
58
  '& .MuiChip-label': { px: 1.5 },
59
59
  }}
@@ -150,7 +150,7 @@ export default function CrossSellCard({
150
150
  transition: 'all 0.2s',
151
151
  '&:hover': {
152
152
  bgcolor: 'primary.main',
153
- color: '#fff',
153
+ color: (theme: any) => primaryContrastColor(theme),
154
154
  borderColor: 'primary.main',
155
155
  },
156
156
  '&:active': { transform: 'scale(0.95)' },
@@ -22,7 +22,13 @@ import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
22
22
  import type { TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
23
23
  import { getPriceUnitAmountByCurrency } from '@blocklet/payment-react-headless';
24
24
  import Toast from '@arcblock/ux/lib/Toast';
25
- import { INTERVAL_LOCALE_KEY, formatDynamicUnitPrice, formatTokenAmount, formatTrialText } from '../../utils/format';
25
+ import {
26
+ INTERVAL_LOCALE_KEY,
27
+ formatDynamicUnitPrice,
28
+ formatTokenAmount,
29
+ formatTrialText,
30
+ primaryContrastColor,
31
+ } from '../../utils/format';
26
32
 
27
33
  interface ProductItemCardProps {
28
34
  item: TLineItemExpanded & { adjustable_quantity?: { enabled: boolean; minimum?: number; maximum?: number } };
@@ -292,7 +298,7 @@ export default function ProductItemCard({
292
298
  fontWeight: 900,
293
299
  letterSpacing: '0.12em',
294
300
  bgcolor: 'primary.main',
295
- color: '#fff',
301
+ color: (th: any) => primaryContrastColor(th),
296
302
  boxShadow: '0 4px 12px rgba(45,124,243,0.2)',
297
303
  '& .MuiChip-label': { px: 1.5 },
298
304
  }}
@@ -467,12 +473,16 @@ export default function ProductItemCard({
467
473
  {/* Discount chip */}
468
474
  {discountCode && perItemDiscount && (
469
475
  <Box sx={{ mt: 1.5 }}>
470
- <Chip
471
- icon={<LocalOfferIcon sx={{ color: 'warning.main', fontSize: 'small' }} />}
472
- label={`${discountCode} (-${perItemDiscount})`}
473
- size="small"
474
- sx={{ height: 22, borderRadius: '6px', '& .MuiChip-label': { fontSize: 12 } }}
475
- />
476
+ {isRateLoading ? (
477
+ <Skeleton variant="rounded" width={160} height={22} sx={{ borderRadius: '6px' }} />
478
+ ) : (
479
+ <Chip
480
+ icon={<LocalOfferIcon sx={{ color: 'warning.main', fontSize: 'small' }} />}
481
+ label={`${discountCode} (-${perItemDiscount})`}
482
+ size="small"
483
+ sx={{ height: 22, borderRadius: '6px', '& .MuiChip-label': { fontSize: 12 } }}
484
+ />
485
+ )}
476
486
  </Box>
477
487
  )}
478
488
 
@@ -9,11 +9,13 @@ import {
9
9
  CircularProgress,
10
10
  IconButton,
11
11
  InputAdornment,
12
+ Skeleton,
12
13
  Stack,
13
14
  TextField,
14
15
  Typography,
15
16
  } from '@mui/material';
16
17
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
18
+ import { formatCouponTerms } from '../../../libs/util';
17
19
 
18
20
  interface PromotionInputProps {
19
21
  promotion: {
@@ -26,17 +28,23 @@ interface PromotionInputProps {
26
28
  };
27
29
  discounts: any[];
28
30
  discountAmount: string | null;
31
+ // eslint-disable-next-line react/require-default-props
32
+ currency?: any;
29
33
  /** Start with input field visible (skip the "Add promotion code" button) */
30
34
  initialShowInput?: boolean;
35
+ /** Show skeleton for the discount amount while switching */
36
+ isAmountLoading?: boolean;
31
37
  }
32
38
 
33
39
  export default function PromotionInput({
34
40
  promotion,
35
41
  discounts,
36
42
  discountAmount,
43
+ currency = null,
37
44
  initialShowInput = false,
45
+ isAmountLoading = false,
38
46
  }: PromotionInputProps) {
39
- const { t } = useLocaleContext();
47
+ const { t, locale } = useLocaleContext();
40
48
  const [showInput, setShowInput] = useState(false);
41
49
  const [code, setCode] = useState('');
42
50
  const [applying, setApplying] = useState(false);
@@ -73,19 +81,7 @@ export default function PromotionInput({
73
81
  const discCode =
74
82
  disc.promotion_code_details?.code || disc.verification_data?.code || disc.promotion_code || '';
75
83
  const coupon = disc.coupon_details || {};
76
- const couponOff =
77
- coupon.percent_off > 0
78
- ? t('payment.checkout.coupon.percentage', { percent: coupon.percent_off })
79
- : `${coupon.percent_off || 0}%`;
80
- let description = '';
81
- if (coupon.duration === 'repeating' && coupon.duration_in_months) {
82
- const months = coupon.duration_in_months;
83
- description = `${couponOff} for ${months} month${months > 1 ? 's' : ''}`;
84
- } else if (coupon.duration === 'forever') {
85
- description = t('payment.checkout.coupon.terms.forever', { couponOff });
86
- } else if (coupon.duration === 'once') {
87
- description = t('payment.checkout.coupon.terms.once', { couponOff });
88
- }
84
+ const description = coupon && currency ? formatCouponTerms(coupon, currency, locale) : '';
89
85
  return (
90
86
  <Stack
91
87
  key={disc.promotion_code || disc.coupon || i}
@@ -115,9 +111,13 @@ export default function PromotionInput({
115
111
  <CloseIcon sx={{ fontSize: 12, color: '#12b886' }} />
116
112
  </IconButton>
117
113
  </Stack>
118
- <Typography sx={{ color: 'text.primary', fontWeight: 600, fontSize: 14 }}>
119
- -{discountAmount || '0'}
120
- </Typography>
114
+ {isAmountLoading ? (
115
+ <Skeleton variant="text" width={80} height={22} />
116
+ ) : (
117
+ <Typography sx={{ color: 'text.primary', fontWeight: 600, fontSize: 14 }}>
118
+ -{discountAmount || '0'}
119
+ </Typography>
120
+ )}
121
121
  </Stack>
122
122
  );
123
123
  })}