"use client"; import { useState, useCallback } 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 { FileDropZone } from "@/components/shared/FileDropZone"; import { RoleChipsInput } from "@/components/shared/RoleChipsInput"; 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 { parseCSV, detectDomainColumn, type ExportRow } from "@/lib/utils/csv"; import { cleanDomain } from "@/lib/utils/domains"; import { toast } from "sonner"; import { Building2, ChevronRight, AlertCircle } from "lucide-react"; import { useAppStore } from "@/lib/store"; import type { DecisionMakerCategory } from "@/lib/services/anymailfinder"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; const DEFAULT_ROLES: DecisionMakerCategory[] = ["ceo"]; const CATEGORY_OPTIONS: { value: DecisionMakerCategory; label: string }[] = [ { value: "ceo", label: "CEO / Owner / Founder" }, { value: "engineering", label: "Engineering" }, { value: "finance", label: "Finance" }, { value: "hr", label: "HR" }, { value: "it", label: "IT" }, { value: "logistics", label: "Logistics" }, { value: "marketing", label: "Marketing" }, { value: "operations", label: "Operations" }, { value: "buyer", label: "Procurement" }, { value: "sales", label: "Sales" }, ]; interface CompanyRow { name: string; domain: string; } export default function AirScalePage() { const [csvData, setCsvData] = useState[]>([]); const [headers, setHeaders] = useState([]); const [domainCol, setDomainCol] = useState(""); const [nameCol, setNameCol] = useState(""); const [categories, setCategories] = useState(DEFAULT_ROLES); const [jobId, setJobId] = useState(null); const [jobStatus, setJobStatus] = useState("idle"); const [progress, setProgress] = useState({ current: 0, total: 0 }); const [results, setResults] = useState([]); const [running, setRunning] = useState(false); const { addJob, updateJob, removeJob } = useAppStore(); const onFile = useCallback((content: string) => { const { data, headers: h } = parseCSV(content); setCsvData(data); setHeaders(h); const detected = detectDomainColumn(h); if (detected) setDomainCol(detected); const nameGuess = h.find(x => /company|name|firma/i.test(x)); if (nameGuess) setNameCol(nameGuess); }, []); const companies: CompanyRow[] = csvData .map(row => ({ name: nameCol ? (row[nameCol] || "") : "", domain: cleanDomain(row[domainCol] || ""), })) .filter(c => c.domain); const withDomain = companies.length; const withoutDomain = csvData.length - withDomain; const startEnrichment = async () => { if (!companies.length) return toast.error("Keine Unternehmen mit Domains gefunden"); if (!categories.length) return toast.error("Mindestens eine Entscheider-Kategorie auswählen"); setRunning(true); setResults([]); setJobStatus("running"); try { const res = await fetch("/api/jobs/airscale-enrich", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ companies, categories }), }); const data = await res.json() as { jobId?: string; error?: string }; if (!res.ok || !data.jobId) throw new Error(data.error || "Failed to start job"); setJobId(data.jobId); addJob({ id: data.jobId, type: "airscale", status: "running", progress: 0, total: companies.length }); toast.success("Anreicherung gestartet!"); pollJob(data.jobId); } catch (err) { toast.error(err instanceof Error ? err.message : "Failed to start"); setRunning(false); setJobStatus("failed"); } }; const pollJob = (id: string) => { 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[]; error?: string; }; setProgress({ current: data.emailsFound, total: data.totalLeads }); setResults(data.results || []); updateJob(id, { status: data.status, progress: data.emailsFound, total: data.totalLeads }); if (data.status === "complete" || data.status === "failed") { clearInterval(interval); setJobStatus(data.status); setRunning(false); removeJob(id); if (data.status === "complete") { toast.success(`Fertig! ${data.emailsFound} E-Mails aus ${data.totalLeads} Unternehmen gefunden`); } else { toast.error(`Job fehlgeschlagen: ${data.error || "Unbekannter Fehler"}`); } } } catch { clearInterval(interval); setRunning(false); } }, 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, confidence_score: r.confidence !== undefined ? Math.round(r.confidence * 100) : undefined, source_tab: "airscale", job_id: jobId || "", found_at: new Date().toISOString(), })); const hitRate = results.length > 0 ? Math.round((results.filter(r => r.email).length / results.length) * 100) : 0; return (
{/* Header */}
Tab 1 AirScale Companies

AirScale → E-Mail Anreicherung

Lade einen AirScale CSV-Export hoch und finde Entscheider-E-Mails über Anymailfinder.

{/* Step 1: Upload */}

1 Upload AirScale CSV

{csvData.length > 0 && (
{/* Stats */}
{[ { label: "Zeilen gesamt", value: csvData.length, color: "text-white" }, { label: "Mit Domain", value: withDomain, color: "text-green-400" }, { label: "Ohne Domain", value: withoutDomain, color: "text-yellow-400" }, ].map(stat => (

{stat.value}

{stat.label}

))}
{/* Column mapper */}
{/* Preview */}
Vorschau (erste 5 Zeilen)
{headers.slice(0, 6).map(h => ( ))} {csvData.slice(0, 5).map((row, i) => ( {headers.slice(0, 6).map(h => ( ))} ))}
{h}
{row[h] || "—"}
)}
{/* Step 2: Configure */}

2 Entscheider-Kategorien

{CATEGORY_OPTIONS.map(opt => ( ))}

Kategorien werden in Prioritätsreihenfolge durchsucht. Die erste Kategorie mit einem gültigen Ergebnis gewinnt.

{/* Step 3: Run */}

3 Anreicherung starten

{!running && jobStatus === "idle" && ( csvData.length === 0 ? ( ) : ( ) )} {(running || jobStatus === "running") && ( )} {jobStatus === "complete" && ( )} {jobStatus === "failed" && (
Anreicherung fehlgeschlagen. Bitte API-Key in den Einstellungen prüfen.
)}
{/* Results */} {results.length > 0 && (

4 Ergebnisse

r.email).length} E-Mails gefunden • ${hitRate}% Trefferquote`} />
)}
); }