"use client"; import { useState, useCallback } from "react"; import { StatusBadge } from "./StatusBadge"; import { StatusPopover } from "./StatusPopover"; import { LeadSidePanel } from "./LeadSidePanel"; export interface Lead { id: string; domain: string | null; companyName: string | null; contactName: string | null; contactTitle: string | null; email: string | null; linkedinUrl: string | null; phone: string | null; address: string | null; sourceTab: string; sourceTerm: string | null; sourceJobId: string | null; serpTitle: string | null; serpSnippet: string | null; serpRank: number | null; serpUrl: string | null; status: string; priority: string; notes: string | null; tags: string | null; country: string | null; headcount: string | null; industry: string | null; description: string | null; capturedAt: string; contactedAt: string | null; } interface LeadsTableProps { leads: Lead[]; selected: Set; onToggleSelect: (id: string) => void; onToggleAll: () => void; allSelected: boolean; onLeadUpdate: (id: string, updates: Partial) => void; } function relativeDate(iso: string): string { const now = new Date(); const d = new Date(iso); const diffMs = now.getTime() - d.getTime(); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); if (diffDays === 0) return "heute"; if (diffDays === 1) return "gestern"; if (diffDays < 7) return `vor ${diffDays} Tagen`; const diffWeeks = Math.floor(diffDays / 7); if (diffWeeks < 5) return `vor ${diffWeeks} Woche${diffWeeks > 1 ? "n" : ""}`; const diffMonths = Math.floor(diffDays / 30); return `vor ${diffMonths} Monat${diffMonths > 1 ? "en" : ""}`; } function fullDate(iso: string): string { return new Date(iso).toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric", }); } interface StatusCellProps { lead: Lead; onUpdate: (id: string, updates: Partial) => void; } function StatusCell({ lead, onUpdate }: StatusCellProps) { const [open, setOpen] = useState(false); async function handleSelect(newStatus: string) { // Optimistic update onUpdate(lead.id, { status: newStatus }); try { await fetch(`/api/leads/${lead.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status: newStatus }), }); } catch { // revert on error onUpdate(lead.id, { status: lead.status }); } } return (
setOpen((v) => !v)} /> {open && ( setOpen(false)} /> )}
); } export function LeadsTable({ leads, selected, onToggleSelect, onToggleAll, allSelected, onLeadUpdate, }: LeadsTableProps) { const [sidePanelLead, setSidePanelLead] = useState(null); const [copiedId, setCopiedId] = useState(null); const handleCopyEmail = useCallback(async (lead: Lead, e: React.MouseEvent) => { e.stopPropagation(); if (!lead.email) return; await navigator.clipboard.writeText(lead.email); setCopiedId(lead.id); setTimeout(() => setCopiedId(null), 1500); }, []); async function handleMarkContacted(lead: Lead) { onLeadUpdate(lead.id, { status: "contacted" }); try { await fetch(`/api/leads/${lead.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status: "contacted" }), }); } catch { onLeadUpdate(lead.id, { status: lead.status }); } } const thStyle: React.CSSProperties = { padding: "10px 16px", fontSize: 11, fontWeight: 500, color: "#6b7280", textTransform: "uppercase", letterSpacing: "0.05em", textAlign: "left", whiteSpace: "nowrap", background: "#0d0d18", borderBottom: "1px solid #1e1e2e", }; return ( <>
{leads.map((lead) => { const isSelected = selected.has(lead.id); const isNew = lead.status === "new"; return ( { if (!isSelected) (e.currentTarget as HTMLTableRowElement).style.background = "#0d0d18"; }} onMouseLeave={(e) => { if (!isSelected) (e.currentTarget as HTMLTableRowElement).style.background = "transparent"; }} > {/* Checkbox */} {/* Unternehmen */} {/* Email */} {/* Branche */} {/* Region */} {/* Status */} {/* Gefunden am */} {/* Aktionen */} ); })}
0} onChange={onToggleAll} style={{ cursor: "pointer", accentColor: "#3b82f6" }} /> Unternehmen E-Mail Branche Region Status Gefunden am Aktionen
onToggleSelect(lead.id)} style={{ cursor: "pointer", accentColor: "#3b82f6" }} />
{lead.companyName || lead.domain || "—"}
{lead.domain && lead.companyName && (
{lead.domain}
)}
{lead.email ? (
{lead.email}
) : ( — nicht gefunden )}
{lead.industry || "—"} {lead.address ?? lead.sourceTerm ?? "—"} {relativeDate(lead.capturedAt)}
{/* Primary action */} {/* Notiz */}
{/* Side panel */} {sidePanelLead && ( setSidePanelLead(null)} onUpdate={(id, updates) => { onLeadUpdate(id, updates); setSidePanelLead((prev) => (prev && prev.id === id ? { ...prev, ...updates } : prev)); }} /> )} ); }