[object Object]

← back to Dw Pairs Well

Design Coordinate: guard degenerate pattern labels (no more '| Vendor' tiles)

76a8e98203731655f262114c6dde670a0b81e56f · 2026-07-12 23:03:12 -0700 · Steve Abrams

Products with malformed titles ('| Osborne & Little Europe', '| DW Bespoke
Studios') rendered the VENDOR as the pattern name on grouped tiles + colorways.
bestPatternLabel() detects empty/pipe-led/vendor-only labels and falls back to
humanized handle -> mfr_root -> dw_sku, recovering real names from descriptive
handles. Display-only; group keys unchanged. Healthy titles pass through as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 76a8e98203731655f262114c6dde670a0b81e56f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Jul 12 23:03:12 2026 -0700

    Design Coordinate: guard degenerate pattern labels (no more '| Vendor' tiles)
    
    Products with malformed titles ('| Osborne & Little Europe', '| DW Bespoke
    Studios') rendered the VENDOR as the pattern name on grouped tiles + colorways.
    bestPatternLabel() detects empty/pipe-led/vendor-only labels and falls back to
    humanized handle -> mfr_root -> dw_sku, recovering real names from descriptive
    handles. Display-only; group keys unchanged. Healthy titles pass through as-is.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 server.js | 39 +++++++++++++++++++++++++++++++++++----
 1 file changed, 35 insertions(+), 4 deletions(-)

diff --git a/server.js b/server.js
index e8be29c..349af2f 100644
--- a/server.js
+++ b/server.js
@@ -532,6 +532,39 @@ function colorNameForRow(row, patternBaseWords) {
   return base || null;
 }
 
+// Guard against degenerate pattern LABELS (display only — never the group key).
+// Some ACTIVE products carry malformed titles/pattern_names that are just a vendor
+// suffix ("| Osborne & Little Europe") or empty — rendering those verbatim shows the
+// VENDOR (or a bare pipe) where the pattern name belongs. A leading "|" is the tell:
+// the pattern segment before the vendor suffix was empty. When the computed label is
+// empty / pipe-led / vendor-only, fall back to mfr_root → humanized handle → dw_sku
+// so a tile never displays "| Vendor". Root cause is upstream catalog data (junk
+// title/mfr_sku); this only hardens the widget against it.
+function bestPatternLabel(row, computed) {
+  const titleCase = s => String(s).replace(/\b\w/g, c => c.toUpperCase());
+  const raw = String(computed || '').trim();
+  const vendor = String(row.vendor || '').trim().toLowerCase();
+  const s = raw.replace(/^[\s|·—–-]+/, '').replace(/[\s|·—–-]+$/, '').trim();
+  const degenerate = !s
+    || raw.startsWith('|')                                   // empty pattern base before vendor suffix
+    || /^[\s|·—–-]+$/.test(raw)
+    || s.toLowerCase() === vendor
+    || s.toLowerCase().replace(/\s+(europe|usa|na|studios?)$/, '') === vendor;
+  if (!degenerate) return titleCase(s);
+  // DW handles are descriptive ("agapantha-trellis-mural-black") — prefer a humanized
+  // handle over an ALL-CAPS mfr code, but only when it carries real words (not a bare
+  // SKU slug like "eur-71203"). Then mfr_root (non-junk), then dw_sku.
+  const h = String(row.handle || '')
+    .replace(/-designer-wallcoverings-los-angeles$/i, '')
+    .replace(/[-_]+/g, ' ').trim();
+  const hasWords = /[a-z]{3,}/i.test(h.replace(/\b[a-z]{1,2}\d/gi, '')) && h.split(' ').some(w => /^[a-z]{3,}$/i.test(w));
+  if (h && hasWords) return titleCase(h);
+  const root = gMfrRoot(row.mfr_sku);
+  if (root && !/^\d{1,3}$/.test(root)) return titleCase(root.replace(/[-_]+/g, ' '));
+  if (h) return titleCase(h);                                // bare SKU-ish handle beats dw_sku
+  return row.dw_sku || 'Pattern';
+}
+
 // Resolve a single product to its group key (used when caller passes handle/dw_sku).
 async function resolveGroupKey({ dw_sku, handle, pattern_id }) {
   if (pattern_id) return pattern_id;
@@ -596,9 +629,7 @@ app.get('/api/colorways', async (req, res) => {
     const labelBase = patternBaseWords.length
       ? patternBaseWords.join(' ')
       : (gStripColors(gNormTitle(members[0].title)) || members[0].pattern_name || '');
-    const pattern = labelBase
-      ? labelBase.replace(/\b\w/g, c => c.toUpperCase())
-      : (members[0].pattern_name || members[0].dw_sku);
+    const pattern = bestPatternLabel(members[0], labelBase);
     const patSetWords = new Set(patternBaseWords);
 
     const colorways = members
@@ -725,7 +756,7 @@ app.get('/api/collection-grouped', async (req, res) => {
         const labelBase = gStripColors(gNormTitle(row.title)) || (row.pattern_name || '');
         g = {
           pattern_id: key,
-          pattern: labelBase ? labelBase.replace(/\b\w/g, c => c.toUpperCase()) : (row.pattern_name || row.dw_sku),
+          pattern: bestPatternLabel(row, labelBase),
           vendor: row.vendor || null,
           // representative tile = first (newest) member
           rep_handle: row.handle, rep_dw_sku: row.dw_sku, rep_image_url: row.image_url,

← d53de9e auto-save: 2026-07-07T08:56:34 (1 files) — tools/pattern-id-  ·  back to Dw Pairs Well  ·  CLIP-1: /api/similar?engine=clip — image-embedding nearest-n 52301ea →