[object Object]

← back to Wild Wallcoverings

viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid

87c194d23e55e8124aee9a223776f61a2f5cad22 · 2026-08-12 08:33:42 -0700 · Steve

Files touched

Diff

commit 87c194d23e55e8124aee9a223776f61a2f5cad22
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Aug 12 08:33:42 2026 -0700

    viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
---
 public/index.html | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 66 insertions(+), 6 deletions(-)

diff --git a/public/index.html b/public/index.html
index cb9eb9c..29ad210 100644
--- a/public/index.html
+++ b/public/index.html
@@ -14,8 +14,24 @@
   h1 { font-size: 20px; margin: 0; letter-spacing: .06em; font-weight: 600; }
   h1 em { font-style: normal; color: #7ec8a9; }
   .sub { color: #888; font-size: 13px; }
-  .controls { display: flex; gap: 14px; align-items: center; padding: 14px 24px; }
+  .controls { display: flex; gap: 14px; align-items: center; padding: 14px 24px; flex-wrap: wrap; }
   .controls label { font-size: 12px; color: #aaa; }
+  /* ── Fields menu (wild-wallcoverings admin viewer) ─────────────────── */
+  .ww-fieldmenu { position:relative; flex-shrink:0; }
+  .ww-fbtn { padding:5px 12px; border:1px solid #3c6; background:transparent; color:#7ec8a9; font-size:11px; letter-spacing:.06em; text-transform:uppercase; cursor:pointer; border-radius:4px; }
+  .ww-fbtn:hover { background:rgba(78,200,128,.08); }
+  .ww-fpop { position:absolute; top:calc(100% + 6px); right:0; background:#1c1c1c; border:1px solid #333; box-shadow:0 8px 24px rgba(0,0,0,.55); padding:6px 4px; z-index:40; min-width:180px; border-radius:6px; }
+  .ww-fpop[hidden] { display:none; }
+  .ww-fpop label { display:flex; align-items:center; gap:10px; padding:7px 14px; font-size:11px; color:#ccc; cursor:pointer; white-space:nowrap; }
+  .ww-fpop label:hover { background:rgba(255,255,255,.05); }
+  .ww-fpop input[type=checkbox] { accent-color:#7ec8a9; }
+  /* Per-field hide rules */
+  html.hf-ww-image    .card img           { display:none !important }
+  html.hf-ww-badge    [data-f="ww-badge"] { display:none !important }
+  html.hf-ww-date     [data-f="ww-date"]  { display:none !important }
+  html.hf-ww-seamless [data-f="ww-seamless"] { display:none !important }
+  html.hf-ww-review   [data-f="ww-review"]{ display:none !important }
+  html.hf-ww-copy     [data-f="ww-copy"]  { display:none !important }
   select, input[type=range] { accent-color: #7ec8a9; background: #1c1c1c;
         color: #eee; border: 1px solid #333; border-radius: 6px; padding: 4px 8px; }
   #board { padding: 0 24px 60px; }
@@ -79,6 +95,10 @@
     </select>
   </label>
   <label>Density <input type="range" id="density" min="1" max="4" value="3"></label>
+  <div class="ww-fieldmenu">
+    <button class="ww-fbtn" id="wwFieldsBtn" aria-haspopup="true" aria-expanded="false">Fields &#9662;</button>
+    <div class="ww-fpop" id="wwFpop" hidden role="menu"></div>
+  </div>
 </div>
 <div id="board"></div>
 <div id="gobar">
@@ -142,12 +162,12 @@ function render() {
         <div class="card ${chosenFile === v.file ? 'chosen' : ''}">
           <img src="/images/${v.file}" loading="lazy" onclick="zoom(this.src)">
           <div class="meta">
-            <span class="badge ${v.engine === 'nano-banana' ? 'nano' : 'local'}">
+            <span class="badge ${v.engine === 'nano-banana' ? 'nano' : 'local'}" data-f="ww-badge">
               ${v.engine === 'nano-banana' ? '🍌 nano banana' : '🖥 local SDXL'}</span>
-            <span class="when" title="${v.created_at}">🕓 ${fmtDate(v.created_at)}</span>
-            ${v.tile ? `<span class="badge" style="background:${['PASS','EDGES-PASS'].includes(v.tile.verdict)?'#14301b;color:#6c6':v.tile.verdict==='WARN'?'#33290f;color:#e0b34d':'#331414;color:#e06c6c'}">seamless: ${['PASS','EDGES-PASS'].includes(v.tile.verdict)?'YES':v.tile.verdict}</span>` : ''}
-            ${v.borderline ? '<span class="badge" style="background:#3a2510;color:#f0a050">⚠ settlement REVIEW</span>' : ''}
-            ${v.copycheck === 'FLAGGED' ? `<span class="badge" style="background:#3a1010;color:#f05050" title="${v.copycheck_what||''}">✋ too close to original</span>` : v.copycheck === 'CLEAN' ? '<span class="badge" style="background:#102a10;color:#5cb85c">✓ original</span>' : ''}
+            <span class="when" data-f="ww-date" title="${v.created_at}">🕓 ${fmtDate(v.created_at)}</span>
+            ${v.tile ? `<span class="badge" data-f="ww-seamless" style="background:${['PASS','EDGES-PASS'].includes(v.tile.verdict)?'#14301b;color:#6c6':v.tile.verdict==='WARN'?'#33290f;color:#e0b34d':'#331414;color:#e06c6c'}">seamless: ${['PASS','EDGES-PASS'].includes(v.tile.verdict)?'YES':v.tile.verdict}</span>` : ''}
+            ${v.borderline ? '<span class="badge" data-f="ww-review" style="background:#3a2510;color:#f0a050">⚠ settlement REVIEW</span>' : ''}
+            ${v.copycheck === 'FLAGGED' ? `<span class="badge" data-f="ww-copy" style="background:#3a1010;color:#f05050" title="${v.copycheck_what||''}">✋ too close to original</span>` : v.copycheck === 'CLEAN' ? '<span class="badge" data-f="ww-copy" style="background:#102a10;color:#5cb85c">✓ original</span>' : ''}
             <button class="choose" onclick="choose('${d.id}','${v.file}')">
               ${chosenFile === v.file ? '✓ CHOSEN — will go live' : 'Choose this version'}</button>
           </div>
@@ -235,6 +255,46 @@ sortSel.onchange = render;
 density.oninput = applyDensity;
 applyDensity();
 load();
+
+// ── Fields menu (wild-wallcoverings) ────────────────────────────────────────
+(function(){
+  var WW_FIELDS = [
+    ['ww-image','Image',true],['ww-badge','Engine Badge',true],
+    ['ww-date','Date',true],['ww-seamless','Seamless',true],
+    ['ww-review','Review Flag',true],['ww-copy','Copy Check',true],
+  ];
+  var wwFields = (function(){
+    var def={}; WW_FIELDS.forEach(function(f){ def[f[0]]=f[2]; });
+    try{ return Object.assign(def, JSON.parse(localStorage.getItem('wildwc.fields')||'{}')); }catch{ return def; }
+  })();
+  function wwApplyFields(){
+    WW_FIELDS.forEach(function(f){ document.documentElement.classList.toggle('hf-'+f[0], !wwFields[f[0]]); });
+  }
+  function wwBuildFieldMenu(){
+    var fpop=document.getElementById('wwFpop'); if(!fpop) return;
+    fpop.innerHTML=WW_FIELDS.map(function(f){
+      return '<label><input type="checkbox" data-field="'+f[0]+'"'+(wwFields[f[0]]?' checked':'')+'>'+f[1]+'</label>';
+    }).join('');
+    fpop.querySelectorAll('input').forEach(function(cb){
+      cb.addEventListener('change',function(){
+        wwFields[cb.dataset.field]=cb.checked;
+        try{ localStorage.setItem('wildwc.fields', JSON.stringify(wwFields)); }catch(ex){}
+        wwApplyFields();
+      });
+    });
+  }
+  var fbtn=document.getElementById('wwFieldsBtn'), fpop=document.getElementById('wwFpop');
+  if(fbtn && fpop){
+    fbtn.addEventListener('click',function(e){
+      e.stopPropagation(); var open=fpop.hidden; fpop.hidden=!open;
+      fbtn.setAttribute('aria-expanded', String(open));
+    });
+    document.addEventListener('click',function(e){
+      if(!fpop.hidden && !e.target.closest('.ww-fieldmenu')){ fpop.hidden=true; fbtn.setAttribute('aria-expanded','false'); }
+    });
+  }
+  wwBuildFieldMenu(); wwApplyFields();
+})();
 </script>
 </body>
 </html>

← 140fbf0 auto-save: 2026-07-23T13:21:35 (3 files) — data/marketing-ap  ·  back to Wild Wallcoverings  ·  creds-safe fetch guard: resolve relative fetch vs credential b522c0b →