feat: Adresse & Telefon in LeadVault Tabelle + Side Panel

- Lead-Modell: address Feld hinzugefügt (Migration)
- Maps-Sync: address + phone aus source JSON extrahiert
- LeadVault Tabelle: Telefon/Adresse als kombinierte Spalte
- LeadVault Side Panel: Adresse mit Pin-Icon
- Telefonnummer ist klickbar (tel: Link)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Timo Uttenweiler
2026-03-20 17:49:29 +01:00
parent 8dc135a8f7
commit fa177a982f
5 changed files with 45 additions and 11 deletions

View File

@@ -152,16 +152,21 @@ async function runMapsEnrich(
// Sync to LeadVault
const finalResults = await prisma.leadResult.findMany({ where: { jobId } });
await sinkLeadsToVault(
finalResults.map(r => ({
domain: r.domain,
companyName: r.companyName,
contactName: r.contactName,
contactTitle: r.contactTitle,
email: r.email,
linkedinUrl: r.linkedinUrl,
emailConfidence: r.confidence,
phone: (() => { try { return JSON.parse(r.source || "{}").phone ?? null; } catch { return null; } })(),
})),
finalResults.map(r => {
let src: { phone?: string; address?: string } = {};
try { src = JSON.parse(r.source || "{}"); } catch { /* ignore */ }
return {
domain: r.domain,
companyName: r.companyName,
contactName: r.contactName,
contactTitle: r.contactTitle,
email: r.email,
linkedinUrl: r.linkedinUrl,
emailConfidence: r.confidence,
phone: src.phone || null,
address: src.address || null,
};
}),
"maps",
params.queries.join(", "),
jobId,

View File

@@ -23,6 +23,7 @@ interface Lead {
email: string | null;
linkedinUrl: string | null;
phone: string | null;
address: string | null;
sourceTab: string;
sourceTerm: string | null;
sourceJobId: string | null;
@@ -264,7 +265,13 @@ function SidePanel({ lead, onClose, onUpdate }: {
{fullLead.phone && (
<div className="flex items-center gap-2">
<Phone className="w-3.5 h-3.5 text-gray-500" />
<span className="text-sm text-gray-300">{fullLead.phone}</span>
<a href={`tel:${fullLead.phone}`} className="text-sm text-gray-300 hover:text-white">{fullLead.phone}</a>
</div>
)}
{fullLead.address && (
<div className="flex items-start gap-2">
<span className="text-gray-500 mt-0.5">📍</span>
<span className="text-sm text-gray-300">{fullLead.address}</span>
</div>
)}
{fullLead.linkedinUrl && (
@@ -794,6 +801,7 @@ export default function LeadVaultPage() {
["priority", "Priorität"],
["companyName", "Unternehmen"],
["contactName", "Kontakt"],
["phone", "Telefon"],
["email", "E-Mail"],
["sourceTab", "Quelle"],
["capturedAt", "Erfasst"],
@@ -868,6 +876,21 @@ export default function LeadVaultPage() {
<p className="text-sm text-gray-300 truncate">{lead.contactName || ""}</p>
{lead.contactTitle && <p className="text-xs text-gray-600 truncate">{lead.contactTitle}</p>}
</td>
<td className="px-3 py-2.5 max-w-[160px]">
{lead.phone ? (
<a href={`tel:${lead.phone}`} onClick={e => e.stopPropagation()}
className="text-xs text-gray-300 hover:text-white whitespace-nowrap">
{lead.phone}
</a>
) : lead.address ? (
<p className="text-xs text-gray-500 truncate" title={lead.address}>{lead.address}</p>
) : (
<span className="text-xs text-gray-600"></span>
)}
{lead.phone && lead.address && (
<p className="text-[11px] text-gray-600 truncate" title={lead.address}>{lead.address}</p>
)}
</td>
<td className="px-3 py-2.5 max-w-[200px]" onClick={e => e.stopPropagation()}>
{lead.email ? (
<button

View File

@@ -8,6 +8,7 @@ export interface VaultLead {
email?: string | null;
linkedinUrl?: string | null;
phone?: string | null;
address?: string | null;
emailConfidence?: number | null;
serpTitle?: string | null;
serpSnippet?: string | null;
@@ -74,6 +75,7 @@ export async function sinkLeadToVault(
email,
linkedinUrl: lead.linkedinUrl || null,
phone: lead.phone || null,
address: lead.address || null,
emailConfidence: lead.emailConfidence ?? null,
serpTitle: lead.serpTitle || null,
serpSnippet: lead.serpSnippet || null,
@@ -139,6 +141,7 @@ export async function sinkLeadsToVault(
email,
linkedinUrl: lead.linkedinUrl || null,
phone: lead.phone || null,
address: lead.address || null,
emailConfidence: lead.emailConfidence ?? null,
serpTitle: lead.serpTitle || null,
serpSnippet: lead.serpSnippet || null,

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Lead" ADD COLUMN "address" TEXT;

View File

@@ -52,6 +52,7 @@ model Lead {
email String?
linkedinUrl String?
phone String?
address String?
sourceTab String
sourceTerm String?