'use client'; import { useState, FormEvent } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; type FeedbackState = | { type: 'idle' } | { type: 'loading' } | { type: 'success'; message: string } | { type: 'error'; message: string; canRetry?: boolean; retryAfter?: number } | { type: 'info'; message: string }; export default function EmailCapture() { const [email, setEmail] = useState(''); const [feedback, setFeedback] = useState({ type: 'idle' }); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); if (!email) { setFeedback({ type: 'error', message: "That email doesn't look right." }); return; } setFeedback({ type: 'loading' }); try { const response = await fetch('/api/signup', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email }), }); const data = await response.json(); if (!response.ok) { if (response.status === 429) { setFeedback({ type: 'error', message: data.error, retryAfter: data.retryAfter }); } else if (response.status === 409) { setFeedback({ type: 'info', message: data.error }); setTimeout(() => setFeedback({ type: 'idle' }), 4000); } else { setFeedback({ type: 'error', message: data.error || 'Something broke. Try again?', canRetry: true }); } return; } setFeedback({ type: 'success', message: data.message }); setEmail(''); setTimeout(() => setFeedback({ type: 'idle' }), 5000); } catch (error) { setFeedback({ type: 'error', message: 'Something broke. Try again?', canRetry: true }); console.error('Signup error:', error); } }; const getMessageColor = () => { if (feedback.type === 'success') return 'text-performance-green'; if (feedback.type === 'info') return 'text-gray-400'; if (feedback.type === 'error' && feedback.retryAfter) return 'text-warning-amber'; if (feedback.type === 'error') return 'text-red-400'; return ''; }; const getGlowColor = () => { if (feedback.type === 'success') return 'shadow-[0_0_80px_rgba(111,227,122,0.15)]'; if (feedback.type === 'info') return 'shadow-[0_0_80px_rgba(34,38,42,0.15)]'; if (feedback.type === 'error' && feedback.retryAfter) return 'shadow-[0_0_80px_rgba(255,197,86,0.15)]'; if (feedback.type === 'error') return 'shadow-[0_0_80px_rgba(248,113,113,0.15)]'; return 'shadow-[0_0_80px_rgba(25,140,255,0.15)]'; }; return (
{feedback.type === 'success' ? (

{feedback.message}

I'll send updates as I build. Zero spam, zero BS.

) : (

Let me know if this resonates

I'm building GridPilot because I got tired of the chaos. If this resonates with you, drop your email.

It means someone out there cares about the same problems. That keeps me going.

{ setEmail(e.target.value); if (feedback.type !== 'loading') { setFeedback({ type: 'idle' }); } }} placeholder="your@email.com" disabled={feedback.type === 'loading'} className={`w-full px-6 py-4 rounded-lg bg-iron-gray text-white placeholder-gray-500 border transition-all duration-150 ${ feedback.type === 'error' && !feedback.retryAfter ? 'border-red-500 focus:ring-2 focus:ring-red-500' : feedback.type === 'error' && feedback.retryAfter ? 'border-warning-amber focus:ring-2 focus:ring-warning-amber/50' : 'border-charcoal-outline focus:border-neon-aqua focus:ring-2 focus:ring-neon-aqua/50' } hover:scale-[1.01] disabled:opacity-50 disabled:cursor-not-allowed`} aria-label="Email address" /> {(feedback.type === 'error' || feedback.type === 'info') && ( {feedback.message} {feedback.type === 'error' && feedback.retryAfter && ( Retry in {feedback.retryAfter}s )} )}
{feedback.type === 'loading' ? ( Joining... ) : ( 'Count me in' )}
I'll send updates as I build
You can tell me what matters most
Zero spam, zero BS
)}
); }