[object Object]

← back to Dw Signup Fulfillment

feat(email): approved branded retail welcome letter + new designer welcome-on-apply letter (Steve-approved 2026-08-07); send designer welcome to applicant on /trade/apply

7f5a947261067898182d51279a59f26ebd36d54b · 2026-08-07 06:47:03 -0700 · Steve Abrams

Files touched

Diff

commit 7f5a947261067898182d51279a59f26ebd36d54b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Aug 7 06:47:03 2026 -0700

    feat(email): approved branded retail welcome letter + new designer welcome-on-apply letter (Steve-approved 2026-08-07); send designer welcome to applicant on /trade/apply
---
 lib/email.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 server.js    |  8 +++++++
 2 files changed, 68 insertions(+), 9 deletions(-)

diff --git a/lib/email.js b/lib/email.js
index cc7a91f..fa56402 100644
--- a/lib/email.js
+++ b/lib/email.js
@@ -62,16 +62,67 @@ async function sendEmail({ to, subject, html, source }) {
 
 // ---- Templates ----
 
+// APPROVED retail welcome letter (Steve, 2026-08-07). Branded intro + the per-customer
+// gift code + "Explore the Collections" CTA. The gift card face value = count × sample price.
 function retailGiftEmail({ firstName, code, value, count }) {
-  const name = firstName ? ` ${firstName}` : '';
+  const greet = firstName ? esc(firstName) : 'there';
   const subject = `Your ${count} free Designer Wallcoverings samples 🎁`;
-  const html = [
-    `<p>Hi${name},</p>`,
-    `<p>Welcome to Designer Wallcoverings! As a thank-you for creating your account, here are your <b>${count} free samples</b>.</p>`,
-    `<p style="font-size:18px;">Your gift code: <b style="letter-spacing:1px;">${esc(code)}</b> &nbsp;(${money(value)} value)</p>`,
-    `<p>Add any ${count} sample swatches to your cart and enter this code at checkout — they're on us.</p>`,
-    `<p>Happy sampling!<br><br>Designer Wallcoverings<br>${config.GEORGE_FROM}</p>`,
-  ].join('\n');
+  const html = `<div style="font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;max-width:600px;margin:0 auto">
+  <div style="border:1px solid #e2ddd4;border-radius:10px;overflow:hidden">
+    <div style="background:#1a1a1a;color:#fff;text-align:center;padding:26px 20px">
+      <div style="font-size:22px;letter-spacing:3px;font-weight:600">DESIGNER WALLCOVERINGS</div>
+      <div style="font-size:11px;letter-spacing:2px;color:#b8afa2;margin-top:4px">FINE WALLCOVERINGS &amp; FABRICS</div>
+    </div>
+    <div style="padding:26px 28px;font-size:15px;line-height:1.7;color:#2a2a2a">
+      <p style="margin:0 0 14px">Dear ${greet},</p>
+      <p style="margin:0 0 14px">Welcome to Designer Wallcoverings — your new account opens the door to the world's most beautiful wallcoverings and fabrics, from over 200 of the finest design houses, all in one place.</p>
+      <p style="margin:0 0 8px">As a thank-you for joining us, here are your <b>${count} free samples</b>. Use this code at checkout:</p>
+      <div style="text-align:center;margin:18px 0">
+        <span style="display:inline-block;background:#f4f1ea;border:1px dashed #b8afa2;border-radius:8px;padding:14px 30px;font-size:22px;letter-spacing:3px;font-weight:700;color:#1a1a1a">${esc(code)}</span>
+      </div>
+      <p style="margin:0 0 14px;color:#555;font-size:14px">Apply it to any samples in your cart — it covers ${count === 3 ? 'three' : count} at no charge (${money(value)} value). Order swatches, see them in your space, and fall in love before you commit.</p>
+      <div style="text-align:center;margin:22px 0 6px">
+        <a href="https://designerwallcoverings.com" style="background:#1a1a1a;color:#fff;text-decoration:none;padding:13px 34px;border-radius:30px;font-size:14px;letter-spacing:1px;display:inline-block">Explore the Collections</a>
+      </div>
+      <p style="margin:20px 0 0;color:#2a2a2a">Warmly,<br><b>The Designer Wallcoverings Team</b></p>
+    </div>
+  </div>
+</div>`;
+  return { subject, html };
+}
+
+// APPROVED designer/trade welcome letter (Steve, 2026-08-07). Sent to the applicant
+// immediately after they submit a trade application — "about us + services". No code
+// (designers get unlimited memo samples via the trade tag once approved).
+function designerWelcomeEmail({ firstName }) {
+  const greet = firstName ? esc(firstName) : 'there';
+  const subject = `Welcome to Designer Wallcoverings — about us & how we work with you`;
+  const html = `<div style="font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;max-width:600px;margin:0 auto">
+  <div style="border:1px solid #e2ddd4;border-radius:10px;overflow:hidden">
+    <div style="background:#1a1a1a;color:#fff;text-align:center;padding:26px 20px">
+      <div style="font-size:22px;letter-spacing:3px;font-weight:600">DESIGNER WALLCOVERINGS</div>
+      <div style="font-size:11px;letter-spacing:2px;color:#b8afa2;margin-top:4px">TO THE TRADE</div>
+    </div>
+    <div style="padding:26px 28px;font-size:15px;line-height:1.7;color:#2a2a2a">
+      <p style="margin:0 0 14px">Dear ${greet},</p>
+      <p style="margin:0 0 14px">Thank you for your interest in a Designer Wallcoverings trade account — we're delighted to welcome you.</p>
+      <p style="margin:0 0 6px"><b>Who we are.</b> Designer Wallcoverings is a to-the-trade resource representing more than 200 of the world's premier wallcovering and fabric houses — names like Schumacher, Thibaut, Cole &amp; Son, Pierre Frey, Maya Romanoff, &Eacute;litis, Fromental and Gracie — curated in one place for designers, architects, and specifiers.</p>
+      <p style="margin:14px 0 6px"><b>How we work with you.</b></p>
+      <ul style="margin:0 0 14px;padding-left:20px;color:#333">
+        <li><b>Trade pricing</b> across every line we carry</li>
+        <li><b>Unlimited memo samples</b> — order freely for your projects</li>
+        <li><b>A dedicated account representative</b> who knows your work</li>
+        <li><b>Specification &amp; sourcing support</b> — if it exists, we'll find it</li>
+        <li><b>Fast quoting</b> and project-level service from swatch to install</li>
+      </ul>
+      <p style="margin:0 0 14px"><b>What happens next.</b> Your trade application is being reviewed. Once approved, your account is tagged for trade pricing and your rep will reach out personally to get you started.</p>
+      <div style="text-align:center;margin:22px 0 6px">
+        <a href="https://designerwallcoverings.com" style="background:#1a1a1a;color:#fff;text-decoration:none;padding:13px 34px;border-radius:30px;font-size:14px;letter-spacing:1px;display:inline-block">Browse the Lines</a>
+      </div>
+      <p style="margin:20px 0 0;color:#2a2a2a">At your service,<br><b>The Designer Wallcoverings Trade Team</b></p>
+    </div>
+  </div>
+</div>`;
   return { subject, html };
 }
 
@@ -159,4 +210,4 @@ function tradeApplicationEmail({ app, approveUrl, rejectUrl, adminUrl }) {
 function money(v) { return `$${Number(v).toFixed(2)}`; }
 function esc(s) { return String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c])); }
 
