@blocklet/payment-react 1.25.10 → 1.26.1

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 (160) hide show
  1. package/es/checkout-v2/checkout-v2.d.ts +2 -0
  2. package/es/checkout-v2/checkout-v2.js +121 -0
  3. package/es/checkout-v2/components/dialogs/checkout-dialogs.d.ts +1 -0
  4. package/es/checkout-v2/components/dialogs/checkout-dialogs.js +106 -0
  5. package/es/checkout-v2/components/left/billing-toggle.d.ts +6 -0
  6. package/es/checkout-v2/components/left/billing-toggle.js +118 -0
  7. package/es/checkout-v2/components/left/cross-sell-card.d.ts +10 -0
  8. package/es/checkout-v2/components/left/cross-sell-card.js +167 -0
  9. package/es/checkout-v2/components/left/product-item-card.d.ts +26 -0
  10. package/es/checkout-v2/components/left/product-item-card.js +571 -0
  11. package/es/checkout-v2/components/left/promotion-input.d.ts +19 -0
  12. package/es/checkout-v2/components/left/promotion-input.js +178 -0
  13. package/es/checkout-v2/components/left/staking-breakdown.d.ts +9 -0
  14. package/es/checkout-v2/components/left/staking-breakdown.js +48 -0
  15. package/es/checkout-v2/components/left/trial-info.d.ts +13 -0
  16. package/es/checkout-v2/components/left/trial-info.js +48 -0
  17. package/es/checkout-v2/components/right/currency-grid.d.ts +8 -0
  18. package/es/checkout-v2/components/right/currency-grid.js +48 -0
  19. package/es/checkout-v2/components/right/customer-info-card.d.ts +17 -0
  20. package/es/checkout-v2/components/right/customer-info-card.js +156 -0
  21. package/es/checkout-v2/components/right/status-feedback.d.ts +7 -0
  22. package/es/checkout-v2/components/right/status-feedback.js +17 -0
  23. package/es/checkout-v2/components/right/submit-button.d.ts +10 -0
  24. package/es/checkout-v2/components/right/submit-button.js +29 -0
  25. package/es/checkout-v2/components/right/subscription-disclaimer.d.ts +11 -0
  26. package/es/checkout-v2/components/right/subscription-disclaimer.js +8 -0
  27. package/es/checkout-v2/components/shared/exchange-rate-footer.d.ts +23 -0
  28. package/es/checkout-v2/components/shared/exchange-rate-footer.js +182 -0
  29. package/es/checkout-v2/components/shared/scenario-badge.d.ts +6 -0
  30. package/es/checkout-v2/components/shared/scenario-badge.js +47 -0
  31. package/es/checkout-v2/components/shared/total-display.d.ts +7 -0
  32. package/es/checkout-v2/components/shared/total-display.js +84 -0
  33. package/es/checkout-v2/index.d.ts +2 -0
  34. package/es/checkout-v2/index.js +1 -0
  35. package/es/checkout-v2/layouts/checkout-layout.d.ts +7 -0
  36. package/es/checkout-v2/layouts/checkout-layout.js +226 -0
  37. package/es/checkout-v2/panels/left/composite-panel.d.ts +1 -0
  38. package/es/checkout-v2/panels/left/composite-panel.js +423 -0
  39. package/es/checkout-v2/panels/left/credit-topup-panel.d.ts +1 -0
  40. package/es/checkout-v2/panels/left/credit-topup-panel.js +611 -0
  41. package/es/checkout-v2/panels/left/scenario-router.d.ts +1 -0
  42. package/es/checkout-v2/panels/left/scenario-router.js +19 -0
  43. package/es/checkout-v2/panels/right/payment-panel.d.ts +1 -0
  44. package/es/checkout-v2/panels/right/payment-panel.js +644 -0
  45. package/es/checkout-v2/types.d.ts +15 -0
  46. package/es/checkout-v2/types.js +0 -0
  47. package/es/checkout-v2/utils/format.d.ts +59 -0
  48. package/es/checkout-v2/utils/format.js +125 -0
  49. package/es/checkout-v2/utils/scenario-detector.d.ts +3 -0
  50. package/es/checkout-v2/utils/scenario-detector.js +17 -0
  51. package/es/checkout-v2/views/error-view.d.ts +7 -0
  52. package/es/checkout-v2/views/error-view.js +269 -0
  53. package/es/checkout-v2/views/loading-view.d.ts +5 -0
  54. package/es/checkout-v2/views/loading-view.js +158 -0
  55. package/es/checkout-v2/views/success-view.d.ts +29 -0
  56. package/es/checkout-v2/views/success-view.js +614 -0
  57. package/es/components/phone-field.d.ts +14 -0
  58. package/es/components/phone-field.js +96 -0
  59. package/es/index.d.ts +3 -1
  60. package/es/index.js +3 -1
  61. package/es/locales/en.js +45 -6
  62. package/es/locales/zh.js +45 -6
  63. package/es/payment/form/index.js +10 -1
  64. package/lib/checkout-v2/checkout-v2.d.ts +2 -0
  65. package/lib/checkout-v2/checkout-v2.js +151 -0
  66. package/lib/checkout-v2/components/dialogs/checkout-dialogs.d.ts +1 -0
  67. package/lib/checkout-v2/components/dialogs/checkout-dialogs.js +131 -0
  68. package/lib/checkout-v2/components/left/billing-toggle.d.ts +6 -0
  69. package/lib/checkout-v2/components/left/billing-toggle.js +126 -0
  70. package/lib/checkout-v2/components/left/cross-sell-card.d.ts +10 -0
  71. package/lib/checkout-v2/components/left/cross-sell-card.js +257 -0
  72. package/lib/checkout-v2/components/left/product-item-card.d.ts +26 -0
  73. package/lib/checkout-v2/components/left/product-item-card.js +738 -0
  74. package/lib/checkout-v2/components/left/promotion-input.d.ts +19 -0
  75. package/lib/checkout-v2/components/left/promotion-input.js +220 -0
  76. package/lib/checkout-v2/components/left/staking-breakdown.d.ts +9 -0
  77. package/lib/checkout-v2/components/left/staking-breakdown.js +96 -0
  78. package/lib/checkout-v2/components/left/trial-info.d.ts +13 -0
  79. package/lib/checkout-v2/components/left/trial-info.js +82 -0
  80. package/lib/checkout-v2/components/right/currency-grid.d.ts +8 -0
  81. package/lib/checkout-v2/components/right/currency-grid.js +96 -0
  82. package/lib/checkout-v2/components/right/customer-info-card.d.ts +17 -0
  83. package/lib/checkout-v2/components/right/customer-info-card.js +246 -0
  84. package/lib/checkout-v2/components/right/status-feedback.d.ts +7 -0
  85. package/lib/checkout-v2/components/right/status-feedback.js +30 -0
  86. package/lib/checkout-v2/components/right/submit-button.d.ts +10 -0
  87. package/lib/checkout-v2/components/right/submit-button.js +35 -0
  88. package/lib/checkout-v2/components/right/subscription-disclaimer.d.ts +11 -0
  89. package/lib/checkout-v2/components/right/subscription-disclaimer.js +33 -0
  90. package/lib/checkout-v2/components/shared/exchange-rate-footer.d.ts +23 -0
  91. package/lib/checkout-v2/components/shared/exchange-rate-footer.js +282 -0
  92. package/lib/checkout-v2/components/shared/scenario-badge.d.ts +6 -0
  93. package/lib/checkout-v2/components/shared/scenario-badge.js +57 -0
  94. package/lib/checkout-v2/components/shared/total-display.d.ts +7 -0
  95. package/lib/checkout-v2/components/shared/total-display.js +154 -0
  96. package/lib/checkout-v2/index.d.ts +2 -0
  97. package/lib/checkout-v2/index.js +13 -0
  98. package/lib/checkout-v2/layouts/checkout-layout.d.ts +7 -0
  99. package/lib/checkout-v2/layouts/checkout-layout.js +308 -0
  100. package/lib/checkout-v2/panels/left/composite-panel.d.ts +1 -0
  101. package/lib/checkout-v2/panels/left/composite-panel.js +515 -0
  102. package/lib/checkout-v2/panels/left/credit-topup-panel.d.ts +1 -0
  103. package/lib/checkout-v2/panels/left/credit-topup-panel.js +795 -0
  104. package/lib/checkout-v2/panels/left/scenario-router.d.ts +1 -0
  105. package/lib/checkout-v2/panels/left/scenario-router.js +29 -0
  106. package/lib/checkout-v2/panels/right/payment-panel.d.ts +1 -0
  107. package/lib/checkout-v2/panels/right/payment-panel.js +906 -0
  108. package/lib/checkout-v2/types.d.ts +15 -0
  109. package/lib/checkout-v2/types.js +1 -0
  110. package/lib/checkout-v2/utils/format.d.ts +59 -0
  111. package/lib/checkout-v2/utils/format.js +158 -0
  112. package/lib/checkout-v2/utils/scenario-detector.d.ts +3 -0
  113. package/lib/checkout-v2/utils/scenario-detector.js +23 -0
  114. package/lib/checkout-v2/views/error-view.d.ts +7 -0
  115. package/lib/checkout-v2/views/error-view.js +321 -0
  116. package/lib/checkout-v2/views/loading-view.d.ts +5 -0
  117. package/lib/checkout-v2/views/loading-view.js +168 -0
  118. package/lib/checkout-v2/views/success-view.d.ts +29 -0
  119. package/lib/checkout-v2/views/success-view.js +735 -0
  120. package/lib/components/phone-field.d.ts +14 -0
  121. package/lib/components/phone-field.js +130 -0
  122. package/lib/index.d.ts +3 -1
  123. package/lib/index.js +8 -0
  124. package/lib/locales/en.js +45 -6
  125. package/lib/locales/zh.js +45 -6
  126. package/lib/payment/form/index.js +10 -1
  127. package/package.json +4 -3
  128. package/src/checkout-v2/checkout-v2.tsx +155 -0
  129. package/src/checkout-v2/components/dialogs/checkout-dialogs.tsx +134 -0
  130. package/src/checkout-v2/components/left/billing-toggle.tsx +122 -0
  131. package/src/checkout-v2/components/left/cross-sell-card.tsx +170 -0
  132. package/src/checkout-v2/components/left/product-item-card.tsx +642 -0
  133. package/src/checkout-v2/components/left/promotion-input.tsx +207 -0
  134. package/src/checkout-v2/components/left/staking-breakdown.tsx +57 -0
  135. package/src/checkout-v2/components/left/trial-info.tsx +63 -0
  136. package/src/checkout-v2/components/right/currency-grid.tsx +59 -0
  137. package/src/checkout-v2/components/right/customer-info-card.tsx +214 -0
  138. package/src/checkout-v2/components/right/status-feedback.tsx +35 -0
  139. package/src/checkout-v2/components/right/submit-button.tsx +37 -0
  140. package/src/checkout-v2/components/right/subscription-disclaimer.tsx +27 -0
  141. package/src/checkout-v2/components/shared/exchange-rate-footer.tsx +221 -0
  142. package/src/checkout-v2/components/shared/scenario-badge.tsx +51 -0
  143. package/src/checkout-v2/components/shared/total-display.tsx +112 -0
  144. package/src/checkout-v2/index.ts +2 -0
  145. package/src/checkout-v2/layouts/checkout-layout.tsx +232 -0
  146. package/src/checkout-v2/panels/left/composite-panel.tsx +465 -0
  147. package/src/checkout-v2/panels/left/credit-topup-panel.tsx +677 -0
  148. package/src/checkout-v2/panels/left/scenario-router.tsx +22 -0
  149. package/src/checkout-v2/panels/right/payment-panel.tsx +703 -0
  150. package/src/checkout-v2/types.ts +18 -0
  151. package/src/checkout-v2/utils/format.ts +205 -0
  152. package/src/checkout-v2/utils/scenario-detector.ts +30 -0
  153. package/src/checkout-v2/views/error-view.tsx +293 -0
  154. package/src/checkout-v2/views/loading-view.tsx +162 -0
  155. package/src/checkout-v2/views/success-view.tsx +770 -0
  156. package/src/components/phone-field.tsx +119 -0
  157. package/src/index.ts +3 -0
  158. package/src/locales/en.tsx +45 -4
  159. package/src/locales/zh.tsx +43 -4
  160. package/src/payment/form/index.tsx +16 -1
