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
|
// Sync to LeadVault
|
||||||
const finalResults = await prisma.leadResult.findMany({ where: { jobId } });
|
const finalResults = await prisma.leadResult.findMany({ where: { jobId } });
|
||||||
await sinkLeadsToVault(
|
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,
|
domain: r.domain,
|
||||||
companyName: r.companyName,
|
companyName: r.companyName,
|
||||||
contactName: r.contactName,
|
contactName: r.contactName,
|
||||||
@@ -160,8 +163,10 @@ async function runMapsEnrich(
|
|||||||
email: r.email,
|
email: r.email,
|
||||||
linkedinUrl: r.linkedinUrl,
|
linkedinUrl: r.linkedinUrl,
|
||||||
emailConfidence: r.confidence,
|
emailConfidence: r.confidence,
|
||||||
phone: (() => { try { return JSON.parse(r.source || "{}").phone ?? null; } catch { return null; } })(),
|
phone: src.phone || null,
|
||||||
})),
|
address: src.address || null,
|
||||||
|
};
|
||||||
|
}),
|
||||||
"maps",
|
"maps",
|
||||||
params.queries.join(", "),
|
params.queries.join(", "),
|
||||||
jobId,
|
jobId,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ interface Lead {
|
|||||||
email: string | null;
|
email: string | null;
|
||||||
linkedinUrl: string | null;
|
linkedinUrl: string | null;
|
||||||
phone: string | null;
|
phone: string | null;
|
||||||
|
address: string | null;
|
||||||
sourceTab: string;
|
sourceTab: string;
|
||||||
sourceTerm: string | null;
|
sourceTerm: string | null;
|
||||||
sourceJobId: string | null;
|
sourceJobId: string | null;
|
||||||
@@ -264,7 +265,13 @@ function SidePanel({ lead, onClose, onUpdate }: {
|
|||||||
{fullLead.phone && (
|
{fullLead.phone && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Phone className="w-3.5 h-3.5 text-gray-500" />
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
{fullLead.linkedinUrl && (
|
{fullLead.linkedinUrl && (
|
||||||
@@ -794,6 +801,7 @@ export default function LeadVaultPage() {
|
|||||||
["priority", "Priorität"],
|
["priority", "Priorität"],
|
||||||
["companyName", "Unternehmen"],
|
["companyName", "Unternehmen"],
|
||||||
["contactName", "Kontakt"],
|
["contactName", "Kontakt"],
|
||||||
|
["phone", "Telefon"],
|
||||||
["email", "E-Mail"],
|
["email", "E-Mail"],
|
||||||
["sourceTab", "Quelle"],
|
["sourceTab", "Quelle"],
|
||||||
["capturedAt", "Erfasst"],
|
["capturedAt", "Erfasst"],
|
||||||
@@ -868,6 +876,21 @@ export default function LeadVaultPage() {
|
|||||||
<p className="text-sm text-gray-300 truncate">{lead.contactName || "–"}</p>
|
<p className="text-sm text-gray-300 truncate">{lead.contactName || "–"}</p>
|
||||||
{lead.contactTitle && <p className="text-xs text-gray-600 truncate">{lead.contactTitle}</p>}
|
{lead.contactTitle && <p className="text-xs text-gray-600 truncate">{lead.contactTitle}</p>}
|
||||||
</td>
|
</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()}>
|
<td className="px-3 py-2.5 max-w-[200px]" onClick={e => e.stopPropagation()}>
|
||||||
{lead.email ? (
|
{lead.email ? (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface VaultLead {
|
|||||||
email?: string | null;
|
email?: string | null;
|
||||||
linkedinUrl?: string | null;
|
linkedinUrl?: string | null;
|
||||||
phone?: string | null;
|
phone?: string | null;
|
||||||
|
address?: string | null;
|
||||||
emailConfidence?: number | null;
|
emailConfidence?: number | null;
|
||||||
serpTitle?: string | null;
|
serpTitle?: string | null;
|
||||||
serpSnippet?: string | null;
|
serpSnippet?: string | null;
|
||||||
@@ -74,6 +75,7 @@ export async function sinkLeadToVault(
|
|||||||
email,
|
email,
|
||||||
linkedinUrl: lead.linkedinUrl || null,
|
linkedinUrl: lead.linkedinUrl || null,
|
||||||
phone: lead.phone || null,
|
phone: lead.phone || null,
|
||||||
|
address: lead.address || null,
|
||||||
emailConfidence: lead.emailConfidence ?? null,
|
emailConfidence: lead.emailConfidence ?? null,
|
||||||
serpTitle: lead.serpTitle || null,
|
serpTitle: lead.serpTitle || null,
|
||||||
serpSnippet: lead.serpSnippet || null,
|
serpSnippet: lead.serpSnippet || null,
|
||||||
@@ -139,6 +141,7 @@ export async function sinkLeadsToVault(
|
|||||||
email,
|
email,
|
||||||
linkedinUrl: lead.linkedinUrl || null,
|
linkedinUrl: lead.linkedinUrl || null,
|
||||||
phone: lead.phone || null,
|
phone: lead.phone || null,
|
||||||
|
address: lead.address || null,
|
||||||
emailConfidence: lead.emailConfidence ?? null,
|
emailConfidence: lead.emailConfidence ?? null,
|
||||||
serpTitle: lead.serpTitle || null,
|
serpTitle: lead.serpTitle || null,
|
||||||
serpSnippet: lead.serpSnippet || 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?
|
email String?
|
||||||
linkedinUrl String?
|
linkedinUrl String?
|
||||||
phone String?
|
phone String?
|
||||||
|
address String?
|
||||||
|
|
||||||
sourceTab String
|
sourceTab String
|
||||||
sourceTerm String?
|
sourceTerm String?
|
||||||
|
|||||||
Reference in New Issue
Block a user