import { IconCircleCheck as CheckCircle, IconLoader2 as Loader2 } from '@tabler/icons-react'; import { useMutation } from '@tanstack/react-query'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; interface NewsletterSignupProps { className?: string; variant?: 'inline' | 'stacked'; buttonText?: string; } /** * Newsletter signup form. Embeddable in landing pages, footers, or standalone. * Uses double opt-in (sends confirmation email). */ export function NewsletterSignup({ className, variant = 'inline', buttonText, }: NewsletterSignupProps) { const { t } = useTranslation(); const resolvedButtonText = buttonText ?? t('landing.newsletter.button'); const [email, setEmail] = useState(''); const [validationError, setValidationError] = useState(null); const subscribeMutation = useMutation({ mutationFn: async () => { const response = await fetch('/api/v1/newsletter/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }), }); if (!response.ok) { const err = await response.json(); throw new Error(err.error || 'Failed to subscribe'); } return response.json(); }, onSuccess: () => { setEmail(''); setValidationError(null); }, }); const handleSubmit = () => { if (!email.trim()) { setValidationError(t('landing.newsletter.errorEmpty')); return; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { setValidationError(t('landing.newsletter.errorInvalid')); return; } setValidationError(null); subscribeMutation.mutate(); }; const handleEmailChange = (e: React.ChangeEvent) => { setEmail(e.target.value); if (validationError) setValidationError(null); }; const hasError = !!validationError || !!subscribeMutation.error; const errorMessage = validationError || subscribeMutation.error?.message; if (subscribeMutation.isSuccess) { return (
{t('landing.newsletter.success')}
); } if (variant === 'stacked') { return (
{ if (e.key === 'Enter') { e.preventDefault(); handleSubmit(); } }} /> {hasError && (

{errorMessage}

)}
); } return (
{ if (e.key === 'Enter') { e.preventDefault(); handleSubmit(); } }} />
{hasError && (

{errorMessage}

)}
); }