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:
@@ -152,7 +152,10 @@ async function runMapsEnrich(
|
||||
// Sync to LeadVault
|
||||
const finalResults = await prisma.leadResult.findMany({ where: { jobId } });
|
||||
await sinkLeadsToVault(
|
||||
finalResults.map(r => ({
|
||||
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,
|
||||
@@ -160,8 +163,10 @@ async function runMapsEnrich(
|
||||
email: r.email,
|
||||
linkedinUrl: r.linkedinUrl,
|
||||
emailConfidence: r.confidence,
|
||||
phone: (() => { try { return JSON.parse(r.source || "{}").phone ?? null; } catch { return null; } })(),
|
||||
})),
|
||||
phone: src.phone || null,
|
||||
address: src.address || null,
|
||||
};
|
||||
}),
|
||||
"maps",
|
||||
params.queries.join(", "),
|
||||
jobId,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Lead" ADD COLUMN "address" TEXT;
|
||||
@@ -52,6 +52,7 @@ model Lead {
|
||||
email String?
|
||||
linkedinUrl String?
|
||||
phone String?
|
||||
address String?
|
||||
|
||||
sourceTab String
|
||||
sourceTerm String?
|
||||
|
||||
Reference in New Issue
Block a user