Skip to content

Commit 6b02568

Browse files
author
PrintableTools Lab Bot
committed
Reduce sponsor lead form friction
1 parent 1ad17a7 commit 6b02568

5 files changed

Lines changed: 73 additions & 36 deletions

File tree

app.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8920,7 +8920,7 @@ ${paragraphs.join("\n")}
89208920
<p>Share only business-safe details. The inquiry is stored for follow-up review, while public dashboards show only aggregate lead counts.</p>
89218921
<div class="notice sponsor-close-path">
89228922
<strong>Fastest paid pilot path</strong>
8923-
<p>Start with the USD 49 starter fit review. The lowest-friction path is the public invoice request; the private fast form needs only business email and website.</p>
8923+
<p>Start with the USD 49 starter fit review. The lowest-friction private form needs only a business email; website is optional and can be inferred from the email domain.</p>
89248924
<p><a class="button" data-sponsor-public-invoice-request data-track-event="sponsor_request_intent" data-track-tool="sponsor" href="${escapeHtml(publicReplyUrl)}" target="_blank" rel="noreferrer">Open USD 49 invoice request</a> <a class="button secondary" data-sponsor-deal-select ${sponsorDealPrefillAttrs(defaultDeal)} data-track-event="sponsor_request_intent" data-track-tool="sponsor" href="#sponsor-quick-form">Use USD 49 starter review</a></p>
89258925
</div>
89268926
<ul>
@@ -8934,28 +8934,25 @@ ${paragraphs.join("\n")}
89348934
<p><a class="button" data-sponsor-public-invoice-request data-track-event="sponsor_request_intent" data-track-tool="sponsor" href="${escapeHtml(publicReplyUrl)}" target="_blank" rel="noreferrer">Open USD 49 invoice request</a></p>
89358935
</div>
89368936
</div>
8937-
<form class="panel form-grid sponsor-quick-form" id="sponsor-quick-form" data-sponsor-quick-form>
8937+
<form class="panel form-grid sponsor-quick-form sponsor-micro-lead-form" id="sponsor-quick-form" data-sponsor-quick-form>
89388938
<input class="sr-only" type="text" name="websiteTrap" tabindex="-1" autocomplete="off" aria-hidden="true">
89398939
<input type="hidden" name="dealId">
8940-
<h3>2-minute pilot invoice review</h3>
8941-
<p class="help">Pick a starter pilot and send two business-safe fields. This requests manual fit review only; any invoice or agreement is sent later through an external provider.</p>
8942-
<label class="field sponsor-deal-picker">
8940+
<h3>One-field USD 49 invoice review</h3>
8941+
<p class="help">Send one business-safe field for manual fit review. Any invoice or agreement is sent later through an external provider.</p>
8942+
<label class="field sponsor-deal-picker sr-only">
89438943
<span>Selected pilot</span>
8944-
<select name="quickDealId" data-sponsor-quick-deal>
8944+
<select name="quickDealId" data-sponsor-quick-deal aria-label="Selected pilot" tabindex="-1">
89458945
${sponsorQuickDealOptions()}
89468946
</select>
89478947
</label>
8948-
<label class="field">
8949-
<span>Company or project (optional)</span>
8950-
<input name="company" maxlength="90" autocomplete="organization" placeholder="Auto-filled from website if blank">
8951-
</label>
8948+
<input type="hidden" name="company" value="">
89528949
<label class="field">
89538950
<span>Business email</span>
89548951
<input name="contactEmail" type="email" maxlength="140" autocomplete="email" required>
89558952
</label>
89568953
<label class="field">
8957-
<span>Website</span>
8958-
<input name="website" type="url" maxlength="220" placeholder="https://example.com" autocomplete="url" required>
8954+
<span>Website (optional)</span>
8955+
<input name="website" type="url" maxlength="220" placeholder="https://example.com or leave blank" autocomplete="url">
89598956
</label>
89608957
<div class="actions">
89618958
<button class="button" type="submit" data-track-event="sponsor_request_intent" data-track-tool="sponsor">Send USD 49 invoice review request</button>