-module.exports = { sendEmail, retailCodeEmail, retailGiftEmail, repNotifyEmail, tradeApprovedEmail, tradeRejectedEmail, tradeApplicationEmail, money, esc };
+module.exports = { sendEmail, retailCodeEmail, retailGiftEmail, designerWelcomeEmail, repNotifyEmail, tradeApprovedEmail, tradeRejectedEmail, tradeApplicationEmail, money, esc };
diff --git a/server.js b/server.js
index de733fa..391f7b8 100644
--- a/server.js
+++ b/server.js
@@ -122,6 +122,14 @@ app.post('/trade/apply', tradeCors, (req, res) => {
   // Email the office inbox a review card with one-click Approve/Reject buttons.
   // Fire-and-forget so the applicant's response isn't blocked on George; DRY_RUN-safe.
   notifyTradeApplication(created).catch(e => console.error('[trade] notify failed:', e.message));
+  // Also send the applicant the "about us + services" welcome letter (approved 2026-08-07).
+  // Fire-and-forget + DRY_RUN-safe; a George hiccup must not fail the applicant's submit.
+  (async () => {
+    const first = created.first_name || (created.contact_name ? String(created.contact_name).split(' ')[0] : '') || (created.email ? created.email.split('@')[0] : '');
+    const { subject, html } = email.designerWelcomeEmail({ firstName: first });
+    const r = await email.sendEmail({ to: created.email, subject, html, source: 'designer-welcome' });
+    if (r && r.ok === false) console.error(`[trade] designer-welcome send FAILED for ${created.email}: ${r.error || r.status}`);
+  })().catch(e => console.error('[trade] designer-welcome error:', e.message));
   res.json({ ok: true, id: created.id, status: created.status, created_at: created.created_at });
 });
 

← 848697f fix(theme): point trade-apply modal at canonical signup.desi  ·  back to Dw Signup Fulfillment  ·  trade-invite outreach: time-boxed office-copy hook + send sc bb988c6 →