Integrate local business workflow and SaaS redesign

This commit is contained in:
2026-06-12 21:08:35 +02:00
parent f00c5a3193
commit 21c7e4c9a4
88 changed files with 2683 additions and 849 deletions

View File

@@ -235,6 +235,8 @@ type GooglePlaceContactEmailSource = {
isBusinessContactAddress?: boolean;
};
export type LeadDiscoverySourceProvider = "google_places" | "local_business_data";
type GooglePlaceApiPlace = {
id?: string;
displayName?: GooglePlaceDisplayName;
@@ -256,6 +258,7 @@ export type GooglePlacesApiResponse = {
export type GooglePlaceCandidate = {
placeId: string;
sourceBusinessId?: string | null;
businessName: string;
address: string;
websiteUrl: string | null;
@@ -272,7 +275,7 @@ export type GooglePlaceCandidate = {
googleTypes: string[];
googlePrimaryType: string | null;
googleMapsUrl: string | null;
sourceProvider: "google_places";
sourceProvider: LeadDiscoverySourceProvider;
sourceFetchedAt: number;
};
@@ -501,6 +504,7 @@ export function normalizePlacesResponse(
export type ExistingLeadLike = {
googlePlaceId?: string | null;
sourceBusinessId?: string | null;
websiteDomain?: string | null;
email?: string | null;
companyName?: string | null;
@@ -509,13 +513,13 @@ export type ExistingLeadLike = {
};
export type BlacklistRow = {
type: "domain" | "email" | "phone" | "company" | "google_place_id";
type: "domain" | "email" | "phone" | "company" | "google_place_id" | "source_business_id";
value: string;
normalizedValue: string;
};
export type BlacklistLookupValue = {
type: "domain" | "email" | "phone" | "company" | "google_place_id";
type: "domain" | "email" | "phone" | "company" | "google_place_id" | "source_business_id";
normalizedValue: string;
};
@@ -560,6 +564,10 @@ export function getBlacklistLookupValues(
type: "google_place_id",
normalizedValue: normalizeDomain(candidate.placeId),
},
{
type: "source_business_id",
normalizedValue: normalizeDomain(candidate.sourceBusinessId ?? candidate.placeId),
},
{
type: "domain",
normalizedValue: normalizeDomain(candidate.websiteDomain),
@@ -588,16 +596,22 @@ export function isDuplicateCandidate(
existing: ExistingLeadLike[],
): boolean {
const candidatePlaceId = normalizeDomain(candidate.placeId);
const candidateSourceBusinessId = normalizeDomain(
candidate.sourceBusinessId ?? candidate.placeId,
);
const candidateDomain = normalizeDomain(candidate.websiteDomain);
const candidateEmails = getCandidateEmailValues(candidate);
return existing.some((entry) => {
const entryPlaceId = normalizeDomain(entry.googlePlaceId);
const entrySourceBusinessId = normalizeDomain(entry.sourceBusinessId);
const entryDomain = normalizeDomain(entry.websiteDomain);
const entryEmail = normalizeEmailAddress(entry.email);
return (
(candidatePlaceId && entryPlaceId === candidatePlaceId) ||
(candidateSourceBusinessId &&
entrySourceBusinessId === candidateSourceBusinessId) ||
(candidateDomain && entryDomain === candidateDomain) ||
candidateEmails.some(
(candidateEmail) => candidateEmail && entryEmail === candidateEmail,
@@ -638,6 +652,9 @@ export function getBlacklistMatches(
blacklistRows: BlacklistRow[],
) {
const candidatePlaceId = normalizeDomain(candidate.placeId);
const candidateSourceBusinessId = normalizeDomain(
candidate.sourceBusinessId ?? candidate.placeId,
);
const candidateDomain = normalizeDomain(candidate.websiteDomain);
const candidateCompany = normalizeText(candidate.businessName);
const candidatePhone = normalizePhone(candidate.phone);
@@ -650,6 +667,11 @@ export function getBlacklistMatches(
switch (row.type) {
case "google_place_id":
return candidatePlaceId !== "" && row.normalizedValue === candidatePlaceId;
case "source_business_id":
return (
candidateSourceBusinessId !== "" &&
row.normalizedValue === candidateSourceBusinessId
);
case "domain":
return candidateDomain !== "" && row.normalizedValue === candidateDomain;
case "company":