"use client"; import { useState } from "react"; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { ProgressCard } from "@/components/shared/ProgressCard"; import { ResultsTable, type ResultRow } from "@/components/shared/ResultsTable"; import { ExportButtons } from "@/components/shared/ExportButtons"; import { EmptyState } from "@/components/shared/EmptyState"; import { toast } from "sonner"; import { Search, ChevronRight } from "lucide-react"; import { useAppStore } from "@/lib/store"; import type { DecisionMakerCategory } from "@/lib/services/anymailfinder"; import type { ExportRow } from "@/lib/utils/csv"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Checkbox } from "@/components/ui/checkbox"; const RESULT_OPTIONS = [10, 25, 50, 100, 200]; const CATEGORY_OPTIONS: { value: DecisionMakerCategory; label: string; recommended?: boolean }[] = [ { value: "ceo", label: "CEO / Owner / President / Founder", recommended: true }, { value: "operations", label: "COO" }, { value: "engineering", label: "CTO" }, { value: "marketing", label: "CMO" }, { value: "finance", label: "CFO" }, { value: "sales", label: "Vertriebsleiter" }, ]; type Stage = "idle" | "running" | "done" | "failed"; export default function SerpPage() { const [query, setQuery] = useState(""); const [numResults, setNumResults] = useState(50); const [country, setCountry] = useState("de"); const [language, setLanguage] = useState("de"); const [filterSocial, setFilterSocial] = useState(true); const [category, setCategory] = useState("ceo"); const [stage, setStage] = useState("idle"); const [jobId, setJobId] = useState(null); const [progress, setProgress] = useState({ current: 0, total: 0, phase: "" }); const [results, setResults] = useState([]); const { addJob, updateJob, removeJob } = useAppStore(); // maxPages: since Google limits to 10 results/page, divide by 10 const maxPages = Math.max(1, Math.ceil(numResults / 10)); const startJob = async () => { if (!query.trim()) return toast.error("Suchbegriff eingeben"); setStage("running"); setResults([]); setProgress({ current: 0, total: numResults, phase: "Google wird durchsucht..." }); try { const res = await fetch("/api/jobs/serp-enrich", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, maxPages, countryCode: country, languageCode: language, filterSocial, categories: [category], }), }); const data = await res.json() as { jobId?: string; error?: string }; if (!res.ok || !data.jobId) throw new Error(data.error || "Failed"); setJobId(data.jobId); addJob({ id: data.jobId, type: "serp", status: "running", progress: 0, total: numResults }); pollJob(data.jobId); } catch (err) { toast.error(err instanceof Error ? err.message : "Failed to start"); setStage("failed"); } }; const pollJob = (id: string) => { let phase = "Google wird durchsucht..."; const interval = setInterval(async () => { try { const res = await fetch(`/api/jobs/${id}/status`); const data = await res.json() as { status: string; totalLeads: number; emailsFound: number; results: ResultRow[]; }; // Infer phase from progress if (data.totalLeads > 0 && data.emailsFound === 0) phase = "Domains werden mit Anymailfinder angereichert..."; if (data.emailsFound > 0) phase = `Bisher ${data.emailsFound} E-Mails gefunden...`; setProgress({ current: data.emailsFound, total: data.totalLeads || numResults, phase }); if (data.results?.length) setResults(data.results); updateJob(id, { status: data.status, progress: data.emailsFound, total: data.totalLeads }); if (data.status === "complete" || data.status === "failed") { clearInterval(interval); removeJob(id); setResults(data.results || []); if (data.status === "complete") { setStage("done"); toast.success(`Fertig! ${data.emailsFound} E-Mails gefunden`); } else { setStage("failed"); toast.error("Job fehlgeschlagen. Bitte API-Keys in den Einstellungen prüfen."); } } } catch { clearInterval(interval); setStage("failed"); } }, 2000); }; const exportRows: ExportRow[] = results.map(r => ({ company_name: r.companyName, domain: r.domain, contact_name: r.contactName, contact_title: r.contactTitle, email: r.email, })); const emailsFound = results.filter(r => r.email).length; return (
{/* Header */}
Tab 3 Google SERP

SERP → E-Mail Anreicherung

Scrape Google-Suchergebnisse über Apify, extrahiere Domains und finde Entscheider-E-Mails.

{/* Step 1: Configure */}

1 Suchkonfiguration

setQuery(e.target.value)} className="bg-[#0d0d18] border-[#2e2e3e] text-white placeholder:text-gray-600 focus:border-blue-500" />

~{maxPages} Google-Seite{maxPages > 1 ? "n" : ""}

setFilterSocial(!!v)} className="border-[#2e2e3e] mt-0.5" />

Filtert: LinkedIn, Facebook, Instagram, Yelp, Wikipedia, Xing, Twitter/X, YouTube

{/* Step 2: Categories */}

2 Entscheider-Kategorien

{CATEGORY_OPTIONS.map(opt => ( ))}
{/* Run */}

3 Pipeline starten

{stage === "idle" || stage === "failed" ? ( ) : stage === "running" ? ( ) : ( )}
{/* Results */} {results.length > 0 && (

Ergebnisse

)} {stage === "idle" && ( )}
); }