← back to Dw Pitch Followup
pitch: list2 letter follows up on the specific QUOTE (number, total, items) not generic samples
c6994a4bb8660ee770ef62a2061b70fb506d40be · 2026-07-14 11:20:16 -0700 · Steve
New quote-followup branch in composeDeterministic for list2: cites the actual quote #(s), total, and quoted items with amounts, and asks to convert to an order. Sanitizes leaked dimension strings out of the project ref and strips empty "(Ref.)" from item names. Verified against a real $6,693.60 two-quote client.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
Diff
commit c6994a4bb8660ee770ef62a2061b70fb506d40be
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jul 14 11:20:16 2026 -0700
pitch: list2 letter follows up on the specific QUOTE (number, total, items) not generic samples
New quote-followup branch in composeDeterministic for list2: cites the actual quote #(s), total, and quoted items with amounts, and asks to convert to an order. Sanitizes leaked dimension strings out of the project ref and strips empty "(Ref.)" from item names. Verified against a real $6,693.60 two-quote client.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
server.js | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/server.js b/server.js
index a5ec916..ba37c53 100644
--- a/server.js
+++ b/server.js
@@ -310,7 +310,10 @@ function composeDeterministic(list, row, ctx, seed, showMfr) {
const alt = (list === 'list1' ? row.patterns : row.details) || [];
sampleItems = alt.map((p) => clientLabel(p, showMfr)).filter(Boolean).slice(0, 8).map((n) => ` • ${n}`);
}
- const proj = String(row.project || '').trim();
+ // Only treat `project` as a real project name — reject dimension/spec strings that leak in from
+ // the invoice line (e.g. '19.6" wide x 32.8 ft. per bolt') so the letter never says "your … project".
+ let proj = String(row.project || '').trim();
+ if (!/[a-z]{3}/i.test(proj) || /\b(wide|ft|bolt|inch|yard|sq|roll|panel|per\b)\b/i.test(proj) || /\d\s*["'”]/.test(proj)) proj = '';
const projRef = proj ? ` for your ${proj} project` : '';
// seed-varied flavor so each Regenerate reads fresh
@@ -347,6 +350,27 @@ function composeDeterministic(list, row, ctx, seed, showMfr) {
return { subject: 'Thank you for your order — Designer Wallcoverings', body: tyBody, engine: 'thank-you' };
}
+ // LIST 2 — "Quoted · No Order": the letter is about the SPECIFIC quote (number, total, items)
+ // and asks to move it to an order — not the generic "how did the samples go" note.
+ const fmtMoney = (n) => '$' + (Number(n) || 0).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
+ if (list === 'list2') {
+ const quotes = (Array.isArray(row.merch_invoices) ? row.merch_invoices : []).filter((i) => !i.booked);
+ const qtotal = row.quote_total || quotes.reduce((s, i) => s + (Number(i.total) || 0), 0);
+ const qnums = quotes.map((i) => (i.invoice ? `#${i.invoice}` : '')).filter(Boolean);
+ const qRef = qnums.length === 1 ? ` (quote ${qnums[0]})` : (qnums.length > 1 ? ` (quotes ${qnums.slice(0, 3).join(', ')})` : '');
+ const qItems = quotes.map((i) => { const it = clientLabel(i.item || '', showMfr).replace(/\(\s*Ref\.?\s*\)/gi, '').replace(/\s{2,}/g, ' ').trim(); return (it && /[a-z]/i.test(it)) ? ` • ${it}${i.total ? ` — ${fmtMoney(i.total)}` : ''}` : null; }).filter(Boolean);
+ const totalStr = qtotal ? ` totaling ${fmtMoney(qtotal)}` : '';
+ const qbody =
+ `Hi ${greetName},\n\n` +
+ `${g} I wanted to follow up on the quote we put together for you${projRef}${qRef}${totalStr}. We'd love to help you bring it to life — if you're ready to move forward, we'll confirm current pricing and stock and get your order placed right away.${tradeLine}\n\n` +
+ (qItems.length ? `Here's what we quoted…\n\n${qItems.slice(0, 12).join('\n')}\n\n` : '') +
+ `If anything's changed — quantities, colorways, or timing — just let me know and I'll update it. ${q}\n\n` +
+ `${signNames} — Designer Wallcoverings\n` +
+ `designerwallcoverings.com · 1-888-373-4564\n\n` +
+ `P.S. Happy to hold pricing or walk through options on a quick call — just say the word.`;
+ return { subject: `Following up on your Designer Wallcoverings quote${qnums.length === 1 ? ` ${qnums[0]}` : ''}`, body: qbody, engine: 'quote-followup' };
+ }
+
// ~3 tight sentences: (1) follow-up + final-purchase ask, (2) witty samples/stock + we're-here-to-help
// [+trade], (3) project-details line then a closing question. Then the sample reminder + sign-off + PS.
const body =
← f9b6323 pitch: Sent log is now LIVE — merge Gmail Sent (recipient-en
·
back to Dw Pitch Followup
·
pitch: letter panel shows product thumbnails + DW product-pa 9aa4cdc →