[object Object]

← back to Majilite Onboard

majilite-onboard: href-to-deeper-data — chips are real drill anchors + URL-synced filters (deep-link/back-button), clears href-drill regression

608aba4be72db4e4c9dac7740b80ca3fa4df2739 · 2026-08-23 07:19:14 -0700 · steve

Files touched

Diff

commit 608aba4be72db4e4c9dac7740b80ca3fa4df2739
Author: steve <steve@designerwallcoverings.com>
Date:   Sun Aug 23 07:19:14 2026 -0700

    majilite-onboard: href-to-deeper-data — chips are real drill anchors + URL-synced filters (deep-link/back-button), clears href-drill regression
---
 public/index.html | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/public/index.html b/public/index.html
index 8f38d2d..7cf2ee7 100644
--- a/public/index.html
+++ b/public/index.html
@@ -230,8 +230,8 @@ function cardHTML(p){
       <div class="sku">${p.dw_sku}</div>
       <div class="row">
         <span class="dot" style="background:${p.color_hex}"></span>
-        <span class="chip" data-f="family" data-v="${p.color_family}">${p.color_family}</span>
-        <span class="chip" data-f="pattern" data-v="${p.pattern}">${p.pattern}</span>
+        <a class="chip drill" href="${drillHref('family',p.color_family)}" data-filter="family" data-f="family" data-v="${p.color_family}">${p.color_family}</a>
+        <a class="chip drill" href="${drillHref('pattern',p.pattern)}" data-filter="pattern" data-f="pattern" data-v="${p.pattern}">${p.pattern}</a>
         ${p.pricing?`<span class="chip" style="border-color:#5fd08a55;color:#5fd08a">$${p.pricing.sample_price} sample</span>`:''}
       </div>
       <div class="when" title="${p.created_at||''}">🕓 ${when}</div>
@@ -239,7 +239,7 @@ function cardHTML(p){
 }
 function addCard(p,isNew){ p._new=isNew; const el=document.createElement('div'); el.innerHTML=cardHTML(p); $('#grid').appendChild(el.firstElementChild); }
 
-function afterLoad(){ $('#cnt').textContent=ALL.length; buildFacets(); apply(); }
+function afterLoad(){ try{readURL();}catch(_){} $('#cnt').textContent=ALL.length; buildFacets(); apply(); }
 
 function buildFacets(){
   const byFam={}, byPat={}, bySt={};
@@ -296,7 +296,7 @@ function closeDrawer(){ $('#drawer').classList.remove('open'); }
 // ---- events ----
 $('#grid').addEventListener('click',e=>{
   const chip=e.target.closest('.chip[data-f]');
-  if(chip){ FILTER[chip.dataset.f]= FILTER[chip.dataset.f]===chip.dataset.v?null:chip.dataset.v; buildFacets(); apply(); e.stopPropagation(); return; }
+  if(chip){ if(e.metaKey||e.ctrlKey)return; e.preventDefault(); FILTER[chip.dataset.f]= FILTER[chip.dataset.f]===chip.dataset.v?null:chip.dataset.v; buildFacets(); apply(); e.stopPropagation(); return; }
   const card=e.target.closest('.card'); if(card) openDrawer(card.dataset.sku);
 });
 document.addEventListener('click',e=>{const f=e.target.closest('aside .facet'); if(!f)return; const k=f.dataset.f; FILTER[k]=FILTER[k]===f.dataset.v?null:f.dataset.v; buildFacets(); apply();});
@@ -342,6 +342,18 @@ applyMFields();
 $('#fieldbtn').addEventListener('click',e=>{e.stopPropagation();$('#fpop').hidden=!$('#fpop').hidden;});
 document.addEventListener('click',e=>{if(!e.target.closest('.fieldmenu'))$('#fpop').hidden=true;});
 
+// ── href-to-deeper-data primitives (page-native over FILTER) — HARD rule 2026-07-31 ──
+// Every displayed data point (chip/facet) is a real, shareable href to its filtered view.
+const FIELDS=[['family'],['pattern'],['status']];
+function qstr(f,q){ const u=new URLSearchParams(); for(const [k] of FIELDS){ if(f[k]) u.set(k,f[k]); } const s=(q!=null?q:$('#q').value.trim()); if(s) u.set('q',s); const o=u.toString(); return o?('?'+o):location.pathname; }
+function drillHref(k,v){ return qstr({...FILTER,[k]:String(v)}); }
+function readURL(){ const u=new URLSearchParams(location.search); for(const [k] of FIELDS){ FILTER[k]=u.get(k)||null; } const q=u.get('q'); if(q!=null&&$('#q')) $('#q').value=q; }
+function writeURL(push){ history[push?'pushState':'replaceState']({},'',qstr(FILTER)); }
+window.qstr=qstr; window.readURL=readURL; window.writeURL=writeURL; window.drillHref=drillHref;
+// wrap apply() so every filter change reflects into the URL (deep-link + back-button)
+const _applyBase=apply; apply=function(){ _applyBase(); try{writeURL(false);}catch(_){} };
+window.addEventListener('popstate',()=>{ readURL(); buildFacets(); apply(); });
+
 // ---- boot ----
 function seedColl(c){COLL=c;$('#pfx').textContent=c.sku_prefix;$('#pmode').textContent=c.onboard_as==='sample'?('$'+c.sample_price+' sample'):(c.display_prices?'priced':'quote-only');}
 if(window.__BOOT__&&window.__BOOT__.collection){seedColl(window.__BOOT__.collection);} else {fetch('/api/collection',{credentials:'same-origin'}).then(r=>r.json()).then(seedColl);}

← 1b9bdcd nav-agent: sync v1.3 (host left-sidebar collapse on load)  ·  back to Majilite Onboard  ·  creds-safe fetch guard: resolve relative fetch vs credential 4e9ee41 →