UNPKG

@blocklet/payment-react

Version:

Reusable react components for payment kit v2

804 lines (803 loc) 29.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); module.exports = CreditTopupPanel; var _jsxRuntime = require("react/jsx-runtime"); var _react = require("react"); var _material = require("@mui/material"); var _InfoOutlined = _interopRequireDefault(require("@mui/icons-material/InfoOutlined")); var _AccessTimeOutlined = _interopRequireDefault(require("@mui/icons-material/AccessTimeOutlined")); var _Inventory2Outlined = _interopRequireDefault(require("@mui/icons-material/Inventory2Outlined")); var _ahooks = require("ahooks"); var _util = require("@ocap/util"); var _context = require("@arcblock/ux/lib/Locale/context"); var _WarningAmber = _interopRequireDefault(require("@mui/icons-material/WarningAmber")); var _paymentReactHeadless = require("@blocklet/payment-react-headless"); var _payment = require("../../../contexts/payment"); var _api = _interopRequireDefault(require("../../../libs/api")); var _util2 = require("../../../libs/util"); var _format = require("../../utils/format"); var _scenarioBadge = _interopRequireDefault(require("../../components/shared/scenario-badge")); var _exchangeRateFooter = _interopRequireDefault(require("../../components/shared/exchange-rate-footer")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function CreditTopupPanel() { const { t, locale } = (0, _context.useLocaleContext)(); const { session, sessionData } = (0, _paymentReactHeadless.useSessionContext)(); const { livemode } = (0, _paymentReactHeadless.useCheckoutStatus)(); const { product } = (0, _paymentReactHeadless.useProduct)(); const { currency, isStripe } = (0, _paymentReactHeadless.usePaymentMethodContext)(); const lineItems = (0, _paymentReactHeadless.useLineItems)(); const rate = (0, _paymentReactHeadless.useExchangeRate)(); const slippage = (0, _paymentReactHeadless.useSlippage)(); const { locked: configLocked } = (0, _paymentReactHeadless.useSubmitFeature)(); const item = lineItems.items[0]; const activePrice = item ? item.upsell_price || item.price : null; const creditConfig = activePrice?.metadata?.credit_config; const creditAmount = creditConfig?.credit_amount ? Number(creditConfig.credit_amount) : 0; const creditCurrencyId = creditConfig?.currency_id; const { session: didSession, getCurrency } = (0, _payment.usePaymentContext)(); const creditCurrency = creditCurrencyId ? getCurrency(creditCurrencyId) : null; const userDid = didSession?.user?.did || sessionData?.customer?.did; const creditCurrencyDecimal = creditCurrency?.decimal; const creditCurrencySymbol = creditCurrency?.symbol || "Credits"; const currencySymbol = "Credits"; const meterId = activePrice?.metadata?.meter_id; const { data: meterData } = (0, _ahooks.useRequest)(async () => { if (!meterId) return null; try { const { data } = await _api.default.get(`/api/meters/public/${meterId}`); return data; } catch { return null; } }, { refreshDeps: [meterId] }); const creditName = meterData?.name || product?.name || "Credits"; const validDuration = creditConfig?.valid_duration_value; const validDurationUnit = creditConfig?.valid_duration_unit || "days"; const scheduleConfig = creditConfig?.schedule; const hasSchedule = scheduleConfig?.enabled && scheduleConfig?.delivery_mode && scheduleConfig.delivery_mode !== "invoice"; const hasExpiry = validDuration && validDuration > 0; const step = Math.max(creditAmount, 1); const adjustableQty = item?.adjustable_quantity; const canAdjust = adjustableQty?.enabled !== false; const minQuantity = Math.max(adjustableQty?.minimum || 1, 1); const quantityAvailable = Math.min(activePrice?.quantity_limit_per_checkout ?? Infinity, activePrice?.quantity_available ?? Infinity); const maxQuantity = quantityAvailable ? Math.min(adjustableQty?.maximum || Infinity, quantityAvailable) : adjustableQty?.maximum || Infinity; const { data: pendingAmount } = (0, _ahooks.useRequest)(async () => { if (!creditConfig || !userDid || !creditCurrencyId || creditCurrencyDecimal == null) return null; try { const { data } = await _api.default.get("/api/meter-events/pending-amount", { params: { customer_id: userDid, currency_id: creditCurrencyId } }); return data?.[creditCurrencyId]; } catch { return null; } }, { refreshDeps: [creditConfig, userDid, creditCurrencyId, creditCurrencyDecimal] }); const minQtyForPending = (0, _react.useMemo)(() => { if (!pendingAmount || !creditAmount || creditAmount <= 0) return null; const pendingBN = new _util.BN(pendingAmount || "0"); if (!pendingBN.gt(new _util.BN(0))) return null; const creditBN = (0, _util.fromTokenToUnit)(creditAmount, creditCurrencyDecimal); if (!creditBN || creditBN.isZero()) return null; return Math.ceil(pendingBN.mul(new _util.BN(100)).div(creditBN).toNumber() / 100); }, [pendingAmount, creditAmount, creditCurrencyDecimal]); const hasPendingAmount = pendingAmount && new _util.BN(pendingAmount || "0").gt(new _util.BN(0)); const pendingDisplayAmount = (0, _react.useMemo)(() => { if (!hasPendingAmount) return ""; return (0, _util2.formatCreditForCheckout)((0, _util2.formatBNStr)(pendingAmount, creditCurrencyDecimal), creditCurrencySymbol, locale); }, [hasPendingAmount, pendingAmount, creditCurrencyDecimal, creditCurrencySymbol, locale]); const minCreditsForPending = minQtyForPending ? minQtyForPending * step : 0; const minCreditsForPendingFormatted = minCreditsForPending ? (0, _util2.formatCreditForCheckout)((0, _util2.formatNumber)(minCreditsForPending), creditCurrencySymbol, locale) : ""; const creditInfoText = (0, _react.useMemo)(() => { if (hasSchedule && scheduleConfig) { const intervalUnit = scheduleConfig.interval_unit; const intervalValue = scheduleConfig.interval_value; let amountPerGrant; if (scheduleConfig.amount_per_grant) { amountPerGrant = Number(scheduleConfig.amount_per_grant); } else { amountPerGrant = creditAmount; } const formattedAmount = (0, _util2.formatCreditAmount)((0, _util2.formatNumber)(amountPerGrant), currencySymbol); const intervalDisplay = intervalValue === 1 ? t(`common.${intervalUnit}`) : `${intervalValue} ${t(`common.${intervalUnit}s`)}`; return scheduleConfig.expire_with_next_grant ? t("payment.checkout.credit.schedule.withRefresh", { amount: formattedAmount, interval: intervalDisplay }) : t("payment.checkout.credit.schedule.periodic", { amount: formattedAmount, interval: intervalDisplay }); } return ""; }, [creditAmount, currencySymbol, hasSchedule, scheduleConfig, t]); const validityText = (0, _react.useMemo)(() => { if (!hasExpiry) return ""; return t("payment.checkout.creditTopup.validFor", { duration: validDuration, unit: t(`common.${validDurationUnit}`) }); }, [hasExpiry, validDuration, validDurationUnit, t]); const hasSubtitle = !!creditInfoText; const currentQty = item?.quantity || 1; const [localQty, setLocalQty] = (0, _react.useState)(currentQty); const [desiredCredits, setDesiredCredits] = (0, _react.useState)(currentQty * step); const [isEditing, setIsEditing] = (0, _react.useState)(false); const [editValue, setEditValue] = (0, _react.useState)(""); const inputRef = (0, _react.useRef)(null); (0, _react.useEffect)(() => { if (configLocked || session?.status === "complete") return; if (item?.quantity && item.quantity !== localQty) { setLocalQty(item.quantity); setDesiredCredits(item.quantity * step); } }, [item?.quantity]); const pendingEnforcedRef = (0, _react.useRef)(false); (0, _react.useEffect)(() => { if (configLocked || session?.status === "complete") return; if (pendingEnforcedRef.current) return; if (minQtyForPending && minQtyForPending > localQty) { pendingEnforcedRef.current = true; const newQty = Math.min(Math.max(minQtyForPending, minQuantity), maxQuantity); setLocalQty(newQty); setDesiredCredits(newQty * step); if (item) lineItems.updateQuantity(item.price_id, newQty); } }, [minQtyForPending]); const maxCredits = maxQuantity * step; const minCredits = minQuantity * step; const debounceRef = (0, _react.useRef)(null); (0, _react.useEffect)(() => () => { if (debounceRef.current) clearTimeout(debounceRef.current); }, []); const commitCredits = (0, _react.useCallback)(credits => { if (credits <= 0) return; const clamped = Math.max(minCredits, Math.min(maxCredits, credits)); const packs = Math.ceil(clamped / step); const clampedPacks = Math.max(minQuantity, Math.min(maxQuantity, packs)); setLocalQty(clampedPacks); setDesiredCredits(clamped); if (item) { if (debounceRef.current) clearTimeout(debounceRef.current); debounceRef.current = setTimeout(() => { lineItems.updateQuantity(item.price_id, clampedPacks); }, 400); } }, [step, minQuantity, maxQuantity, minCredits, maxCredits, item, lineItems]); const handleStep = (0, _react.useCallback)(delta => { const newCredits = desiredCredits + delta * step; if (newCredits < step * minQuantity || Math.ceil(newCredits / step) > maxQuantity) return; commitCredits(newCredits); }, [desiredCredits, step, minQuantity, maxQuantity, commitCredits]); const actualPacks = Math.max(minQuantity, Math.min(maxQuantity, Math.ceil(desiredCredits / step))); const actualCredits = actualPacks * step; const actualCreditsFormatted = (0, _util2.formatNumber)(actualCredits, 6, true, true); const desiredCreditsFormatted = (0, _util2.formatNumber)(desiredCredits, 6, true, true); const showReceiveSection = canAdjust && step > 1; const pendingMessage = (0, _react.useMemo)(() => { if (!hasPendingAmount) return ""; if (actualCredits >= minCreditsForPending) { return t("payment.checkout.creditTopup.pendingEnough", { pendingAmount: pendingDisplayAmount, availableAmount: (0, _util2.formatCreditForCheckout)((0, _util2.formatNumber)(actualCredits - minCreditsForPending), creditCurrencySymbol, locale) }); } return t("payment.checkout.creditTopup.pendingWarning", { pendingAmount: pendingDisplayAmount, minCredits: minCreditsForPendingFormatted }); }, [hasPendingAmount, actualCredits, minCreditsForPending, pendingDisplayAmount, creditCurrencySymbol, locale, minCreditsForPendingFormatted, t]); const startEditing = (0, _react.useCallback)(() => { if (!canAdjust) return; setEditValue(String(desiredCredits)); setIsEditing(true); setTimeout(() => inputRef.current?.select(), 0); }, [canAdjust, desiredCredits]); const commitEdit = (0, _react.useCallback)(() => { setIsEditing(false); const parsed = parseInt(editValue.replace(/,/g, ""), 10); if (!Number.isFinite(parsed) || parsed <= 0) return; commitCredits(parsed); }, [editValue, commitCredits]); const handleEditKeyDown = (0, _react.useCallback)(e => { if (e.key === "Enter") commitEdit();else if (e.key === "Escape") setIsEditing(false); }, [commitEdit]); const numberHeight = { xs: 48, md: 72 }; const numberFontSx = { fontSize: numberHeight, fontWeight: 800, lineHeight: 1, letterSpacing: "-0.03em" }; const circleBtnSx = { width: { xs: 40, md: 56 }, height: { xs: 40, md: 56 }, borderRadius: "50%", bgcolor: "background.paper", border: "1px solid", borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.12)" : "divider", color: "text.secondary", transition: "all 0.2s ease", "&:hover": { borderColor: "primary.main", color: "primary.main", bgcolor: "background.paper" }, "&.Mui-disabled": { borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)", color: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.12)" } }; return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", flexDirection: "column", height: "100%" }, children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { flex: 1, display: "flex", flexDirection: "column", justifyContent: "center" }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_scenarioBadge.default, { livemode, label: (0, _format.tSafe)(t, "payment.checkout.typeBadge.topup", "TOP-UP") }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontWeight: 800, fontSize: { xs: 28, md: 48 }, lineHeight: 1.1, letterSpacing: "-0.03em", color: "text.primary", mb: { xs: 0.5, md: 1 } }, children: t("payment.checkout.creditTopup.title", { name: creditName }) }), hasSubtitle && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, { sx: { display: "flex", alignItems: "flex-start", gap: 1, mb: { xs: 1.5, md: 2.5 } }, children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 13, md: 15 }, fontWeight: 500, color: "text.secondary", lineHeight: 1.5 }, children: creditInfoText }) }), hasPendingAmount && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, { direction: "row", alignItems: "flex-start", spacing: 1.5, sx: { mb: { xs: 1.5, md: 2.5 }, p: { xs: 1.5, md: 2 }, borderRadius: "12px", bgcolor: theme => theme.palette.mode === "dark" ? "rgba(255,152,0,0.12)" : "rgba(255,152,0,0.08)", border: "1px solid", borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,152,0,0.25)" : "rgba(255,152,0,0.2)" }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_WarningAmber.default, { sx: { fontSize: 18, color: "warning.main", mt: 0.25, flexShrink: 0 } }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 12, md: 13 }, fontWeight: 600, lineHeight: 1.5, color: "text.primary" }, children: pendingMessage })] }), canAdjust ? /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", flexDirection: "column", maxWidth: 480 }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 14, md: 17 }, fontWeight: 700, color: "text.secondary", mt: { xs: 3, md: 5 }, mb: { xs: 2, md: 3 } }, children: t("payment.checkout.creditTopup.question", { symbol: currencySymbol }) }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", alignItems: "center", gap: { xs: 1.5, md: 2 } }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, { onClick: () => handleStep(-1), disabled: actualPacks <= minQuantity, sx: circleBtnSx, children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, { component: "span", sx: { fontSize: { xs: 22, md: 28 }, fontWeight: 300, lineHeight: 1, mt: "-1px" }, children: "\u2212" }) }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { flex: 1, minWidth: 0, textAlign: "center", cursor: canAdjust ? "text" : "default" }, onClick: startEditing, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, { sx: { height: numberHeight, display: "flex", alignItems: "center", justifyContent: "center" }, children: isEditing ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.InputBase, { inputRef, value: editValue, onChange: e => setEditValue(e.target.value.replace(/\D/g, "")), onBlur: commitEdit, onKeyDown: handleEditKeyDown, autoFocus: true, inputProps: { inputMode: "numeric", pattern: "[0-9]*" }, sx: { width: "100%", bgcolor: "transparent", "&.Mui-focused": { bgcolor: "transparent" }, "& input": { textAlign: "center", ...numberFontSx, p: 0, fontFamily: "inherit", bgcolor: "transparent", "&:focus": { bgcolor: "transparent" } } } }) : /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { ...numberFontSx, color: "text.primary", userSelect: "none" }, children: desiredCreditsFormatted }) }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, { sx: { display: "flex", alignItems: "center", justifyContent: "center", gap: 0.75, mt: 1 }, children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 12, md: 14 }, fontWeight: 700, color: "text.secondary", textTransform: "uppercase", letterSpacing: "0.05em" }, children: currencySymbol }) })] }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, { onClick: () => handleStep(1), disabled: actualPacks >= maxQuantity, sx: circleBtnSx, children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, { component: "span", sx: { fontSize: { xs: 22, md: 28 }, fontWeight: 300, lineHeight: 1 }, children: "+" }) })] }), step > 1 && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: 11, fontWeight: 600, color: "grey.500", textTransform: "uppercase", letterSpacing: "0.08em", textAlign: "center", mt: { xs: 1.5, md: 2 } }, children: t("payment.checkout.creditTopup.increment", { step: (0, _util2.formatNumber)(step, 6, true, true), symbol: currencySymbol }) }), showReceiveSection && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", flexDirection: "column", mt: { xs: 3, md: 4 }, borderRadius: "20px", backdropFilter: "blur(12px)", bgcolor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.08)" : "rgba(59,130,246,0.04)", border: "1px solid", borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.18)" : "rgba(59,130,246,0.10)", boxShadow: theme => theme.palette.mode === "dark" ? "0 8px 32px rgba(59,130,246,0.06)" : "0 8px 32px rgba(59,130,246,0.04)" }, children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { p: { xs: 2.5, md: 3.5 } }, children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, { sx: { fontSize: { xs: 16, md: 20 }, fontWeight: 700, color: "primary.main", mb: 0.5 }, children: [t("payment.checkout.creditTopup.willReceive"), ": ", /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, { component: "span", sx: { fontWeight: 800 }, children: (0, _util2.formatCreditForCheckout)(actualCreditsFormatted, currencySymbol, locale) })] }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", alignItems: "center", gap: 0.75, mb: 0.5 }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_Inventory2Outlined.default, { sx: { fontSize: 16, color: "text.secondary" } }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 12, md: 14 }, fontWeight: 600, color: "text.secondary" }, children: t("payment.checkout.creditTopup.packInfo", { packs: actualPacks, perPack: (0, _util2.formatNumber)(step, 6, true, true) }) })] }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 12, md: 13 }, fontWeight: 500, color: "grey.500" }, children: t("payment.checkout.creditTopup.autoMatch") })] }), validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", alignItems: "center", gap: 0.75, px: { xs: 2.5, md: 3.5 }, py: { xs: 1.5, md: 2 }, borderTop: "1px solid", borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.15)" : "rgba(59,130,246,0.08)" }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_AccessTimeOutlined.default, { sx: { fontSize: 14, color: "grey.500" } }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 12, md: 13 }, fontWeight: 500, color: "grey.500" }, children: validityText })] })] }), !showReceiveSection && validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", alignItems: "center", gap: 0.75, mt: { xs: 2.5, md: 3.5 } }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_InfoOutlined.default, { sx: { fontSize: 14, color: "grey.500" } }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 12, md: 13 }, fontWeight: 500, color: "grey.500" }, children: validityText })] })] }) : // ── Non-adjustable: show fixed credit amount in a styled card ── /* @__PURE__ */ (0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", flexDirection: "column", mt: { xs: 2, md: 3 }, borderRadius: "20px", backdropFilter: "blur(12px)", bgcolor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.08)" : "rgba(59,130,246,0.04)", border: "1px solid", borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.18)" : "rgba(59,130,246,0.10)", boxShadow: theme => theme.palette.mode === "dark" ? "0 8px 32px rgba(59,130,246,0.06)" : "0 8px 32px rgba(59,130,246,0.04)" }, children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { p: { xs: 2.5, md: 3.5 } }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 13, md: 14 }, fontWeight: 600, color: "text.secondary", mb: 1 }, children: t("payment.checkout.creditTopup.willReceive") }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, { sx: { display: "flex", alignItems: "center", gap: 1.5 }, children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 32, md: 48 }, fontWeight: 800, lineHeight: 1, letterSpacing: "-0.03em", color: "primary.main" }, children: (0, _util2.formatCreditForCheckout)(actualCreditsFormatted, currencySymbol, locale) }) })] }), validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { display: "flex", alignItems: "center", gap: 0.75, px: { xs: 2.5, md: 3.5 }, py: { xs: 1.5, md: 2 }, borderTop: "1px solid", borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.15)" : "rgba(59,130,246,0.08)" }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_AccessTimeOutlined.default, { sx: { fontSize: 14, color: "grey.500" } }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: { xs: 12, md: 13 }, fontWeight: 500, color: "grey.500" }, children: validityText })] })] })] }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, { sx: { flexShrink: 0 }, children: [rate.hasDynamicPricing && rate.status === "unavailable" && !isStripe && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, { direction: "row", alignItems: "center", spacing: 0.75, sx: { mb: 2 }, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { sx: { fontSize: 13, color: "text.secondary", fontWeight: 500 }, children: t("payment.dynamicPricing.unavailable.title") }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { component: "span", onClick: rate.refresh, sx: { fontSize: 13, color: "primary.main", fontWeight: 600, cursor: "pointer", "&:hover": { textDecoration: "underline" } }, children: t("payment.dynamicPricing.unavailable.retry") })] }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_exchangeRateFooter.default, { hasDynamicPricing: rate.hasDynamicPricing, rate: { value: rate.value, display: rate.display, provider: rate.provider, providerDisplay: rate.providerDisplay, fetchedAt: rate.fetchedAt, status: rate.status }, slippage: { percent: slippage.percent, set: slippage.set }, currencySymbol: currency?.symbol || "", isSubscription: ["subscription", "setup"].includes(session?.mode || "payment") })] })] }); }