[object Object]

← back to Dw Photo Capture

Add collections to search: each product carries its Shopify collection titles; lookup matches collection name + cards show 📚 collection chips

418e97242152c4d0fc8e806586ec3952e8f97da1 · 2026-06-24 19:06:06 -0700 · steve@designerwallcoverings.com

Files touched

Diff

commit 418e97242152c4d0fc8e806586ec3952e8f97da1
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date:   Wed Jun 24 19:06:06 2026 -0700

    Add collections to search: each product carries its Shopify collection titles; lookup matches collection name + cards show 📚 collection chips
---
 data/build.json   | 5 +++--
 public/index.html | 5 ++++-
 server.js         | 6 ++++--
 3 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/data/build.json b/data/build.json
index 9a222da..e19e8a3 100644
--- a/data/build.json
+++ b/data/build.json
@@ -1,5 +1,5 @@
 {
-  "next": 17,
+  "next": 18,
   "map": {
     "578af86f": 2,
     "bc95fdb0": 3,
@@ -15,6 +15,7 @@
     "788487db": 13,
     "3a1be30f": 14,
     "d0f0df55": 15,
-    "9905b54e": 16
+    "9905b54e": 16,
+    "48c9e659": 17
   }
 }
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 6a7cc7a..dfb8f18 100644
--- a/public/index.html
+++ b/public/index.html
@@ -42,6 +42,8 @@
   .model b{font:800 16px/1 ui-monospace,Menlo,monospace;color:var(--ink);letter-spacing:.03em}
   .sku{font:600 12px/1 ui-monospace,Menlo,monospace;color:var(--gold);letter-spacing:.02em}
   .sub{font-size:11.5px;color:var(--muted);display:flex;gap:8px;flex-wrap:wrap;align-items:center}
+  .cols{display:flex;gap:5px;flex-wrap:wrap}
+  .colchip{font-size:10px;color:#bcd;background:#16201c;border:1px solid #2a3a33;border-radius:99px;padding:2px 7px;white-space:nowrap}
   .price{color:var(--green);font-weight:700}
   .price.no{color:var(--muted)}
   .when{font-size:11px;color:var(--muted)}
@@ -284,6 +286,7 @@ function render(){
           <span class="price ${priced(x)?'':'no'}">${priced(x)?'$'+x.price:'no price'}</span>
           <span>${isNew?'NEW · not on Shopify':x.status}</span>
         </div>
+        ${(x.collections&&x.collections.length)?`<div class="cols">${x.collections.slice(0,4).map(c=>'<span class="colchip">📚 '+c+'</span>').join('')}</div>`:''}
         ${x.ts?`<div class="when">🕓 ${fmt(x.ts)}${x.live?(x.created?' · 🟢 CREATED + LIVE':' · 🟢 LIVE on store'):x.shopify_pushed?(x.live_reason?(' · photo on Shopify ('+x.live_reason+')'):' · photo on Shopify'):''}</div>`:''}
         ${x.push_err?`<div class="when" style="color:var(--red)">${x.push_err}</div>`:''}
         ${(x.keep_images&&x.image)?`<label class="onlyimg${x._replaceAll?' armed':''}"><input type="checkbox" class="onlyimg-cb"${x._replaceAll?' checked':''}> Replace all — make this the only image</label>`:''}
@@ -596,7 +599,7 @@ $('#sort').addEventListener('change',e=>{setLS('sort',e.target.value);render();}
 ['input','change'].forEach(ev=>$('#dens').addEventListener(ev,e=>{applyDensity(e.target.value);setLS('dens',e.target.value);}));
 document.querySelectorAll('.chip').forEach(c=>c.addEventListener('click',()=>{
   document.querySelectorAll('.chip').forEach(z=>z.classList.remove('on'));c.classList.add('on');filter=c.dataset.f;setLS('filter',filter);
-  if(filter==='any'){ $('#q').placeholder='🔎 Any TWIL SKU, model #, or name…'; $('#q').focus(); doLookup(); }
+  if(filter==='any'){ $('#q').placeholder='🔎 SKU, model #, name, or collection…'; $('#q').focus(); doLookup(); }
   else if(filter==='shop'){ $('#q').placeholder='🌐 Any Shopify SKU, name, or vendor…'; $('#q').focus(); doShopSearch(); }
   else if(filter==='new'){ $('#q').placeholder='🔎 Filter the new SKUs…'; loadNew(); }
   else { $('#q').placeholder='🔎 Search name or model #…'; render(); }
diff --git a/server.js b/server.js
index fecedd9..8c72562 100644
--- a/server.js
+++ b/server.js
@@ -176,7 +176,7 @@ const server = http.createServer((req, res) => {
     if (!q) return send(res, 200, { items: [], indexed: CATALOG.length });
     const terms = q.split(/\s+/);
     const matches = INDEX.filter(x => {
-      const hay = `${x.dw_sku} ${x.mfr} ${x.title}`.toLowerCase();
+      const hay = `${x.dw_sku} ${x.mfr} ${x.title} ${(x.collections || []).join(' ')}`.toLowerCase();
       return terms.every(t => hay.includes(t));
     }).slice(0, 80);
     return send(res, 200, { total: matches.length, items: matches, indexed: INDEX.length });
@@ -388,7 +388,7 @@ function rebuildIndex() {
   const sheetOnly = SHEET.filter(s => !haveSku.has(s.grs.toUpperCase()) && !(s.mfr && haveMfr.has(nmfr(s.mfr)))).map(s => ({
     product_id: null, title: sheetTitle(s), status: 'NEW', dw_sku: s.grs, mfr: s.mfr || '',
     price: s.price || null, image: s.img1 && /^https?:/.test(s.img1) ? s.img1 : null,
-    needs_create: true, done: false
+    collections: ['TWIL Naturals', 'Grasscloth'], needs_create: true, done: false
   }));
   INDEX = CATALOG.concat(sheetOnly);
   console.log(`lookup index: ${CATALOG.length} live + ${sheetOnly.length} new-from-sheet = ${INDEX.length}`);
@@ -397,6 +397,7 @@ async function buildCatalog() {
   const Q = `query($c:String){products(first:100,query:"vendor:Fentucci",after:$c){pageInfo{hasNextPage endCursor}
     edges{node{legacyResourceId title status
       imgs:images(first:1){edges{node{src}}}
+      cols:collections(first:8){edges{node{title}}}
       mf:metafield(namespace:"custom",key:"manufacturer_sku"){value}
       mf2:metafield(namespace:"dwc",key:"manufacturer_sku"){value}
       v:variants(first:3){edges{node{sku title price}}}}}}}`;
@@ -415,6 +416,7 @@ async function buildCatalog() {
           dw_sku: (roll || {}).sku || '', mfr: (n.mf || {}).value || (n.mf2 || {}).value || '',
           price: (roll || {}).price || null,
           image: (n.imgs.edges[0] && n.imgs.edges[0].node.src) || null,
+          collections: (n.cols.edges || []).map(c => c.node.title),
           done: false
         });
       }

← 087b386 Create 22 net-new GRS drafts (full sheet info, photo->live)  ·  back to Dw Photo Capture  ·  All-Shopify search covers collections too: parallel product 3cbfbc0 →