[object Object]

← back to All Designerwallcoverings

microsites.html: density slider 1-30 with live readout + sort every field (products/name/slug/type both dirs, catalog/live/gated)

7f71cd113bd1328d8b3f4a8a1225d545af21aadb · 2026-07-06 16:28:25 -0700 · Steve Abrams

Files touched

Diff

commit 7f71cd113bd1328d8b3f4a8a1225d545af21aadb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jul 6 16:28:25 2026 -0700

    microsites.html: density slider 1-30 with live readout + sort every field (products/name/slug/type both dirs, catalog/live/gated)
---
 public/microsites.html | 45 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 34 insertions(+), 11 deletions(-)

diff --git a/public/microsites.html b/public/microsites.html
index 3ee2982..3a083e9 100644
--- a/public/microsites.html
+++ b/public/microsites.html
@@ -99,12 +99,19 @@
     <button id="fAll">All Live Sites</button>
   </div>
   <select id="sort" aria-label="Sort">
-    <option value="products">Most Products</option>
-    <option value="az">Name A→Z</option>
-    <option value="za">Name Z→A</option>
-    <option value="status">Catalog First</option>
+    <option value="products">Products: Most</option>
+    <option value="products_asc">Products: Fewest</option>
+    <option value="az">Name: A→Z</option>
+    <option value="za">Name: Z→A</option>
+    <option value="slug">Slug: A→Z</option>
+    <option value="slug_desc">Slug: Z→A</option>
+    <option value="type">Type: A→Z</option>
+    <option value="type_desc">Type: Z→A</option>
+    <option value="catalog">Has Catalog First</option>
+    <option value="live">Live First</option>
+    <option value="gated">Trade-Gated First</option>
   </select>
-  <label class="dens">Density <input type="range" id="density" min="2" max="8" step="1" value="4"></label>
+  <label class="dens">Density <input type="range" id="density" min="1" max="30" step="1" value="4"><span id="densN">4</span></label>
 </div>
 
 <main class="grid" id="grid"></main>
@@ -125,6 +132,7 @@ const state = {
 
 document.documentElement.style.setProperty('--cols', state.cols);
 density.value = state.cols;
+densN.textContent = state.cols;
 sort.value = state.sort;
 setSeg();
 
@@ -160,15 +168,30 @@ fetch('/api/microsites').then(r=>r.json()).then(d=>{
   apply();
 }).catch(()=>{ fresh.textContent='crawl snapshot not ready — check back shortly'; });
 
+// Every field on a microsite record gets a sorter. Ties break on name so order is stable.
+const nm = s => displayName(s);
+const str = v => String(v == null ? '￿' : v);      // nulls sink to the end on A→Z
+const gated = s => (s.status === 401 ? 1 : 0);
+const SORTERS = {
+  products:     (a,b)=> (b.productCount-a.productCount) || nm(a).localeCompare(nm(b)),
+  products_asc: (a,b)=> (a.productCount-b.productCount) || nm(a).localeCompare(nm(b)),
+  az:           (a,b)=> nm(a).localeCompare(nm(b)),
+  za:           (a,b)=> nm(b).localeCompare(nm(a)),
+  slug:         (a,b)=> str(a.slug).localeCompare(str(b.slug)),
+  slug_desc:    (a,b)=> str(b.slug).localeCompare(str(a.slug)),
+  type:         (a,b)=> str(a.type).localeCompare(str(b.type)) || nm(a).localeCompare(nm(b)),
+  type_desc:    (a,b)=> str(b.type).localeCompare(str(a.type)) || nm(a).localeCompare(nm(b)),
+  catalog:      (a,b)=> (b.feed-a.feed) || (b.productCount-a.productCount) || nm(a).localeCompare(nm(b)),
+  live:         (a,b)=> (gated(a)-gated(b)) || (b.productCount-a.productCount) || nm(a).localeCompare(nm(b)),
+  gated:        (a,b)=> (gated(b)-gated(a)) || nm(a).localeCompare(nm(b)),
+};
+
 function apply(){
   let v = state.all;
   if (state.filter==='feeds') v = v.filter(x=>x.feed);
   if (state.q){ const q=state.q.toLowerCase();
-    v = v.filter(x=> (x.slug||'').includes(q) || displayName(x).toLowerCase().includes(q) || (x.vendor||'').toLowerCase().includes(q)); }
-  if (state.sort==='az') v=[...v].sort((a,b)=>displayName(a).localeCompare(displayName(b)));
-  else if (state.sort==='za') v=[...v].sort((a,b)=>displayName(b).localeCompare(displayName(a)));
-  else if (state.sort==='status') v=[...v].sort((a,b)=>(b.feed-a.feed)||(b.productCount-a.productCount)||displayName(a).localeCompare(displayName(b)));
-  else v=[...v].sort((a,b)=>(b.productCount-a.productCount)||displayName(a).localeCompare(displayName(b)));
+    v = v.filter(x=> (x.slug||'').includes(q) || nm(x).toLowerCase().includes(q) || (x.vendor||'').toLowerCase().includes(q)); }
+  v = [...v].sort(SORTERS[state.sort] || SORTERS.products);
   state.view = v; state.rendered = 0; grid.innerHTML=''; renderMore();
 }
 
@@ -210,7 +233,7 @@ new IntersectionObserver(es=>{ if(es[0].isIntersecting) renderMore(); }, {rootMa
 q.addEventListener('input', e=>{ state.q=e.target.value.trim(); apply(); });
 sort.addEventListener('change', e=>{ state.sort=e.target.value; localStorage.setItem('adwm_sort',state.sort); apply(); });
 density.addEventListener('input', e=>{ state.cols=e.target.value; localStorage.setItem('adwm_cols',state.cols);
-  document.documentElement.style.setProperty('--cols', state.cols); });
+  densN.textContent=state.cols; document.documentElement.style.setProperty('--cols', state.cols); });
 fFeeds.onclick=()=>{ state.filter='feeds'; localStorage.setItem('adwm_filter','feeds'); setSeg(); apply(); };
 fAll.onclick=()=>{ state.filter='all'; localStorage.setItem('adwm_filter','all'); setSeg(); apply(); };
 </script>

← ce4838d untrack data/microsites.json (live crawl artifact) + restore  ·  back to All Designerwallcoverings  ·  api: carry series (Book) facet + website/internal_url per pr 3876a0d →