← back to Dw Signup Fulfillment
scripts/send-trade-invites.js
62 lines
'use strict';
// One-off (Steve 2026-08-07, "1,2"): email the two PRE-FIX trade applicants who
// applied via /trade/apply before the welcome-letter wiring and have NO Shopify
// store account yet (so approve() hard-fails). Send them the approved designer
// welcome letter + a "create your account" CTA so they can make an account and
// then be approved for trade pricing. The office copy (info@) rides the email.js
// time-boxed hook (source 'designer-invite', through Wed 2026-08-12).
//
// MUST be run with DRY_RUN=0 to actually send (a bare node process defaults to
// dry-run; only the pm2 service carries DRY_RUN=0). Example:
// DRY_RUN=0 node scripts/send-trade-invites.js
const email = require('../lib/email');
const trade = require('../lib/trade');
// Explicit first names — these applicants' rows carry no name field, and deriving
// from the email gives "alison"/"hello", so name them correctly for a real outreach.
const TARGETS = [
{ id: 'TRADE-20260804-fbda2a', firstName: 'Alison' }, // Willmott Strickland
{ id: 'TRADE-20260805-d4aedd', firstName: 'Megan' }, // Megan Noelle Low
];
function cap(s) { s = String(s || '').trim(); return s ? s[0].toUpperCase() + s.slice(1) : s; }
function firstNameOf(app, override) {
if (override) return override;
if (app.first_name) return cap(String(app.first_name).trim());
if (app.contact_name) return cap(String(app.contact_name).trim().split(/\s+/)[0]);
if (app.name) return cap(String(app.name).trim().split(/\s+/)[0]);
return ''; // fall back to "there" in the template rather than an email handle
}
function inviteHtml(firstName) {
let { html } = email.designerWelcomeEmail({ firstName });
// Inject the missing "create your account" step (these applicants have no store account yet).
html = html.replace(
'<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.',
'<b>What happens next.</b> One quick step activates everything: <b>create your free Designer Wallcoverings account</b> (about a minute), and we’ll switch on your trade pricing and unlimited memo samples right away. Your dedicated rep will then reach out personally to get you started.'
);
// Repoint the primary CTA from "Browse the Lines" to the account-creation flow.
html = html.replace(
'<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>',
'<a href="https://designerwallcoverings.com/account" style="background:#1a1a1a;color:#fff;text-decoration:none;padding:13px 34px;border-radius:30px;font-size:14px;letter-spacing:1px;display:inline-block">Create Your Free Account</a>'
);
return html;
}
(async () => {
const rows = trade.readAll();
for (const t of TARGETS) {
const id = t.id;
const app = rows.find((a) => a.id === id);
if (!app) { console.log(`${id}: NOT FOUND, skipping`); continue; }
const fn = firstNameOf(app, t.firstName);
const subject = 'Designer Wallcoverings — your trade account: one step to activate';
const html = inviteHtml(fn);
const r = await email.sendEmail({ to: app.email, subject, html, source: 'designer-invite' });
console.log(`${id} -> ${app.email} (first="${fn}") ok=${r.ok !== false} dry=${r.dryRun || false} ref=${r.messageId || r.error || r.status || '?'}`);
}
// Let the fire-and-forget office copies (email.js hook) complete before the process exits.
await new Promise((res) => setTimeout(res, 3000));
})().catch((e) => { console.error('send error:', e.message); process.exit(1); });