[object Object]

← back to Majilite Onboard

viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer

f5257e6597534d461a25d5d6cbc136d154d0a8e8 · 2026-08-12 08:53:18 -0700 · Steve

Files touched

Diff

commit f5257e6597534d461a25d5d6cbc136d154d0a8e8
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Aug 12 08:53:18 2026 -0700

    viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer
---
 public/index.html | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 84 insertions(+), 1 deletion(-)

diff --git a/public/index.html b/public/index.html
index c54378a..4ea8f97 100644
--- a/public/index.html
+++ b/public/index.html
@@ -59,6 +59,38 @@
   .when{font-size:10px;color:var(--dim);margin-top:6px}
   .spec-badge{position:absolute;top:6px;right:6px;font-size:9px;background:#0b0d10cc;border:1px solid var(--line);color:var(--teal);border-radius:6px;padding:1px 6px}
   .imgonly .meta{display:none}
+  /* ── List view ── */
+  .grid.list{display:flex;flex-direction:column;gap:6px}
+  .grid.list .card{display:grid;grid-template-columns:52px 1fr auto;align-items:center;border-radius:8px;gap:10px;padding:6px 12px 6px 6px}
+  .grid.list .thumb{width:52px;height:52px;min-width:52px;border-radius:6px;overflow:hidden}
+  .grid.list .thumb img{width:100%;height:100%;object-fit:cover}
+  .grid.list .meta{display:flex;flex-direction:column;gap:2px;padding:0}
+  .grid.list .meta .t{font-size:13px}
+  .grid.list .meta .sku{font-size:11px}
+  .grid.list .meta .row{margin-top:4px}
+  .grid.list .meta .when{margin-top:0}
+  .grid.list .spec-badge{position:static;font-size:9px}
+  /* ── View toggle buttons ── */
+  .view-btn{background:var(--panel);border:1px solid var(--line);color:var(--dim);border-radius:7px;padding:6px 11px;cursor:pointer;font-size:12px;transition:border-color .12s,color .12s}
+  .view-btn:hover{border-color:var(--teal);color:var(--ink)}
+  .view-btn.active{border-color:var(--teal);color:var(--teal)}
+  /* ── Fields ▾ popover ── */
+  .fieldmenu{position:relative}
+  .fbtn{background:var(--panel);border:1px solid var(--line);color:var(--dim);border-radius:7px;padding:6px 11px;cursor:pointer;font-size:12px;white-space:nowrap}
+  .fbtn:hover{border-color:var(--teal);color:var(--ink)}
+  .fpop{position:absolute;top:calc(100% + 6px);left:0;background:#0f1216;border:1px solid var(--line);border-radius:8px;box-shadow:0 10px 30px #0008;padding:8px 4px;z-index:30;min-width:180px}
+  .fpop[hidden]{display:none}
+  .fpop label{display:flex;align-items:center;gap:9px;padding:6px 12px;font-size:12.5px;color:var(--dim);cursor:pointer;border-radius:6px;white-space:nowrap}
+  .fpop label:hover{background:var(--panel);color:var(--ink)}
+  .fpop input{accent-color:var(--teal);margin:0}
+  /* ── field hide classes (grid only) ── */
+  body.hf-thumb   .grid:not(.list) .thumb{display:none}
+  body.hf-title   .grid:not(.list) .meta .t{display:none}
+  body.hf-sku     .grid:not(.list) .meta .sku{display:none}
+  body.hf-color   .grid:not(.list) .meta .row .dot,
+  body.hf-color   .grid:not(.list) .meta .row .chip[data-f="family"]{display:none}
+  body.hf-pattern .grid:not(.list) .meta .row .chip[data-f="pattern"]{display:none}
+  body.hf-when    .grid:not(.list) .meta .when{display:none}
   /* drawer */
   #drawer{position:fixed;top:0;right:0;height:100vh;width:420px;max-width:92vw;background:var(--panel);border-left:1px solid var(--line);transform:translateX(100%);transition:transform .18s;overflow:auto;z-index:20}
   #drawer.open{transform:none}
@@ -112,10 +144,25 @@
           <option value="pattern">Pattern A→Z</option>
           <option value="title">Title A→Z</option>
           <option value="sku">SKU A→Z</option>
+          <option value="color">Color A→Z</option>
+          <option value="vendor">Vendor A→Z</option>
         </select>
       </label>
       <label>Density <input type="range" id="dens" min="3" max="9" value="5"/></label>
       <label><input type="checkbox" id="imgonly"/> image-only</label>
+      <button class="view-btn" id="viewgrid" title="Grid view">⊞ Grid</button>
+      <button class="view-btn" id="viewlist" title="List view">☰ List</button>
+      <div class="fieldmenu">
+        <button class="fbtn" id="fieldbtn">Fields ▾</button>
+        <div class="fpop" id="fpop" hidden>
+          <label><input type="checkbox" data-mf="thumb" checked> Image</label>
+          <label><input type="checkbox" data-mf="title" checked> Title / Color</label>
+          <label><input type="checkbox" data-mf="sku" checked> SKU</label>
+          <label><input type="checkbox" data-mf="color" checked> Color swatch</label>
+          <label><input type="checkbox" data-mf="pattern" checked> Pattern chip</label>
+          <label><input type="checkbox" data-mf="when" checked> Created date</label>
+        </div>
+      </div>
       <button class="btn" id="reset">Reset</button>
     </div>
     <div class="grid" id="grid"></div>
@@ -219,7 +266,9 @@ function apply(){
     pattern:(a,b)=>a.pattern.localeCompare(b.pattern)||a.color.localeCompare(b.color),
     title:(a,b)=>a.title.localeCompare(b.title),
     family:(a,b)=>a.color_family.localeCompare(b.color_family)||a.title.localeCompare(b.title),
-    light:(a,b)=>hexLum(b.color_hex)-hexLum(a.color_hex), dark:(a,b)=>hexLum(a.color_hex)-hexLum(b.color_hex) }[s];
+    light:(a,b)=>hexLum(b.color_hex)-hexLum(a.color_hex), dark:(a,b)=>hexLum(a.color_hex)-hexLum(b.color_hex),
+    color:(a,b)=>String(a.color||'').localeCompare(String(b.color||'')),
+    vendor:(a,b)=>String(a.vendor||'').localeCompare(String(b.vendor||'')) }[s];
   VIEW.sort(cmp);
   $('#grid').innerHTML=VIEW.map(p=>cardHTML({...p,_new:false})).join('');
 }
