"use client"; import { useState, useCallback } from "react"; import Link from "next/link"; import { toast } from "sonner"; import { SearchCard } from "@/components/search/SearchCard"; import { LoadingCard, type LeadResult } from "@/components/search/LoadingCard"; export default function SuchePage() { const [query, setQuery] = useState(""); const [region, setRegion] = useState(""); const [count, setCount] = useState(50); const [loading, setLoading] = useState(false); const [jobId, setJobId] = useState(null); const [leads, setLeads] = useState([]); const [searchDone, setSearchDone] = useState(false); const [selected, setSelected] = useState>(new Set()); const [deleting, setDeleting] = useState(false); const [onlyNew, setOnlyNew] = useState(false); const [saveOnlyNew, setSaveOnlyNew] = useState(false); function handleChange(field: "query" | "region" | "count", value: string | number) { if (field === "query") setQuery(value as string); if (field === "region") setRegion(value as string); if (field === "count") setCount(value as number); } async function handleSubmit() { if (!query.trim() || loading) return; setLoading(true); setJobId(null); setLeads([]); setSearchDone(false); setSelected(new Set()); setOnlyNew(false); // saveOnlyNew intentionally kept — user setting persists across searches try { const res = await fetch("/api/search", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: query.trim(), region: region.trim(), count }), }); if (!res.ok) { const err = await res.json() as { error?: string }; throw new Error(err.error || "Fehler beim Starten der Suche"); } const data = await res.json() as { jobId: string }; setJobId(data.jobId); } catch (err) { const msg = err instanceof Error ? err.message : "Unbekannter Fehler"; toast.error(msg); setLoading(false); } } const handleDone = useCallback(async (result: LeadResult[], warning?: string) => { setLoading(false); setLeads(result); setSearchDone(true); // Auto-delete existing (non-new) leads from vault if "Nur neue speichern" is active const existingIds = result.filter(l => !l.isNew).map(l => l.id); if (saveOnlyNew && existingIds.length > 0) { try { await fetch("/api/leads/delete-from-results", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ resultIds: existingIds }), }); // Mark them as deleted in local state (isNew stays false, they remain visible but are gone from vault) } catch { /* silent — vault cleanup best-effort */ } } const newCount = result.filter(l => l.isNew).length; if (warning) { toast.warning(`${result.length} Unternehmen gefunden — E-Mail-Anreicherung fehlgeschlagen: ${warning}`, { duration: 6000 }); } else if (saveOnlyNew && existingIds.length > 0) { toast.success(`✓ ${newCount} neue Leads gespeichert, ${existingIds.length} bereits vorhandene verworfen`, { duration: 5000 }); } else { toast.success(`✓ ${result.length} Leads gefunden und im Leadspeicher gespeichert`, { duration: 4000 }); } }, [saveOnlyNew]); async function handleDelete(ids: string[]) { if (!ids.length || deleting) return; setDeleting(true); try { await fetch("/api/leads/delete-from-results", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ resultIds: ids }), }); setLeads(prev => prev.filter(l => !ids.includes(l.id))); setSelected(prev => { const next = new Set(prev); ids.forEach(id => next.delete(id)); return next; }); toast.success(`${ids.length} Lead${ids.length > 1 ? "s" : ""} gelöscht`); } catch { toast.error("Löschen fehlgeschlagen"); } finally { setDeleting(false); } } const handleError = useCallback((message: string) => { setLoading(false); setJobId(null); toast.error(`Suche fehlgeschlagen: ${message}`); }, []); return (
{/* Hero */}
Lead-Suche

Leads finden

Suchbegriff eingeben — wir finden passende Unternehmen mit Kontaktdaten.

{/* Search Card */} {/* Save option */} {!loading && !searchDone && ( )} {/* Loading Card */} {loading && jobId && ( )} {/* Results */} {searchDone && leads.length > 0 && (() => { const newCount = leads.filter(l => l.isNew).length; const visibleLeads = onlyNew ? leads.filter(l => l.isNew) : leads; const allVisibleSelected = visibleLeads.length > 0 && visibleLeads.every(l => selected.has(l.id)); const someVisibleSelected = visibleLeads.some(l => selected.has(l.id)); const selectedVisible = visibleLeads.filter(l => selected.has(l.id)); return (
{/* Header */}
{/* Select-all checkbox */} { if (el) el.indeterminate = someVisibleSelected && !allVisibleSelected; }} onChange={e => { if (e.target.checked) { setSelected(prev => { const next = new Set(prev); visibleLeads.forEach(l => next.add(l.id)); return next; }); } else { setSelected(prev => { const next = new Set(prev); visibleLeads.forEach(l => next.delete(l.id)); return next; }); } }} style={{ accentColor: "#3b82f6", cursor: "pointer", width: 14, height: 14 }} /> {selectedVisible.length > 0 ? `${selectedVisible.length} ausgewählt` : `${visibleLeads.length} Leads`} {/* Filter tabs */}
{[ { label: `Alle (${leads.length})`, value: false }, { label: `Nur neue (${newCount})`, value: true }, ].map(tab => ( ))}
{selectedVisible.length > 0 && ( )} Im Leadspeicher →
{/* Table */}
))} {visibleLeads.map((lead, i) => { const isSelected = selected.has(lead.id); return ( ); })}
{["Unternehmen", "Domain", "Kontakt", "E-Mail"].map(h => ( {h}
{ setSelected(prev => { const next = new Set(prev); e.target.checked ? next.add(lead.id) : next.delete(lead.id); return next; }); }} style={{ accentColor: "#3b82f6", cursor: "pointer", width: 13, height: 13 }} />
{lead.companyName || "—"} {!lead.isNew && ( vorhanden )}
{lead.domain || "—"} {lead.contactName ? `${lead.contactName}${lead.contactTitle ? ` · ${lead.contactTitle}` : ""}` : "—"} {lead.email ? ( {lead.email} ) : ( )}
); })()}
); }