← 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
M package-lock.jsonM package.jsonM public/microsites.htmlM server.js
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 →