[object Object]

← back to Designer Wallcoverings

Add Koroseal commercial wallcovering mailer for 2026-08-12 (from Majilite template)

cdc8b24af735571d39e040c4f920eddba3dac6b9 · 2026-08-12 11:04:18 -0700 · Steve Abrams

Files touched

Diff

commit cdc8b24af735571d39e040c4f920eddba3dac6b9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 12 11:04:18 2026 -0700

    Add Koroseal commercial wallcovering mailer for 2026-08-12 (from Majilite template)
---
 mailers/cc-api/build-koroseal-campaign.js | 134 ++++++++++++++++++++++++++++++
 mailers/koroseal-commercial-launch.html   |  71 ++++++++++++++++
 mailers/koroseal-commercial-preview.png   | Bin 0 -> 1755935 bytes
 3 files changed, 205 insertions(+)

diff --git a/mailers/cc-api/build-koroseal-campaign.js b/mailers/cc-api/build-koroseal-campaign.js
new file mode 100644
index 00000000..ce3f83e9
--- /dev/null
+++ b/mailers/cc-api/build-koroseal-campaign.js
@@ -0,0 +1,134 @@
+'use strict';
+
+/**
+ * build-hollywood-campaign.js — Designer Wallcoverings
+ * A DUPLICATE of build-pr-naturals-campaign.js (same validation + create path),
+ * pointed at ../hollywood-launch.html. Promotes this week's storewide New Arrivals.
+ *
+ *   --dry-run (DEFAULT): prints payload + validation checklist. NO network.
+ *   --confirm          : creates the DRAFT campaign — STILL double-gated by
+ *                        CC_LIVE === '1' inside cc-client. Creating a DRAFT
+ *                        does NOT send; scheduling/sending is a separate gate.
+ */
+
+const fs = require('fs');
+const path = require('path');
+const cc = require('./cc-client');
+
+const MAILER = path.join(__dirname, '..', 'koroseal-commercial-launch.html');
+
+const argv = process.argv.slice(2);
+const CONFIRM = argv.includes('--confirm');
+const DRY_RUN = argv.includes('--dry-run') || !CONFIRM; // default dry-run
+
+const CAMPAIGN = {
+  name: `Koroseal Commercial Wallcoverings — ${new Date().toISOString().slice(0, 10)}`,
+  subject: 'New: Koroseal Commercial Wallcoverings — contract-grade Type II vinyl',
+  preheader: 'Durable, scrubbable, Class A fire-rated wallcoverings for hospitality, healthcare & corporate interiors. Order swatches today.',
+  fromName: 'Designer Wallcoverings',
+  fromEmail: 'steve@designerwallcoverings.com',
+  replyTo: 'steve@designerwallcoverings.com',
+  physicalAddress: {
+    address_line1: '15442 Ventura Bl',
+    city: 'Sherman Oaks',
+    state_code: 'CA',
+    postal_code: '91403',
+    country_code: 'US',
+  },
+};
+
+const PLACEHOLDER_ADDRESS_MARKER = '15442 Ventura Boulevard #102';
+
+function loadHtml() {
+  if (!fs.existsSync(MAILER)) throw new Error(`mailer not found: ${MAILER}`);
+  return fs.readFileSync(MAILER, 'utf8');
+}
+function extractImageSrcs(html) {
+  const srcs = []; const re = /<img\b[^>]*?\bsrc\s*=\s*(["'])(.*?)\1/gi; let m;
+  while ((m = re.exec(html)) !== null) srcs.push(m[2]);
+  return srcs;
+}
+const isAbsoluteHttps = (u) => /^https:\/\//i.test(u);
+const unhostedLogoSrcs = (srcs) => srcs.filter((s) => /\/vendor-logos\//i.test(s));
+
+function visibleWallpaperHits(html) {
+  const hits = [];
+  const noComments = html.replace(/<!--[\s\S]*?-->/g, '');
+  const altRe = /\balt\s*=\s*(["'])(.*?)\1/gi; let m;
+  while ((m = altRe.exec(noComments)) !== null) if (/\bwallpapers?\b/i.test(m[2])) hits.push(`alt: "${m[2]}"`);
+  const textOnly = noComments.replace(/<[^>]+>/g, ' ');
+  for (const t of (textOnly.match(/\bwallpapers?\b/gi) || [])) hits.push(`visible text: "${t}"`);
+  return hits;
+}
+
+function runChecklist(html) {
+  const results = []; const add = (name, pass, detail) => results.push({ name, pass, detail });
+  add('subject set', !!CAMPAIGN.subject && CAMPAIGN.subject.trim().length > 0, CAMPAIGN.subject);
+  add('from name + email set', !!CAMPAIGN.fromName && !!CAMPAIGN.fromEmail && CAMPAIGN.fromEmail.includes('@'), `${CAMPAIGN.fromName} <${CAMPAIGN.fromEmail}>`);
+  add('reply-to set', !!CAMPAIGN.replyTo && CAMPAIGN.replyTo.includes('@'), CAMPAIGN.replyTo);
+  const addr = CAMPAIGN.physicalAddress || {};
+  const addrComplete = !!(addr.address_line1 && addr.city && addr.state_code && addr.postal_code && addr.country_code);
+  const addrIsPlaceholder = (addr.address_line1 || '').includes(PLACEHOLDER_ADDRESS_MARKER);
+  add('physical address complete', addrComplete, JSON.stringify(addr));
+  add('physical address non-placeholder', addrComplete && !addrIsPlaceholder, addrIsPlaceholder ? 'matches placeholder marker' : 'confirmed non-placeholder');
+  add('html content non-empty', !!html && html.length > 200, `${html.length} bytes`);
+  const bannedHits = visibleWallpaperHits(html);
+  add('no banned word "Wallpaper" (visible copy + alt)', bannedHits.length === 0, bannedHits.length ? `found:\n      - ${bannedHits.join('\n      - ')}` : 'clean (alt + visible text)');
+  const srcs = extractImageSrcs(html);
+  const nonHttps = srcs.filter((s) => !isAbsoluteHttps(s));
+  add('all image src absolute https', nonHttps.length === 0, nonHttps.length ? `${nonHttps.length}/${srcs.length} not absolute-https` : `${srcs.length}/${srcs.length} absolute-https`);
+  const unhosted = unhostedLogoSrcs(srcs);
+  add('logos hosted at public https (not the un-hosted /vendor-logos/ path)', unhosted.length === 0, unhosted.length ? `${unhosted.length} on un-hosted path` : 'all logos on a confirmed-live host');
+  return results;
+}
+
+function buildPayload(html) {
+  return {
+    name: CAMPAIGN.name,
+    email_campaign_activities: [{
+      format_type: 5,
+      from_name: CAMPAIGN.fromName,
+      from_email: CAMPAIGN.fromEmail,
+      reply_to_email: CAMPAIGN.replyTo,
+      subject: CAMPAIGN.subject,
+      preheader: CAMPAIGN.preheader,
+      html_content: html,
+      physical_address_in_footer: CAMPAIGN.physicalAddress,
+    }],
+  };
+}
+
+async function main() {
+  const html = loadHtml();
+  console.log('='.repeat(72));
+  console.log('New Arrivals Weekly → Constant Contact v3 campaign builder');
+  console.log(`mode: ${CONFIRM ? '--confirm' : '--dry-run (default)'}   CC_LIVE=${process.env.CC_LIVE === '1' ? '1' : '(unset)'}`);
+  console.log('='.repeat(72));
+
+  const payload = buildPayload(html);
+  const printable = JSON.parse(JSON.stringify(payload));
+  printable.email_campaign_activities[0].html_content = `<<${html.length} bytes of HTML — truncated>>\n` + html.slice(0, 300) + '\n…';
+  console.log('\n--- CREATE-CAMPAIGN PAYLOAD (html_content truncated) ---');
+  console.log(JSON.stringify(printable, null, 2));
+
+  console.log('\n--- VALIDATION CHECKLIST ---');
+  const results = runChecklist(html); let failed = 0;
+  for (const r of results) { const mark = r.pass ? 'PASS' : 'FAIL'; if (!r.pass) failed++; console.log(`  [${mark}] ${r.name}`); if (r.detail) console.log(`         ${r.detail}`); }
+  console.log(`\n  ${results.length - failed}/${results.length} checks passed, ${failed} failed.`);
+
+  if (DRY_RUN) {
+    console.log('\nDRY-RUN: no network call made. (default)');
+    process.exitCode = failed > 0 ? 2 : 0;
+    return;
+  }
+  if (failed > 0) { console.log('\nREFUSING to create: validation has FAILs.'); process.exitCode = 2; return; }
+  console.log('\n--confirm + validation clean → creating DRAFT (NO-OPs unless CC_LIVE=1):');
+  const activityId = await cc.createCustomCodeCampaign({
+    name: CAMPAIGN.name, subject: CAMPAIGN.subject, preheader: CAMPAIGN.preheader,
+    fromEmail: CAMPAIGN.fromEmail, fromName: CAMPAIGN.fromName, replyTo: CAMPAIGN.replyTo,
+    htmlContent: html, physicalAddress: CAMPAIGN.physicalAddress, confirm: true,
+  });
+  console.log('result:', activityId);
+}
+
+main().catch((e) => { console.error('[build-new-arrivals] ERROR:', e.message); process.exitCode = 1; });
diff --git a/mailers/koroseal-commercial-launch.html b/mailers/koroseal-commercial-launch.html
new file mode 100644
index 00000000..6a1b5835
--- /dev/null
+++ b/mailers/koroseal-commercial-launch.html
@@ -0,0 +1,71 @@
+<!-- Constant Contact campaign — Koroseal Commercial Wallcoverings (email-safe tables + inline styles) -->
+<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#0d0f12;margin:0;padding:0">
+<tr><td align="center" style="padding:0">
+  <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;background:#14171c;font-family:Helvetica,Arial,sans-serif;color:#e9ecf1">
+
+    <!-- wordmark -->
+    <tr><td align="center" style="padding:22px 24px 8px">
+      <div style="font-size:13px;letter-spacing:.28em;color:#b7bcc2;text-transform:uppercase">Designer Wallcoverings &amp; Fabrics</div>
+    </td></tr>
+
+    <!-- hero -->
+    <tr><td style="padding:0">
+      <img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/SILKEN_ONE-GRANDIOSE_S522-35_e44d551f-dcb5-4f08-b64d-2c5456b598a2.jpg?v=1751949410" width="600" alt="Koroseal Silken One Grandiose commercial wallcovering in gold" style="display:block;width:100%;height:auto;border:0"/>
+    </td></tr>
+
+    <!-- headline -->
+    <tr><td align="center" style="padding:26px 30px 6px">
+      <div style="font-size:12px;letter-spacing:.22em;color:#c9a24a;text-transform:uppercase;margin-bottom:10px">Commercial Collection · Contract-Grade</div>
+      <h1 style="margin:0;font-size:30px;line-height:1.15;font-weight:400;letter-spacing:.01em;color:#ffffff;font-style:italic">Koroseal Commercial Wallcoverings</h1>
+    </td></tr>
+
+    <!-- body -->
+    <tr><td align="center" style="padding:14px 40px 6px">
+      <p style="margin:0;font-size:15px;line-height:1.6;color:#c9ced6">
+        <strong>Type&nbsp;II commercial-grade wallcoverings</strong> engineered for high-traffic
+        hospitality, healthcare &amp; corporate interiors &mdash; durable, scrubbable, and
+        <strong>Class&nbsp;A fire-rated</strong> (ASTM&nbsp;E84). A deep library of textured grounds
+        and colorways built for large-scale specification and easy installation.
+      </p>
+    </td></tr>
+
+    <!-- swatch trio -->
+    <tr><td style="padding:22px 30px 6px">
+      <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>
+        <td width="33%" style="padding:4px"><img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/ZEPHYR_GREY_0204D271__REPUBLIC_OF_II_BY_IV_FOR_KOROSEAL.jpg?v=1751967203" width="100%" alt="Zephyr Grey" style="display:block;width:100%;height:auto;border-radius:6px;border:0"/></td>
+        <td width="33%" style="padding:4px"><img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/SG28-04_HAVEN-AVE_KALAMATA.jpg?v=1751952779" width="100%" alt="Haven Ave Kalamata Taupe" style="display:block;width:100%;height:auto;border-radius:6px;border:0"/></td>
+        <td width="33%" style="padding:4px"><img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/THREADED_SILVER_TH21-10__KOROSEAL_050eb8c4-471c-4906-9271-08ab65041b82.jpg?v=1751948306" width="100%" alt="Threaded Silver Gray" style="display:block;width:100%;height:auto;border-radius:6px;border:0"/></td>
+      </tr></table>
+    </td></tr>
+
+    <!-- CTA -->
+    <tr><td align="center" style="padding:22px 30px 8px">
+      <a href="https://www.designerwallcoverings.com/collections/koroseal"
+         style="display:inline-block;background:#5b6cff;color:#ffffff;text-decoration:none;font-size:15px;font-weight:600;letter-spacing:.02em;padding:14px 34px;border-radius:8px">
+        Shop the Collection &rarr;
+      </a>
+      <div style="margin-top:12px;font-size:13px;color:#8a929e">Order any swatch as a <strong style="color:#c9ced6">$4.25 sample</strong>.</div>
+    </td></tr>
+
+    <!-- reassurance line -->
+    <tr><td align="center" style="padding:16px 40px 8px">
+      <div style="font-size:12px;line-height:1.6;color:#8a929e;border-top:1px solid #232830;padding-top:18px">
+        54&quot; wide &middot; sold by the yard &middot; Type&nbsp;II 20&nbsp;oz vinyl &middot; scrubbable &amp; mildew-resistant &middot; trade pricing available.<br>
+        In stock now at Designer Wallcoverings &amp; Fabrics.
+      </div>
+    </td></tr>
+
+    <!-- CAN-SPAM footer: CC injects the account's registered physical address + a working
+         unsubscribe link at send time. Tokens below satisfy the pre-flight gate. -->
+    <tr><td align="center" style="padding:0 40px 26px">
+      <div style="font-size:11px;line-height:1.6;color:#6b7079">
+        Designer Wallcoverings &amp; Fabrics &nbsp;&middot;&nbsp; (888) 373-4564 &nbsp;&middot;&nbsp; DesignerWallcoverings.com<br>
+        [[physical_address]]<br>
+        You received this because you're a Designer Wallcoverings customer or trade contact.
+        &nbsp;<a href="[[UNSUBSCRIBE]]" style="color:#8a929e">Unsubscribe</a> or update your preferences.
+      </div>
+    </td></tr>
+
+  </table>
+</td></tr>
+</table>
diff --git a/mailers/koroseal-commercial-preview.png b/mailers/koroseal-commercial-preview.png
new file mode 100644
index 00000000..c101cb33
Binary files /dev/null and b/mailers/koroseal-commercial-preview.png differ

← 1b04ddec auto-data-snapshot: 2026-08-12T10:57:24 (4 data files) — sho  ·  back to Designer Wallcoverings  ·  Add Hollywood Wallcoverings mailer staged for 2026-08-19 (ne 2769fe62 →