/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { useState, useEffect } from 'react' import { Gift, ExternalLink, Loader2, Receipt, WalletCards } from 'lucide-react' import { useTranslation } from 'react-i18next' import { formatNumber } from '@/lib/format' import { cn } from '@/lib/utils' import { Alert, AlertDescription } from '@/components/ui/alert' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Skeleton } from '@/components/ui/skeleton' import { TitledCard } from '@/components/ui/titled-card' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' import { formatCurrency, getDiscountLabel, getPaymentIcon, getMinTopupAmount, calculatePresetPricing, } from '../lib' import type { PaymentMethod, PresetAmount, TopupInfo, CreemProduct, WaffoPayMethod, } from '../types' import { CreemProductsSection } from './creem-products-section' interface RechargeFormCardProps { topupInfo: TopupInfo | null presetAmounts: PresetAmount[] selectedPreset: number | null onSelectPreset: (preset: PresetAmount) => void topupAmount: number onTopupAmountChange: (amount: number) => void paymentAmount: number calculating: boolean onPaymentMethodSelect: (method: PaymentMethod) => void paymentLoading: string | null redemptionCode: string onRedemptionCodeChange: (code: string) => void onRedeem: () => void redeeming: boolean topupLink?: string loading?: boolean priceRatio?: number usdExchangeRate?: number onOpenBilling?: () => void creemProducts?: CreemProduct[] enableCreemTopup?: boolean onCreemProductSelect?: (product: CreemProduct) => void enableWaffoTopup?: boolean waffoPayMethods?: WaffoPayMethod[] waffoMinTopup?: number onWaffoMethodSelect?: (method: WaffoPayMethod, index: number) => void enableWaffoPancakeTopup?: boolean } export function RechargeFormCard({ topupInfo, presetAmounts, selectedPreset, onSelectPreset, topupAmount, onTopupAmountChange, paymentAmount, calculating, onPaymentMethodSelect, paymentLoading, redemptionCode, onRedemptionCodeChange, onRedeem, redeeming, topupLink, loading, priceRatio = 1, usdExchangeRate = 1, onOpenBilling, creemProducts, enableCreemTopup, onCreemProductSelect, enableWaffoTopup, waffoPayMethods, waffoMinTopup, onWaffoMethodSelect, enableWaffoPancakeTopup, }: RechargeFormCardProps) { const { t } = useTranslation() const [localAmount, setLocalAmount] = useState(topupAmount.toString()) useEffect(() => { setLocalAmount(topupAmount.toString()) }, [topupAmount]) const handleAmountChange = (value: string) => { setLocalAmount(value) const numValue = parseInt(value) || 0 if (numValue >= 0) { onTopupAmountChange(numValue) } } const hasConfigurableTopup = topupInfo?.enable_online_topup || topupInfo?.enable_stripe_topup || enableWaffoTopup || enableWaffoPancakeTopup const hasAnyTopup = hasConfigurableTopup || enableCreemTopup const hasStandardPaymentMethods = Array.isArray(topupInfo?.pay_methods) && topupInfo.pay_methods.length > 0 const hasWaffoPaymentMethods = Array.isArray(waffoPayMethods) && waffoPayMethods.length > 0 const minTopup = getMinTopupAmount(topupInfo) if (loading) { return (
{/* Preset Amounts Skeleton */}
{Array.from({ length: 8 }).map((_, i) => ( ))}
{/* Custom Amount Input Skeleton */}
{/* Payment Methods Skeleton */}
{Array.from({ length: 3 }).map((_, i) => ( ))}
{/* Redemption Code Section Skeleton */}
) } return ( } action={ onOpenBilling ? ( ) : null } contentClassName='space-y-4 sm:space-y-6' > {/* Online Topup Section */} {hasAnyTopup ? (
{hasConfigurableTopup && ( <> {presetAmounts.length > 0 && (
{presetAmounts.map((preset, index) => { const discount = preset.discount || topupInfo?.discount?.[preset.value] || 1.0 const { displayValue, actualPrice, savedAmount, hasDiscount, } = calculatePresetPricing( preset.value, priceRatio, discount, usdExchangeRate ) return ( ) })}
)}
handleAmountChange(e.target.value)} min={minTopup} placeholder={`Minimum ${minTopup}`} className='h-9 text-base sm:h-10 sm:text-lg' />
{t('Amount to pay:')} {calculating ? ( ) : ( {formatCurrency(paymentAmount)} )}
{hasStandardPaymentMethods ? (
{topupInfo?.pay_methods?.map((method) => { const minTopup = method.min_topup || 0 const disabled = minTopup > topupAmount const button = ( ) return disabled ? ( {t('Minimum topup amount: {{amount}}', { amount: minTopup, })} ) : ( button ) })}
) : hasWaffoPaymentMethods ? null : ( {t( 'No payment methods available. Please contact administrator.' )} )}
{enableWaffoTopup && hasWaffoPaymentMethods && onWaffoMethodSelect && (
{waffoPayMethods?.map((method, index) => { const loadingKey = `waffo-${index}` const waffoMin = waffoMinTopup || 0 const belowMin = waffoMin > topupAmount const button = ( ) return belowMin ? ( {t('Minimum topup amount: {{amount}}', { amount: waffoMin, })} ) : ( button ) })}
)} )}
) : ( {t( 'Online topup is not enabled. Please use redemption code or contact administrator.' )} )} {/* Creem Products Section */} {enableCreemTopup && Array.isArray(creemProducts) && creemProducts.length > 0 && onCreemProductSelect && (
)} {/* Redemption Code Section */}
onRedemptionCodeChange(e.target.value)} placeholder={t('Enter your redemption code')} className='h-9 min-w-0' />
{topupLink && (

{t('Need a redemption code?')}{' '} {t('Get one here')}

)}
) }