import { Stack, Typography, TextField, Card } from '@mui/material'; import { useLocaleContext } from '@arcblock/ux/lib/Locale/context'; import { useState, useMemo } from 'react'; import type { TPaymentCurrency } from '@blocklet/payment-types'; import ProductCard from '../../payment/product-card'; import { formatPrice, formatNumber, formatDynamicPrice, formatUsdAmount, formatExchangeRate, formatToDatetime, formatCreditForCheckout, } from '../../libs/util'; import QuoteDetailsPanel from '../quote-details-panel'; import type { SlippageConfigValue } from '../slippage-config'; interface ExchangeRateData { rate?: string; provider_name?: string; provider_id?: string; provider_display?: string; // Human-readable: "CoinGecko" or "CoinGecko (2 sources)" timestamp_ms?: number; } interface AutoTopupProductCardProps { product: any; price: any; currency: TPaymentCurrency; quantity: number; onQuantityChange: (quantity: number) => void; maxQuantity?: number; minQuantity?: number; creditCurrency: TPaymentCurrency; exchangeRate?: string | null; isDynamicPricing?: boolean; exchangeRateData?: ExchangeRateData | null; slippageConfig?: SlippageConfigValue | null; slippagePercent?: number; onSlippageChange?: (config: SlippageConfigValue) => void; disabled?: boolean; } export default function AutoTopupProductCard({ product, price, currency, quantity, onQuantityChange, maxQuantity = 99, minQuantity = 1, creditCurrency, exchangeRate = null, isDynamicPricing = false, exchangeRateData = null, slippageConfig = null, slippagePercent = 0.5, onSlippageChange = undefined, disabled = false, }: AutoTopupProductCardProps) { const { t, locale } = useLocaleContext(); const [localQuantity, setLocalQuantity] = useState(quantity); const localQuantityNum = Number(localQuantity) || 0; // Calculate payment amount for dynamic pricing const { paymentAmount, usdReferenceDisplay } = useMemo(() => { if (!isDynamicPricing || !exchangeRate || !price?.base_amount) { // Fixed pricing: use existing formatPrice return { paymentAmount: formatPrice(price, currency, product?.unit_label, localQuantity, true), usdReferenceDisplay: null, }; } // Dynamic pricing: calculate token amount from base_amount / exchange_rate const baseAmount = Number(price.base_amount) * localQuantityNum; const rate = Number(exchangeRate); if (rate <= 0 || !Number.isFinite(baseAmount)) { return { paymentAmount: formatPrice(price, currency, product?.unit_label, localQuantity, true), usdReferenceDisplay: null, }; } const tokenAmount = baseAmount / rate; const formattedToken = formatDynamicPrice(tokenAmount, true, 6); const formattedUsd = formatUsdAmount(baseAmount.toString(), locale); return { paymentAmount: `${formattedToken} ${currency.symbol}`, usdReferenceDisplay: formattedUsd ? `≈ $${formattedUsd}` : null, }; }, [isDynamicPricing, exchangeRate, price, currency, product?.unit_label, localQuantity, localQuantityNum, locale]); const handleQuantityChange = (newQuantity: number) => { if (!newQuantity) { setLocalQuantity(undefined); return; } if (newQuantity >= minQuantity && newQuantity <= maxQuantity) { setLocalQuantity(newQuantity); onQuantityChange(newQuantity); } }; const handleQuantityInputChange = (event: React.ChangeEvent) => { const value = parseInt(event.target.value || '0', 10); if (!Number.isNaN(value)) { handleQuantityChange(value); } }; const creditUnitAmount = Number(price.metadata?.credit_config?.credit_amount || 0); return ( {t('common.quantity')}: {/* 充值金额显示 */} {t('payment.autoTopup.rechargeAmount')} {paymentAmount} {usdReferenceDisplay && ( {usdReferenceDisplay} )} {/* Dynamic Pricing - Exchange Rate Panel */} {isDynamicPricing && exchangeRateData?.rate && ( )} ); }