import { useState, useEffect, useRef } from 'react'; import gsap from 'gsap'; const API_BASE = '/api'; export default function App() { const [niche, setNiche] = useState(''); const [location, setLocation] = useState(''); const [limit, setLimit] = useState(10); const [jobStatus, setJobStatus] = useState({ status: 'idle', message: 'Awaiting transmission...', progress: 0 }); const [results, setResults] = useState([]); const [activeTab, setActiveTab] = useState('search'); const heroRef = useRef(null); const panelRef = useRef(null); const gridRef = useRef(null); const particlesRef = useRef([]); const isRunning = ['scraping','enriching','saving'].includes(jobStatus.status); /* ── Entrance GSAP animation ── */ useEffect(() => { const ctx = gsap.context(() => { gsap.fromTo(heroRef.current, { opacity: 0, y: -40 }, { opacity: 1, y: 0, duration: 1.1, ease: 'power4.out' } ); gsap.fromTo(panelRef.current, { opacity: 0, y: 30, scale: 0.97 }, { opacity: 1, y: 0, scale: 1, duration: 0.9, delay: 0.3, ease: 'back.out(1.4)' } ); }); return () => ctx.revert(); }, []); /* ── Animate particles ── */ useEffect(() => { particlesRef.current.forEach((el, i) => { if (!el) return; gsap.to(el, { y: `random(-30, 30)`, x: `random(-20, 20)`, opacity: `random(0.2, 0.8)`, duration: `random(3, 6)`, repeat: -1, yoyo: true, ease: 'sine.inOut', delay: i * 0.3, }); }); }, []); /* ── Poll status while running ── */ useEffect(() => { if (!isRunning) return; const id = setInterval(async () => { try { const res = await fetch(`${API_BASE}/status`); const data = await res.json(); setJobStatus(data); if (data.status === 'complete') { clearInterval(id); loadResults(); } else if (data.status === 'error') { clearInterval(id); } } catch (_) {} }, 1000); return () => clearInterval(id); }, [isRunning]); /* ── Animate result rows in ── */ useEffect(() => { if (results.length > 0 && gridRef.current) { const rows = gridRef.current.querySelectorAll('tbody tr'); gsap.fromTo(rows, { opacity: 0, x: -15 }, { opacity: 1, x: 0, stagger: 0.06, duration: 0.4, ease: 'power2.out' }); } }, [results]); const loadResults = async () => { try { const r = await fetch(`${API_BASE}/results`); setResults(await r.json()); setActiveTab('results'); } catch (_) {} }; const handleSubmit = async (e) => { e.preventDefault(); if (!niche.trim() || !location.trim()) return; setResults([]); setJobStatus({ status: 'scraping', message: 'Initiating agent...', progress: 3 }); try { await fetch(`${API_BASE}/scrape`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ niche, location, limit }), }); } catch (_) { setJobStatus({ status: 'error', message: 'Cannot reach backend on :8000', progress: 0 }); } }; const exportCSV = () => { const cols = ['name','website','phone','email','rating','facebook','instagram','linkedin','status']; const lines = [cols.join(','), ...results.map(r => cols.map(k => `"${(r[k]||'').replace(/"/g,'""')}"`).join(','))]; const a = document.createElement('a'); a.href = URL.createObjectURL(new Blob([lines.join('\n')], { type: 'text/csv' })); a.download = `leads_${niche}_${location}.csv`; a.click(); }; const safeHost = url => { try { return new URL(url).hostname; } catch { return url; } }; const statusColor = { idle:'#4a5568', scraping:'#00f2ff', enriching:'#8b5cf6', saving:'#10b981', complete:'#22c55e', error:'#ef4444' }; /* ── Grid layout ── */ return (
{/* Particles */}
{[...Array(18)].map((_, i) => (
particlesRef.current[i] = el} style={{ left: `${Math.random()*100}%`, top: `${Math.random()*100}%`, width: `${2+Math.random()*3}px`, height: `${2+Math.random()*3}px`, opacity: 0.3 + Math.random() * 0.5 }} /> ))}
{/* ── HERO ── */}

LEAD HUNTER AI

AGENTIC BUSINESS INTELLIGENCE ENGINE v2.0

{/* ── STATUS BAR ── */} {jobStatus.status !== 'idle' && (
{jobStatus.message} {jobStatus.progress}%
)} {jobStatus.status !== 'idle' && (
)} {/* ── TABS ── */}
{/* ── SEARCH PANEL ── */} {activeTab === 'search' && (
🔍 setNiche(e.target.value)} disabled={isRunning} required />
📍 setLocation(e.target.value)} disabled={isRunning} required />
# setLimit(Number(e.target.value))} disabled={isRunning} />
3
AI Agents
Niches
Free
Cost
)} {/* ── RESULTS PANEL ── */} {activeTab === 'results' && (
{results.length === 0 ? (

📡 No data yet. Run a search first.

) : ( <>
{results.length} leads discovered
{results.map((row, i) => ( ))}
BUSINESS CONTACT RATING CHANNELS STATUS
{row.name}
{row.website && {safeHost(row.website)}} {row.phone &&
{row.phone}
}
{row.email || }
★ {row.rating || '—'}
{row.facebook && fb} {row.instagram && ig} {row.linkedin && in}
{row.status || '—'}
)}
)}
); }