import React, { useState } from 'react'; import { CardElement, AddressElement, useStripe, useElements } from '@stripe/react-stripe-js'; import { format } from 'date-fns'; import toast from 'react-hot-toast'; import { GlassCard } from '../../../shared-minimal/components/mobile/GlassCard'; import { useCreateDonation, useDonations } from '../hooks/useSubscription'; import type { StripeCardElementChangeEvent, StripeAddressElementChangeEvent } from '@stripe/stripe-js'; import type { SubscriptionTier } from '../types/subscription.types'; interface DonationSectionMobileProps { currentTier?: SubscriptionTier; } const CARD_ELEMENT_OPTIONS = { style: { base: { fontSize: '16px', color: '#424770', '::placeholder': { color: '#aab7c4', }, }, invalid: { color: '#9e2146', }, }, }; export const DonationSectionMobile: React.FC = ({ currentTier }) => { const stripe = useStripe(); const elements = useElements(); const [amount, setAmount] = useState(''); const [error, setError] = useState(null); const [processing, setProcessing] = useState(false); const [cardComplete, setCardComplete] = useState(false); const [addressComplete, setAddressComplete] = useState(false); const [showSuccess, setShowSuccess] = useState(false); const createDonationMutation = useCreateDonation(); const { data: donationsData, isLoading: isLoadingDonations } = useDonations(); const donations = donationsData?.data || []; const handleCardChange = (event: StripeCardElementChangeEvent) => { setError(event.error?.message || null); setCardComplete(event.complete); }; const handleAddressChange = (event: StripeAddressElementChangeEvent) => { setAddressComplete(event.complete); }; const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); if (!stripe || !elements) { return; } const cardElement = elements.getElement(CardElement); if (!cardElement) { return; } const addressElement = elements.getElement(AddressElement); if (!addressElement) { return; } // Validate amount const amountNum = parseFloat(amount); if (isNaN(amountNum) || amountNum < 0.5) { setError('Minimum donation amount is $0.50'); return; } // Get billing address data const addressData = await addressElement.getValue(); if (!addressData.complete) { setError('Please complete the billing address'); return; } setProcessing(true); setError(null); try { // Create donation payment intent const donationResponse = await createDonationMutation.mutateAsync(amountNum); const { clientSecret } = donationResponse.data; // Confirm payment with Stripe including billing details const { error: confirmError } = await stripe.confirmCardPayment(clientSecret, { payment_method: { card: cardElement, billing_details: { name: addressData.value.name, address: { line1: addressData.value.address.line1, line2: addressData.value.address.line2 || undefined, city: addressData.value.address.city, state: addressData.value.address.state, postal_code: addressData.value.address.postal_code, country: addressData.value.address.country, }, }, }, }); if (confirmError) { setError(confirmError.message || 'Payment failed'); setProcessing(false); return; } // Success! setShowSuccess(true); setAmount(''); cardElement.clear(); toast.success('Thank you for your donation!'); // Hide success message after 5 seconds setTimeout(() => { setShowSuccess(false); }, 5000); } catch (err: unknown) { const error = err as { response?: { data?: { error?: string } } }; setError(error.response?.data?.error || 'An unexpected error occurred'); } finally { setProcessing(false); } }; const isFormValid = amount && parseFloat(amount) >= 0.5 && cardComplete && addressComplete && !processing; return (

Support MotoVaultPro

{currentTier === 'free' && (

Love MotoVaultPro? Consider making a one-time donation to support development!

)}

Your donations help us continue to improve and maintain MotoVaultPro. Thank you for your support!

{showSuccess && (

Thank you for your generous donation! Your support means the world to us.

)}
$ setAmount(e.target.value)} placeholder="Enter amount" min="0.5" step="0.01" disabled={processing} className="w-full pl-8 pr-4 py-3 bg-white dark:bg-nero border border-slate-200 dark:border-grigio rounded-xl text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-titanio focus:outline-none focus:ring-2 focus:ring-rose-500 min-h-[44px]" />
{error && (

{error}

)}
{donations.length > 0 && (

Donation History

{isLoadingDonations ? (
) : (
{donations.map((donation: any) => (
{format(new Date(donation.createdAt), 'MMM dd, yyyy')}
${(donation.amountCents / 100).toFixed(2)}
{donation.status}
))}
)}
)}
); };