@@ -0,0 +1,571 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import AddIcon from "@mui/icons-material/Add";
4
+ import CheckIcon from "@mui/icons-material/Check";
5
+ import LocalOfferIcon from "@mui/icons-material/LocalOffer";
6
+ import RemoveIcon from "@mui/icons-material/Remove";
7
+ import ShoppingCartCheckoutIcon from "@mui/icons-material/ShoppingCartCheckout";
8
+ import {
9
+ Avatar,
10
+ Box,
11
+ Chip,
12
+ Collapse,
13
+ IconButton,
14
+ Skeleton,
15
+ Stack,
16
+ Switch,
17
+ TextField,
18
+ Typography,
19
+ useMediaQuery,
20
+ useTheme
21
+ } from "@mui/material";
22
+ import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
23
+ import { getPriceUnitAmountByCurrency } from "@blocklet/payment-react-headless";
24
+ import Toast from "@arcblock/ux/lib/Toast";
25
+ import { INTERVAL_LOCALE_KEY, formatDynamicUnitPrice, formatTokenAmount, formatTrialText } from "../../utils/format.js";
26
+ export default function ProductItemCard({
27
+ item,
28
+ currency,
29
+ discounts,
30
+ exchangeRate,
31
+ onQuantityChange,
32
+ onUpsell,
33
+ onDownsell,
34
+ trialActive,
35
+ trialDays,
36
+ t,
37
+ recommended = false,
38
+ hideUpsell = false,
39
+ isRateLoading = false,
40
+ showFeatures = true,
41
+ children = void 0
42
+ }) {
43
+ const activePrice = item.upsell_price || item.price;
44
+ const product = activePrice?.product;
45
+ const name = product?.name || "Item";
46
+ const logo = product?.images?.[0] || "";
47
+ const features = product?.features || [];
48
+ const recurring = activePrice?.recurring;
49
+ const quantity = item.quantity || 1;
50
+ const isMetered = recurring?.usage_type === "metered";
51
+ const metered = isMetered ? ` ${t("common.metered")}` : "";
52
+ const perUnitFormatted = formatDynamicUnitPrice(activePrice, currency, exchangeRate);
53
+ const isSubscription = !!recurring;
54
+ const typeBadgeText = isSubscription ? t("payment.checkout.typeBadge.subscription") : t("payment.checkout.typeBadge.oneTime");
55
+ const priceIntervalSuffix = recurring?.interval ? ` / ${t(`common.${recurring.interval}`)}` : "";
56
+ const subtitleText = (() => {
57
+ if (quantity > 1 && perUnitFormatted && currency) {
58
+ return `${quantity} \xD7 ${perUnitFormatted} ${currency.symbol}`;
59
+ }
60
+ if (isMetered) return metered.trim();
61
+ return "";
62
+ })();
63
+ const itemTotal = (() => {
64
+ const quoteCurrencyId = item.quote_currency_id;
65
+ if (item.custom_amount && quoteCurrencyId && quoteCurrencyId === currency?.id) {
66
+ return formatTokenAmount(item.custom_amount, currency);
67
+ }
68
+ if (activePrice?.pricing_type === "dynamic" && exchangeRate && activePrice.base_amount) {
69
+ const rate = Number(exchangeRate);
70
+ if (rate > 0 && Number.isFinite(rate)) {
71
+ const baseUsd = Number(activePrice.base_amount);
72
+ if (baseUsd > 0 && Number.isFinite(baseUsd)) {
73
+ const tokenAmount = baseUsd * quantity / rate;
74
+ const abs = Math.abs(tokenAmount);
75
+ const precision = abs > 0 && abs < 0.01 ? 6 : 2;
76
+ return tokenAmount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: precision }).replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "") || "0";
77
+ }
78
+ }
79
+ }
80
+ if (!exchangeRate && activePrice?.base_amount != null) {
81
+ const baseUsd = Number(activePrice.base_amount);
82
+ if (baseUsd >= 0 && Number.isFinite(baseUsd)) {
83
+ const total = baseUsd * quantity;
84
+ return total.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
85
+ }
86
+ }
87
+ if (activePrice && currency) {
88
+ const unitAmount = getPriceUnitAmountByCurrency(activePrice, currency);
89
+ if (unitAmount && unitAmount !== "0") {
90
+ const totalUnits = BigInt(unitAmount) * BigInt(quantity);
91
+ return formatTokenAmount(totalUnits.toString(), currency);
92
+ }
93
+ }
94
+ return "0";
95
+ })();
96
+ const discount = discounts?.[0];
97
+ const discountCode = discount?.promotion_code_details?.code || discount?.verification_data?.code || discount?.promotion_code || "";
98
+ const perItemDiscount = (() => {
99
+ if (!discountCode || !discount) return null;
100
+ const couponDetails = discount?.coupon_details;
101
+ if (couponDetails?.percent_off > 0) {
102
+ const numericTotal = parseFloat(String(itemTotal).replace(/,/g, ""));
103
+ if (!Number.isNaN(numericTotal) && numericTotal > 0) {
104
+ const discAmount = numericTotal * couponDetails.percent_off / 100;
105
+ const abs = Math.abs(discAmount);
106
+ const precision = abs > 0 && abs < 0.01 ? 6 : 2;
107
+ return `${discAmount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: precision }).replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "") || "0"} ${currency?.symbol || ""}`;
108
+ }
109
+ }
110
+ if (item.discount_amounts?.length > 0 && currency) {
111
+ return `${formatTokenAmount(item.discount_amounts[0].amount, currency)} ${currency.symbol}`;
112
+ }
113
+ return null;
114
+ })();
115
+ const adjustable = item.adjustable_quantity?.enabled;
116
+ const min = item.adjustable_quantity?.minimum || 1;
117
+ const max = item.adjustable_quantity?.maximum;
118
+ const [qtyInput, setQtyInput] = useState(String(quantity));
119
+ const [isEditing, setIsEditing] = useState(false);
120
+ const theme = useTheme();
121
+ const isMobile = useMediaQuery(theme.breakpoints.down("md"));
122
+ const [featuresOpen, setFeaturesOpen] = useState(!isMobile);
123
+ useEffect(() => {
124
+ if (!isEditing) setQtyInput(String(quantity));
125
+ }, [quantity, isEditing]);
126
+ const canUpsell = !!item.price?.upsell?.upsells_to;
127
+ const isUpselled = !!item.upsell_price;
128
+ const upsellTo = item.price?.upsell?.upsells_to;
129
+ let savingsPercent = 0;
130
+ if (canUpsell && upsellTo) {
131
+ const fromAmount = parseFloat(item.price?.base_amount || item.price?.unit_amount || "0");
132
+ const toAmount = parseFloat(upsellTo?.base_amount || upsellTo?.unit_amount || "0");
133
+ const fromInterval = item.price?.recurring?.interval;
134
+ const toInterval = upsellTo?.recurring?.interval;
135
+ if (fromAmount > 0 && toAmount > 0 && fromInterval && toInterval) {
136
+ const monthsMap = { day: 365, week: 52, month: 12, year: 1 };
137
+ const fromYearly = fromAmount * (monthsMap[fromInterval] || 1);
138
+ const toYearly = toAmount * (monthsMap[toInterval] || 1);
139
+ if (fromYearly > toYearly) {
140
+ savingsPercent = Math.round((fromYearly - toYearly) / fromYearly * 100);
141
+ }
142
+ }
143
+ }
144
+ const upsellInterval = upsellTo?.recurring?.interval;
145
+ const upsellPrice = (() => {
146
+ if (!upsellTo) return "";
147
+ const slashText = upsellInterval ? t("common.slash", { interval: t(`common.${upsellInterval}`) }) : "";
148
+ if (upsellTo.pricing_type === "dynamic" && upsellTo.base_amount && exchangeRate) {
149
+ const rate = Number(exchangeRate);
150
+ if (rate > 0 && Number.isFinite(rate)) {
151
+ const baseUsd = parseFloat(upsellTo.base_amount);
152
+ if (baseUsd > 0 && Number.isFinite(baseUsd)) {
153
+ const tokenAmount = baseUsd / rate;
154
+ const abs = Math.abs(tokenAmount);
155
+ const precision = abs > 0 && abs < 0.01 ? 6 : 2;
156
+ const formatted = tokenAmount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: precision }).replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "") || "0";
157
+ return `${formatted} ${currency?.symbol || ""} ${slashText}`;
158
+ }
159
+ }
160
+ }
161
+ if (upsellTo.pricing_type === "dynamic" && upsellTo.base_amount && upsellTo.base_currency === "USD") {
162
+ const baseUsd = parseFloat(upsellTo.base_amount);
163
+ const formattedUsd = baseUsd.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
164
+ return `$${formattedUsd} ${slashText}`;
165
+ }
166
+ const upsellUnitFormatted = formatDynamicUnitPrice(upsellTo, currency, exchangeRate);
167
+ return upsellUnitFormatted ? `${upsellUnitFormatted} ${currency?.symbol || ""} ${slashText}` : "";
168
+ })();
169
+ const originalInterval = item.price?.recurring?.interval;
170
+ const downsellPrice = (() => {
171
+ if (!item.price || !isUpselled) return "";
172
+ const originalPrice = item.price;
173
+ const originalSlash = originalInterval ? t("common.slash", { interval: t(`common.${originalInterval}`) }) : "";
174
+ if (originalPrice.pricing_type === "dynamic" && originalPrice.base_amount && exchangeRate) {
175
+ const rate = Number(exchangeRate);
176
+ if (rate > 0 && Number.isFinite(rate)) {
177
+ const baseUsd = parseFloat(originalPrice.base_amount);
178
+ if (baseUsd > 0 && Number.isFinite(baseUsd)) {
179
+ const tokenAmount = baseUsd / rate;
180
+ const abs = Math.abs(tokenAmount);
181
+ const precision = abs > 0 && abs < 0.01 ? 6 : 2;
182
+ const formatted = tokenAmount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: precision }).replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "") || "0";
183
+ return `${formatted} ${currency?.symbol || ""} ${originalSlash}`;
184
+ }
185
+ }
186
+ }
187
+ if (originalPrice.pricing_type === "dynamic" && originalPrice.base_amount && originalPrice.base_currency === "USD") {
188
+ const baseUsd = parseFloat(originalPrice.base_amount);
189
+ const formattedUsd = baseUsd.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
190
+ return `$${formattedUsd} ${originalSlash}`;
191
+ }
192
+ const unitFormatted = formatDynamicUnitPrice(originalPrice, currency, exchangeRate);
193
+ return unitFormatted ? `${unitFormatted} ${currency?.symbol || ""} ${originalSlash}` : "";
194
+ })();
195
+ const upsellToggleLabel = (() => {
196
+ if (isUpselled) {
197
+ const recurringLabel2 = originalInterval ? t(INTERVAL_LOCALE_KEY[originalInterval] || "") : "";
198
+ return t("payment.checkout.upsell.revert", { recurring: recurringLabel2 });
199
+ }
200
+ const recurringLabel = upsellInterval ? t(INTERVAL_LOCALE_KEY[upsellInterval] || "") : "";
201
+ return t("payment.checkout.upsell.save", { recurring: recurringLabel });
202
+ })();
203
+ return /* @__PURE__ */ jsxs(Box, { sx: { position: "relative" }, children: [
204
+ recommended && /* @__PURE__ */ jsx(
205
+ Chip,
206
+ {
207
+ label: "RECOMMENDED",
208
+ size: "small",
209
+ sx: {
210
+ position: "absolute",
211
+ top: 0,
212
+ right: 40,
213
+ transform: "translateY(-50%)",
214
+ zIndex: 1,
215
+ height: 22,
216
+ fontSize: 9,
217
+ fontWeight: 900,
218
+ letterSpacing: "0.12em",
219
+ bgcolor: "primary.main",
220
+ color: "#fff",
221
+ boxShadow: "0 4px 12px rgba(45,124,243,0.2)",
222
+ "& .MuiChip-label": { px: 1.5 }
223
+ }
224
+ }
225
+ ),
226
+ /* @__PURE__ */ jsxs(
227
+ Box,
228
+ {
229
+ sx: {
230
+ p: { xs: 2, md: 3 },
231
+ bgcolor: "background.paper",
232
+ borderRadius: { xs: "16px", md: "24px" },
233
+ border: "1px solid",
234
+ borderColor: "divider",
235
+ boxShadow: (th) => th.palette.mode === "dark" ? "0 12px 40px -8px rgba(0,0,0,0.3)" : "0 12px 40px -8px rgba(0,0,0,0.06)",
236
+ transition: "all 0.3s ease",
237
+ ...canUpsell && !hideUpsell ? { borderRadius: { xs: "16px 16px 0 0", md: "24px 24px 0 0" } } : {},
238
+ "&:hover": {
239
+ borderColor: (th) => th.palette.mode === "dark" ? "rgba(255,255,255,0.12)" : "rgba(45,124,243,0.15)"
240
+ }
241
+ },
242
+ children: [
243
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: { xs: 1.5, md: 2.5 }, sx: { alignItems: "center", width: "100%" }, children: [
244
+ logo ? /* @__PURE__ */ jsx(
245
+ Avatar,
246
+ {
247
+ src: logo,
248
+ alt: name,
249
+ variant: "rounded",
250
+ sx: {
251
+ width: { xs: 44, md: 64 },
252
+ height: { xs: 44, md: 64 },
253
+ borderRadius: { xs: "12px", md: "16px" },
254
+ flexShrink: 0
255
+ }
256
+ }
257
+ ) : /* @__PURE__ */ jsx(
258
+ Avatar,
259
+ {
260
+ variant: "rounded",
261
+ sx: {
262
+ width: { xs: 44, md: 64 },
263
+ height: { xs: 44, md: 64 },
264
+ borderRadius: { xs: "12px", md: "16px" },
265
+ bgcolor: (th) => th.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "#eff6ff",
266
+ flexShrink: 0
267
+ },
268
+ children: /* @__PURE__ */ jsx(ShoppingCartCheckoutIcon, { sx: { fontSize: { xs: 22, md: 28 }, color: "primary.main", opacity: 0.45 } })
269
+ }
270
+ ),
271
+ /* @__PURE__ */ jsx(Box, { sx: { flex: 1, minWidth: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "flex-start", sx: { mb: 0.25 }, children: [
272
+ /* @__PURE__ */ jsxs(Box, { sx: { minWidth: 0 }, children: [
273
+ /* @__PURE__ */ jsx(
274
+ Typography,
275
+ {
276
+ sx: { color: "text.primary", fontWeight: 800, fontSize: { xs: 15, md: 18 }, lineHeight: 1.3 },
277
+ children: name
278
+ }
279
+ ),
280
+ /* @__PURE__ */ jsx(
281
+ Typography,
282
+ {
283
+ component: "span",
284
+ sx: {
285
+ display: "inline-block",
286
+ mt: 0.5,
287
+ fontSize: 10,
288
+ fontWeight: 700,
289
+ letterSpacing: "0.08em",
290
+ lineHeight: 1,
291
+ textTransform: "uppercase",
292
+ color: "primary.main",
293
+ bgcolor: (th) => th.palette.mode === "dark" ? `${th.palette.primary.main}1A` : `${th.palette.primary.main}0D`,
294
+ px: 0.75,
295
+ py: 0.4,
296
+ borderRadius: "3px"
297
+ },
298
+ children: typeBadgeText
299
+ }
300
+ ),
301
+ subtitleText && /* @__PURE__ */ jsx(Typography, { sx: { color: "text.disabled", fontSize: 13, fontWeight: 500, mt: 0.25 }, children: subtitleText })
302
+ ] }),
303
+ /* @__PURE__ */ jsx(Stack, { alignItems: "flex-end", sx: { flexShrink: 0, ml: 1.5 }, children: trialActive && isSubscription ? /* @__PURE__ */ jsx(
304
+ Typography,
305
+ {
306
+ sx: { fontWeight: 800, color: "text.primary", whiteSpace: "nowrap", fontSize: { xs: 15, md: 18 } },
307
+ children: formatTrialText(t, trialDays, recurring?.interval || "day")
308
+ }
309
+ ) : isRateLoading ? /* @__PURE__ */ jsx(Skeleton, { variant: "text", width: 100, height: 28 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
310
+ /* @__PURE__ */ jsxs(
311
+ Typography,
312
+ {
313
+ sx: {
314
+ fontWeight: 800,
315
+ color: "text.primary",
316
+ whiteSpace: "nowrap",
317
+ fontSize: { xs: 15, md: 18 },
318
+ transition: "opacity 0.3s ease"
319
+ },
320
+ children: [
321
+ itemTotal,
322
+ " ",
323
+ currency?.symbol,
324
+ priceIntervalSuffix
325
+ ]
326
+ }
327
+ ),
328
+ exchangeRate && activePrice?.base_amount && /* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 12, color: "text.disabled", fontWeight: 600, lineHeight: 1 }, children: [
329
+ "\u2248 $",
330
+ (Number(activePrice.base_amount) * quantity).toFixed(2)
331
+ ] })
332
+ ] }) })
333
+ ] }) })
334
+ ] }),
335
+ showFeatures && features.length > 0 && /* @__PURE__ */ jsxs(
336
+ Box,
337
+ {
338
+ sx: {
339
+ mt: { xs: 2, md: 2.5 },
340
+ pt: { xs: 2, md: 2.5 },
341
+ borderTop: "1px solid",
342
+ borderColor: (th) => th.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "grey.100"
343
+ },
344
+ children: [
345
+ /* @__PURE__ */ jsxs(
346
+ Stack,
347
+ {
348
+ direction: "row",
349
+ alignItems: "center",
350
+ justifyContent: "space-between",
351
+ onClick: () => setFeaturesOpen((v) => !v),
352
+ sx: { cursor: "pointer", userSelect: "none", mb: featuresOpen ? 1.25 : 0 },
353
+ children: [
354
+ /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, fontWeight: 700, color: "text.primary" }, children: t("payment.checkout.planFeatures") }),
355
+ /* @__PURE__ */ jsx(
356
+ KeyboardArrowUpIcon,
357
+ {
358
+ sx: {
359
+ fontSize: 20,
360
+ color: "text.secondary",
361
+ transition: "transform 0.2s ease",
362
+ transform: featuresOpen ? "rotate(0deg)" : "rotate(180deg)"
363
+ }
364
+ }
365
+ )
366
+ ]
367
+ }
368
+ ),
369
+ /* @__PURE__ */ jsx(Collapse, { in: featuresOpen, children: /* @__PURE__ */ jsx(Stack, { spacing: 1.25, children: features.map((feature) => /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1.5, alignItems: "center", children: [
370
+ /* @__PURE__ */ jsx(
371
+ Box,
372
+ {
373
+ sx: {
374
+ width: 20,
375
+ height: 20,
376
+ borderRadius: "50%",
377
+ bgcolor: (th) => th.palette.mode === "dark" ? "rgba(16,185,129,0.15)" : "rgba(16,185,129,0.1)",
378
+ display: "flex",
379
+ alignItems: "center",
380
+ justifyContent: "center",
381
+ flexShrink: 0
382
+ },
383
+ children: /* @__PURE__ */ jsx(CheckIcon, { sx: { fontSize: 14, color: "success.main" } })
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, fontWeight: 500, color: "text.secondary" }, children: feature.name })
387
+ ] }, feature.name)) }) })
388
+ ]
389
+ }
390
+ ),
391
+ discountCode && perItemDiscount && /* @__PURE__ */ jsx(Box, { sx: { mt: 1.5 }, children: /* @__PURE__ */ jsx(
392
+ Chip,
393
+ {
394
+ icon: /* @__PURE__ */ jsx(LocalOfferIcon, { sx: { color: "warning.main", fontSize: "small" } }),
395
+ label: `${discountCode} (-${perItemDiscount})`,
396
+ size: "small",
397
+ sx: { height: 22, borderRadius: "6px", "& .MuiChip-label": { fontSize: 12 } }
398
+ }
399
+ ) }),
400
+ adjustable && /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", sx: { mt: 2 }, children: [
401
+ /* @__PURE__ */ jsx(Typography, { sx: { color: "text.secondary", minWidth: "fit-content", fontSize: 13, fontWeight: 500 }, children: t("common.quantity") }),
402
+ /* @__PURE__ */ jsx(
403
+ IconButton,
404
+ {
405
+ size: "small",
406
+ onClick: () => onQuantityChange(item.price_id, quantity - 1),
407
+ disabled: quantity <= min,
408
+ sx: {
409
+ width: 36,
410
+ height: 36,
411
+ border: "1px solid",
412
+ borderColor: "divider",
413
+ borderRadius: "50%",
414
+ "&:hover": { bgcolor: "action.hover" }
415
+ },
416
+ children: /* @__PURE__ */ jsx(RemoveIcon, { sx: { fontSize: 20 } })
417
+ }
418
+ ),
419
+ /* @__PURE__ */ jsx(
420
+ TextField,
421
+ {
422
+ value: qtyInput,
423
+ size: "small",
424
+ type: "number",
425
+ slotProps: {
426
+ htmlInput: {
427
+ style: {
428
+ textAlign: "center",
429
+ padding: "6px 4px",
430
+ fontWeight: 700,
431
+ fontSize: 16,
432
+ MozAppearance: "textfield"
433
+ },
434
+ min,
435
+ max: max || void 0
436
+ }
437
+ },
438
+ sx: {
439
+ minWidth: 64,
440
+ "& input": { textAlign: "center" },
441
+ "& .MuiOutlinedInput-root": { borderRadius: "12px" },
442
+ "& input::-webkit-outer-spin-button, & input::-webkit-inner-spin-button": {
443
+ WebkitAppearance: "none",
444
+ margin: 0
445
+ },
446
+ "& input[type=number]": {
447
+ MozAppearance: "textfield"
448
+ }
449
+ },
450
+ onFocus: () => setIsEditing(true),
451
+ onChange: (e) => setQtyInput(e.target.value),
452
+ onKeyDown: (e) => {
453
+ if (e.key === "Enter") {
454
+ e.preventDefault();
455
+ e.stopPropagation();
456
+ e.target.blur();
457
+ }
458
+ },
459
+ onBlur: () => {
460
+ setIsEditing(false);
461
+ const v = parseInt(qtyInput, 10);
462
+ if (!Number.isNaN(v) && v >= min && (!max || v <= max) && v !== quantity) {
463
+ onQuantityChange(item.price_id, v);
464
+ } else {
465
+ setQtyInput(String(quantity));
466
+ }
467
+ }
468
+ }
469
+ ),
470
+ /* @__PURE__ */ jsx(
471
+ IconButton,
472
+ {
473
+ size: "small",
474
+ onClick: () => onQuantityChange(item.price_id, quantity + 1),
475
+ disabled: max ? quantity >= max : false,
476
+ sx: {
477
+ width: 36,
478
+ height: 36,
479
+ border: "1px solid",
480
+ borderColor: "divider",
481
+ borderRadius: "50%",
482
+ "&:hover": { bgcolor: "action.hover" }
483
+ },
484
+ children: /* @__PURE__ */ jsx(AddIcon, { sx: { fontSize: 20 } })
485
+ }
486
+ )
487
+ ] }),
488
+ children
489
+ ]
490
+ }
491
+ ),
492
+ canUpsell && !hideUpsell && /* @__PURE__ */ jsxs(
493
+ Box,
494
+ {
495
+ sx: {
496
+ px: { xs: 2, md: 3 },
497
+ py: 1.5,
498
+ bgcolor: "background.paper",
499
+ border: "1px solid",
500
+ borderTop: 0,
501
+ borderColor: "divider",
502
+ borderRadius: { xs: "0 0 16px 16px", md: "0 0 24px 24px" },
503
+ boxShadow: (th) => th.palette.mode === "dark" ? "0 12px 40px -8px rgba(0,0,0,0.3)" : "0 12px 40px -8px rgba(0,0,0,0.06)"
504
+ },
505
+ children: [
506
+ /* @__PURE__ */ jsx(Box, { sx: { borderTop: "1px solid", borderColor: "divider", mb: 1.5 } }),
507
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", children: [
508
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [
509
+ /* @__PURE__ */ jsx(
510
+ Switch,
511
+ {
512
+ checked: isUpselled,
513
+ onChange: async () => {
514
+ try {
515
+ if (isUpselled) {
516
+ await onDownsell(item.upsell_price?.id || item.price.id);
517
+ } else {
518
+ await onUpsell(item.price_id, upsellTo.id);
519
+ }
520
+ } catch (err) {
521
+ Toast.error(err?.response?.data?.error || err?.message || "Failed");
522
+ }
523
+ },
524
+ size: "small",
525
+ sx: {
526
+ "& .MuiSwitch-switchBase.Mui-checked": { color: "#12b886" },
527
+ "& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track": { bgcolor: "#12b886" }
528
+ }
529
+ }
530
+ ),
531
+ /* @__PURE__ */ jsx(
532
+ Typography,
533
+ {
534
+ sx: { fontSize: 13, color: "text.secondary", cursor: "pointer", fontWeight: 600 },
535
+ onClick: async () => {
536
+ try {
537
+ if (isUpselled) await onDownsell(item.upsell_price?.id || item.price.id);
538
+ else await onUpsell(item.price_id, upsellTo.id);
539
+ } catch (err) {
540
+ Toast.error(err?.response?.data?.error || err?.message || "Failed");
541
+ }
542
+ },
543
+ children: upsellToggleLabel
544
+ }
545
+ ),
546
+ !isUpselled && savingsPercent > 0 && /* @__PURE__ */ jsx(
547
+ Chip,
548
+ {
549
+ label: t("payment.checkout.upsell.off", { saving: savingsPercent }),
550
+ size: "small",
551
+ sx: {
552
+ height: 22,
553
+ fontSize: 11,
554
+ fontWeight: 700,
555
+ bgcolor: (th) => th.palette.mode === "dark" ? "rgba(18,184,134,0.1)" : "#ebfef5",
556
+ color: "#12b886",
557
+ border: "1px solid",
558
+ borderColor: (th) => th.palette.mode === "dark" ? "rgba(18,184,134,0.2)" : "#d3f9e8",
559
+ borderRadius: "9999px",
560
+ "& .MuiChip-label": { px: 1 }
561
+ }
562
+ }
563
+ )
564
+ ] }),
565
+ /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 13, color: "text.primary", whiteSpace: "nowrap", fontWeight: 700 }, children: isUpselled ? downsellPrice : upsellPrice })
566
+ ] })
567
+ ]
568
+ }
569
+ )
570
+ ] });
571
+ }
@@ -0,0 +1,19 @@
1
+ interface PromotionInputProps {
2
+ promotion: {
3
+ applied: boolean;
4
+ code: string | null;
5
+ active: boolean;
6
+ inactiveReason: string | null;
7
+ apply: (code: string) => Promise<{
8
+ success: boolean;
9
+ error?: string;
10
+ }>;
11
+ remove: () => Promise<void>;
12
+ };
13
+ discounts: any[];
14
+ discountAmount: string | null;
15
+ /** Start with input field visible (skip the "Add promotion code" button) */
16
+ initialShowInput?: boolean;
17
+ }
18
+ export default function PromotionInput({ promotion, discounts, discountAmount, initialShowInput, }: PromotionInputProps): import("react").JSX.Element | null;
19
+ export {};