[object Object]

← back to All Designerwallcoverings

chore: vendor_contacts self-heal DDL, microsites compose validation, v1.6.0 (session close)

016fb393f15f37384a8cd41b2208ba124fb87d93 · 2026-07-15 13:57:41 -0700 · Steve Abrams

Files touched

Diff

commit 016fb393f15f37384a8cd41b2208ba124fb87d93
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jul 15 13:57:41 2026 -0700

    chore: vendor_contacts self-heal DDL, microsites compose validation, v1.6.0 (session close)
---
 package-lock.json      |  4 ++--
 package.json           |  2 +-
 public/microsites.html |  1 +
 server.js              | 22 ++++++++++++++++++++++
 4 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index c683a35..f1dfe10 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "all-designerwallcoverings",
-  "version": "1.5.0",
+  "version": "1.6.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "all-designerwallcoverings",
-      "version": "1.5.0",
+      "version": "1.6.0",
       "dependencies": {
         "@browserbasehq/sdk": "^2.15.0",
         "dotenv": "^17.4.2",
diff --git a/package.json b/package.json
index f4dd3fd..c3d83ee 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "all-designerwallcoverings",
-  "version": "1.5.0",
+  "version": "1.6.0",
   "description": "Front-facing vendor directory for all dw_unified vendors — all.designerwallcoverings.com",
   "main": "server.js",
   "scripts": {
diff --git a/public/microsites.html b/public/microsites.html
index 4172b7d..084726c 100644
--- a/public/microsites.html
+++ b/public/microsites.html
@@ -304,6 +304,7 @@ mmDraft.addEventListener('click', async () => {
   const to = mmTo.value.trim();
   mmStat.hidden = false; mmStat.className = 'mstat';
   if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(to)) { mmStat.className = 'mstat err'; mmStat.textContent = 'Enter the vendor rep’s email address first.'; return; }
+  if (!mmSub.value.trim() || !mmBody.value.trim()) { mmStat.className = 'mstat err'; mmStat.textContent = 'Subject and message are required.'; return; }
   mmDraft.disabled = true; const orig = mmDraft.textContent; mmDraft.textContent = 'Drafting…';
   mmStat.textContent = '◌ Saving contact + creating Gmail draft…';
   try {
diff --git a/server.js b/server.js
index 6b55595..7c56026 100644
--- a/server.js
+++ b/server.js
@@ -1184,10 +1184,31 @@ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 const mailtoUrl = (to, subject, body) =>
   `mailto:${encodeURIComponent(to)}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
 
+// vendor_contacts may not exist on a fresh DB (it was born on the mac3 mirror) — without
+// it every save degrades to contact_saved:false. Self-heal: create-if-missing, once.
+let CONTACTS_DDL_DONE = false;
+async function ensureVendorContactsTable() {
+  if (CONTACTS_DDL_DONE || !pool) return;
+  try {
+    await pool.query(`CREATE TABLE IF NOT EXISTS vendor_contacts (
+      vendor text PRIMARY KEY,
+      email text,
+      updated_at timestamptz,
+      draft_subject text,
+      draft_body text,
+      draft_saved_at timestamptz,
+      sheet_url text,
+      sheet_created_at timestamptz
+    )`);
+    CONTACTS_DDL_DONE = true;
+  } catch (e) { console.error('vendor_contacts init failed (will retry next request):', e.message); }
+}
+
 async function handleVendorContact(req, res, u) {
   const vendor = (u.searchParams.get('vendor') || '').trim();
   if (!vendor) return sendJSON(res, 400, { ok: false, reason: 'vendor required' });
   if (!pool) return sendJSON(res, 503, { ok: false, reason: 'db not ready' });
+  await ensureVendorContactsTable();
   try {
     const { rows } = await pool.query(
       `SELECT vendor, email, draft_subject, draft_body FROM vendor_contacts
@@ -1222,6 +1243,7 @@ async function handleVendorInquiry(req, res) {
   let saved = false;
   if (pool) {
     try {
+      await ensureVendorContactsTable();
       const upd = await pool.query(
         `UPDATE vendor_contacts SET email=$2, draft_subject=$3, draft_body=$4,
                 draft_saved_at=now(), updated_at=now()

← 3fa3f9f Email Vendor everywhere: stock & price inquiry compose on ev  ·  back to All Designerwallcoverings  ·  Kravet family gets a $0 authoritative-price live check (krav f9158ab →