@@ -258,6 +307,40 @@ $('#reset').addEventListener('click',()=>{FILTER={family:null,pattern:null,statu
 $('#run').addEventListener('click',()=>runImport(45));
 $('#replay').addEventListener('click',()=>runImport(20));
 
+// ── VIEW TOGGLE ────────────────────────────────────────────────────
+const GRID_EL=$('#grid');
+function setView(v){
+  GRID_EL.classList.toggle('list',v==='list');
+  $('#viewgrid').classList.toggle('active',v==='grid');
+  $('#viewlist').classList.toggle('active',v==='list');
+  // hide density slider in list mode (columns irrelevant)
+  $('#dens').parentElement.style.opacity=v==='list'?'.35':'1';
+  $('#dens').disabled=v==='list';
+  try{localStorage.setItem(LS('view'),v);}catch(_){}
+}
+$('#viewgrid').addEventListener('click',()=>setView('grid'));
+$('#viewlist').addEventListener('click',()=>setView('list'));
+setView(localStorage.getItem(LS('view'))||'grid');
+
+// ── FIELDS MENU ───────────────────────────────────────────────────
+const MFIELDS_DEFAULT={thumb:true,title:true,sku:true,color:true,pattern:true,when:true};
+let MFIELDS=Object.assign({},MFIELDS_DEFAULT);
+try{const s=localStorage.getItem(LS('fields'));if(s)MFIELDS=Object.assign({},MFIELDS_DEFAULT,JSON.parse(s));}catch(_){}
+function applyMFields(){
+  for(const k of Object.keys(MFIELDS_DEFAULT)){
+    document.body.classList.toggle('hf-'+k,!MFIELDS[k]);
+  }
+  document.querySelectorAll('#fpop input[data-mf]').forEach(cb=>{cb.checked=!!MFIELDS[cb.dataset.mf];});
+  try{localStorage.setItem(LS('fields'),JSON.stringify(MFIELDS));}catch(_){}
+}
+document.querySelectorAll('#fpop input[data-mf]').forEach(cb=>{
+  cb.addEventListener('change',()=>{MFIELDS[cb.dataset.mf]=cb.checked;applyMFields();});
+});
+applyMFields();
+// popover open/close
+$('#fieldbtn').addEventListener('click',e=>{e.stopPropagation();$('#fpop').hidden=!$('#fpop').hidden;});
+document.addEventListener('click',e=>{if(!e.target.closest('.fieldmenu'))$('#fpop').hidden=true;});
+
 // ---- 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);}

← cca8690 chore: v-patch (session close — DWCC viewer regen)  ·  back to Majilite Onboard  ·  nav-agent: universal grid-controls drop-in on internal viewe a9e93ff →