All files / features/live/components DonationFeed.tsx

100% Statements 5/5
100% Branches 8/8
100% Functions 2/2
100% Lines 5/5

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102                                  1x 2x 2x   2x             2x                                                                                                                                                  
import { motion, AnimatePresence } from 'framer-motion'
import { useTranslation } from 'react-i18next'
import { useCurrencyFormatter } from '@core/hooks/useCurrencyFormatter'
 
export interface Donation {
    amount: number // in cents
    currency: string
    donorName: string
    message?: string
    isAnonymous: boolean
    timestamp: number
}
 
interface DonationFeedProps {
    donations: Donation[]
}
 
export const DonationFeed = ({ donations }: DonationFeedProps) => {
    const { t } = useTranslation('common')
    const { formatCurrency } = useCurrencyFormatter()
 
    return (
        <div
            className="flex-1 overflow-hidden relative fade-mask-bottom"
            data-testid="recent-donations-list"
        >
            <AnimatePresence mode="popLayout">
                {donations.map((d) => (
                    <motion.div
                        key={`${d.donorName}-${d.timestamp}`}
                        layout
                        initial={{ opacity: 0, x: 50, scale: 0.8 }}
                        animate={{ opacity: 1, x: 0, scale: 1 }}
                        exit={{ opacity: 0, scale: 0.5, transition: { duration: 0.2 } }}
                        className="mb-4"
                    >
                        <div
                            className="p-5 rounded-2xl flex justify-between items-center shadow-lg relative overflow-hidden group border"
                            style={{
                                backgroundColor: 'var(--live-feed-item-bg)',
                                borderColor: 'var(--live-feed-item-border)',
                                backdropFilter: 'blur(var(--glass-blur))',
                            }}
                        >
                            {/* Glow Effect on Hover/Entry */}
                            <div className="absolute inset-0 bg-gradient-to-r from-purple-500/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity"></div>
 
                            <div className="flex items-center gap-4 z-10">
                                <div
                                    className="w-10 h-10 rounded-full flex items-center justify-center text-lg font-bold"
                                    style={{
                                        background:
                                            'linear-gradient(to bottom right, var(--live-avatar-bg-start), var(--live-avatar-bg-end))',
                                    }}
                                >
                                    {d.isAnonymous ? '?' : d.donorName.charAt(0).toUpperCase()}
                                </div>
                                <div>
                                    <p
                                        className="font-bold leading-tight"
                                        style={{ color: 'var(--live-text-main)' }}
                                    >
                                        {d.isAnonymous ? t('live.anonymous') : d.donorName}
                                    </p>
                                    {d.message && (
                                        <p
                                            className="text-sm line-clamp-1 italic max-w-[200px]"
                                            style={{ color: 'var(--live-text-secondary)' }}
                                        >
                                            "{d.message}"
                                        </p>
                                    )}
                                </div>
                            </div>
 
                            <div
                                className="text-2xl font-bold z-10"
                                style={{ color: 'var(--live-amount-color)' }}
                            >
                                +{formatCurrency(d.amount / 100, { currency: d.currency })}
                            </div>
                        </div>
                    </motion.div>
                ))}
            </AnimatePresence>
 
            {donations.length === 0 && (
                <div
                    className="h-full flex flex-col items-center justify-center space-y-4"
                    style={{ color: 'var(--live-text-muted)' }}
                >
                    <div
                        className="w-16 h-16 rounded-full border-2 border-dashed animate-spin-slow"
                        style={{ borderColor: 'var(--live-text-muted)' }}
                    ></div>
                    <p>{t('live.waiting')}</p>
                </div>
            )}
        </div>
    )
}