functions/api/sponsor-lead.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export async function onRequestGet({ env }) {
172172
function normalizeLead(body, request) {
173173
const rawCompany = cleanText(body.company, 90);
174174
const contactEmail = cleanEmail(body.contactEmail);
175-
const website = cleanUrl(body.website, 220);
175+
const website = cleanUrl(body.website, 220) || inferWebsiteFromEmail(contactEmail);
176176
const company = rawCompany || inferCompanyFromContact(website, contactEmail);
177177
const placement = cleanChoice(body.placement, PLACEMENTS, "media-kit-review");
178178
const budgetRange = cleanChoice(body.budgetRange, BUDGET_RANGES, "exploratory");
@@ -192,7 +192,6 @@ function normalizeLead(body, request) {
192192

193193
if (company.length < 2) return { error: "Company or project name is required." };
194194
if (!contactEmail) return { error: "A valid business email is required." };
195-
if (!website) return { error: "A valid website URL is required." };
196195
if (audienceFit.length < 12) return { error: "Audience fit needs a short public-safe note." };
197196
if (!consent) return { error: "Confirm that the inquiry avoids private payment, tax, or customer files." };
198197

@@ -393,6 +392,12 @@ function hostFromEmail(value) {
393392
return String(value || "").split("@").pop()?.toLowerCase() || "";
394393
}
395394

395+
function inferWebsiteFromEmail(value) {
396+
const host = hostFromEmail(value);
397+
if (!host || host === "gmail.com" || host === "yahoo.com" || host === "outlook.com" || host === "hotmail.com" || host === "icloud.com" || host === "proton.me" || host === "protonmail.com") return "";
398+
return cleanUrl(`https://${host}`, 220);
399+
}
400+
396401
function cleanChoice(value, allowed, fallback) {
397402
const key = cleanKey(value, 40);
398403
return allowed.has(key) ? key : fallback;

scripts/seo-content.cjs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7875,7 +7875,7 @@ function sponsorLeadFormHtml() {
78757875
<p>Share only business-safe details. The inquiry is stored for follow-up review, while public dashboards show only aggregate lead counts.</p>
78767876
<div class="notice sponsor-close-path">
78777877
<strong>Fastest paid pilot path</strong>
7878-
<p>Start with the USD 49 starter fit review. The lowest-friction path is the public invoice request; the private fast form needs only business email and website.</p>
7878+
<p>Start with the USD 49 starter fit review. The lowest-friction private form needs only a business email; website is optional and can be inferred from the email domain.</p>
78797879
<p><a class="button" data-sponsor-public-invoice-request data-track-event="sponsor_request_intent" data-track-tool="sponsor" href="${escapeHtml(publicReplyUrl)}" target="_blank" rel="noreferrer">Open USD 49 invoice request</a> <a class="button secondary" data-sponsor-deal-select ${sponsorDealPrefillAttrs(defaultDeal)} data-track-event="sponsor_request_intent" data-track-tool="sponsor" href="#sponsor-quick-form">Use USD 49 starter review</a></p>
78807880
</div>
78817881
<ul>
@@ -7889,28 +7889,25 @@ function sponsorLeadFormHtml() {
78897889
<p><a class="button" data-sponsor-public-invoice-request data-track-event="sponsor_request_intent" data-track-tool="sponsor" href="${escapeHtml(publicReplyUrl)}" target="_blank" rel="noreferrer">Open USD 49 invoice request</a></p>
78907890
</div>
78917891
</div>
7892-
<form class="panel form-grid sponsor-quick-form" id="sponsor-quick-form" data-sponsor-quick-form>
7892+
<form class="panel form-grid sponsor-quick-form sponsor-micro-lead-form" id="sponsor-quick-form" data-sponsor-quick-form>
78937893
<input class="sr-only" type="text" name="websiteTrap" tabindex="-1" autocomplete="off" aria-hidden="true">
78947894
<input type="hidden" name="dealId">
7895-
<h3>2-minute pilot invoice review</h3>
7896-
<p class="help">Pick a starter pilot and send two business-safe fields. This requests manual fit review only; any invoice or agreement is sent later through an external provider.</p>
7897-
<label class="field sponsor-deal-picker">
7895+
<h3>One-field USD 49 invoice review</h3>
7896+
<p class="help">Send one business-safe field for manual fit review. Any invoice or agreement is sent later through an external provider.</p>
7897+
<label class="field sponsor-deal-picker sr-only">
78987898
<span>Selected pilot</span>
7899-
<select name="quickDealId" data-sponsor-quick-deal>
7899+
<select name="quickDealId" data-sponsor-quick-deal aria-label="Selected pilot" tabindex="-1">
79007900
${sponsorQuickDealOptions()}
79017901
</select>
79027902
</label>
7903-
<label class="field">
7904-
<span>Company or project (optional)</span>
7905-
<input name="company" maxlength="90" autocomplete="organization" placeholder="Auto-filled from website if blank">
7906-
</label>
7903+
<input type="hidden" name="company" value="">
79077904
<label class="field">
79087905
<span>Business email</span>
79097906
<input name="contactEmail" type="email" maxlength="140" autocomplete="email" required>
79107907
</label>
79117908
<label class="field">
7912-
<span>Website</span>
7913-
<input name="website" type="url" maxlength="220" placeholder="https://example.com" autocomplete="url" required>
7909+
<span>Website (optional)</span>
7910+
<input name="website" type="url" maxlength="220" placeholder="https://example.com or leave blank" autocomplete="url">
79147911
</label>
79157912
<div class="actions">
79167913
<button class="button" type="submit" data-track-event="sponsor_request_intent" data-track-tool="sponsor">Send USD 49 invoice review request</button>

scripts/test-events.cjs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,10 +609,48 @@ async function main() {
609609
const inferredCompanyLead = JSON.parse(store.data.get(`sponsor:lead:${inferredCompanyPayload.id}`));
610610
assert(inferredCompanyLead.company === "Lowfriction", "Sponsor lead endpoint should infer a company label from the website when quick form company is blank");
611611
assert(inferredCompanyLead.commitment === "request-invoice", "Inferred-company quick sponsor lead should keep invoice request commitment");
612+
const emailOnlySponsorResponse = await sponsorLeadSource.onRequestPost({
613+
request: new Request("https://example.test/api/sponsor-lead", {
614+
method: "POST",
615+
headers: { "Content-Type": "application/json", "CF-Connecting-IP": "203.0.113.20" },
616+
body: JSON.stringify(sponsorLeadBody({
617+
company: "",
618+
contactEmail: "buyer@onefield-sponsor.example",
619+
website: "",
620+
dealId: "starter-fit-review",
621+
})),
622+
}),
623+
env,
624+
});
625+
const emailOnlySponsorPayload = await emailOnlySponsorResponse.json();
626+
assert(emailOnlySponsorResponse.status === 200 && emailOnlySponsorPayload.ok, "Sponsor lead endpoint should accept one-field business-email invoice review requests");
627+
const emailOnlySponsorLead = JSON.parse(store.data.get(`sponsor:lead:${emailOnlySponsorPayload.id}`));
628+
assert(emailOnlySponsorLead.company === "Onefield Sponsor", "Sponsor lead endpoint should infer a company label from the email domain");
629+
assert(emailOnlySponsorLead.website === "https://onefield-sponsor.example/", "Sponsor lead endpoint should infer a website from a business email domain");
630+
assert(emailOnlySponsorLead.commitment === "request-invoice", "One-field sponsor lead should keep invoice request commitment");
631+
const personalEmailSponsorResponse = await sponsorLeadSource.onRequestPost({
632+
request: new Request("https://example.test/api/sponsor-lead", {
633+
method: "POST",
634+
headers: { "Content-Type": "application/json", "CF-Connecting-IP": "203.0.113.21" },
635+
body: JSON.stringify(sponsorLeadBody({
636+
company: "",
637+
contactEmail: "buyer@gmail.com",
638+
website: "",
639+
dealId: "starter-fit-review",
640+
})),
641+
}),
642+
env,
643+
});
644+
const personalEmailSponsorPayload = await personalEmailSponsorResponse.json();
645+
assert(personalEmailSponsorResponse.status === 200 && personalEmailSponsorPayload.ok, "Sponsor lead endpoint should accept one-field email requests even when a website cannot be inferred");
646+
const personalEmailSponsorLead = JSON.parse(store.data.get(`sponsor:lead:${personalEmailSponsorPayload.id}`));
647+
assert(personalEmailSponsorLead.company === "Gmail", "Sponsor lead endpoint should keep an inferred review label for personal email domains");
648+
assert(personalEmailSponsorLead.website === "", "Sponsor lead endpoint should not invent a sponsor website from a personal email domain");
649+
assert(personalEmailSponsorLead.commitment === "request-invoice", "Personal-email sponsor lead should keep invoice request commitment");
612650
const finalOpsMetrics = await (await opsMetricsSource.onRequestGet({ env })).json();
613651
const finalPrintableProject = finalOpsMetrics.projects.find((row) => row.id === "printable-tools-lab");
614-
assert(finalOpsMetrics.sponsorInvoiceRequests === 3, "Ops metrics should count full, quick, and inferred-company sponsor invoice requests");
615-
assert(finalPrintableProject.summary.sponsorInvoiceRequests === 3, "Project ops metrics should count full, quick, and inferred-company sponsor invoice requests");
652+
assert(finalOpsMetrics.sponsorInvoiceRequests === 5, "Ops metrics should count full, quick, inferred-company, and one-field sponsor invoice requests");
653+
assert(finalPrintableProject.summary.sponsorInvoiceRequests === 5, "Project ops metrics should count full, quick, inferred-company, and one-field sponsor invoice requests");
616654
assert([...store.data.keys()].some((key) => key.startsWith("sponsor:validation:")), "Validation sponsor lead should use isolated KV keys");
617655
assert(Number(store.data.get("total:sponsor_lead_tests")) === 1, "Validation sponsor lead should count only validation tests");
618656
const githubPages = sellerMetrics.sources.find((row) => row.source === "github-pages");

0 commit comments

Comments
 (0)