Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions app/actions/leads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,23 @@ export async function findLeadsAction(input: {
}

let added = 0;
let scanning = 0;
const scanning = 0;
for (const candidate of found.prospects.slice(0, limit)) {
const res = await researchProspect({
userId: auth.userId,
projectId: input.projectId,
url: candidate.url,
discoveredVia: candidate.via,
discoveryLabel: candidate.label,
// Finding leads for a project does not scan them. The scan exists to
// supply findings for the CrawlProof audit pitch, and firing one at
// every discovered business spends worker time on evidence nobody is
// going to cite — and points a scanner at people who only turned up in
// a search. Campaigns that do pitch an audit turn scanning back on
// explicitly.
skipScan: true,
});
if (res.status === "scanning") {
scanning += 1;
added += 1;
} else if (res.status === "researched") {
if (res.status === "researched") {
added += 1;
}
}
Expand All @@ -101,9 +105,7 @@ export async function findLeadsAction(input: {
ok: true,
added,
scanning,
note: scanning
? `${added} leads added — ${scanning} are scanning now. Refresh in a minute to see scores.`
: `${added} leads added.`,
note: `${added} leads added.`,
};
}

Expand Down
6 changes: 5 additions & 1 deletion components/leads/campaign-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ export function CampaignPanel({
const [pitchIntro, setPitchIntro] = useState("");
const [pitchAsk, setPitchAsk] = useState("");
const [pitchFacts, setPitchFacts] = useState("");
const [scanProspects, setScanProspects] = useState(true);
// Scanning is off by default now: a project-level campaign is pitching the
// project's own offer, not a CrawlProof audit, so there is nothing for a
// scan of the prospect to contribute. Selecting the audit pitch turns it
// back on, because that pitch is built from findings.
const [scanProspects, setScanProspects] = useState(false);
const [senderName, setSenderName] = useState("");
const [replyTo, setReplyTo] = useState("");

Expand Down
13 changes: 9 additions & 4 deletions components/leads/lead-finder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import { findLeadsAction } from "@/app/actions/leads";

/**
* The "add leads" box. A search query finds businesses; a directory URL mines
* one page's outbound links. Both queue a free scan per domain, because a
* lead with no report is not a lead — every claim in the eventual email has
* to trace back to one.
* one page's outbound links.
*
* Neither scans the businesses it finds. Scanning exists to supply findings
* for the CrawlProof audit pitch, and a project pitching its own offer has no
* use for them — so firing a scan at every discovered domain would spend
* worker time on evidence nobody cites, and point a scanner at people whose
* only involvement was appearing in a search. A campaign that does pitch an
* audit turns scanning back on explicitly.
*/
export function LeadFinder({ projectId }: { projectId: string }) {
const router = useRouter();
Expand Down Expand Up @@ -88,7 +93,7 @@ export function LeadFinder({ projectId }: { projectId: string }) {
</div>
<p className="mt-2 text-xs text-[var(--color-muted)]">
{mode === "query"
? "Finds businesses, then queues a free scan of each so the pitch can cite real findings."
? "Finds businesses and looks up a contact address for each. It does not scan them."
: "Every outbound link on that page becomes a candidate. Platforms and aggregators are filtered out."}
</p>
{note && <p className="mt-2 text-sm text-[var(--color-fg)]">{note}</p>}
Expand Down
Loading