[object Object]

← back to Crezana Internal

Add sort control to crezana internal viewer (Steve hard rule)

7a000b764f1ffdb3c99db2342daedd97e4e350c4 · 2026-07-09 09:52:42 -0700 · Steve

Grid had density but no sort <select>. Added Newest/Title/Color/Style sort
wired to render, persisted to localStorage (crz_sort), restored on load —
matches the fromental/gracie pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 7a000b764f1ffdb3c99db2342daedd97e4e350c4
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Jul 9 09:52:42 2026 -0700

    Add sort control to crezana internal viewer (Steve hard rule)
    
    Grid had density but no sort <select>. Added Newest/Title/Color/Style sort
    wired to render, persisted to localStorage (crz_sort), restored on load —
    matches the fromental/gracie pattern.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/index.html | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/public/index.html b/public/index.html
index a881e3f..684e1cd 100644
--- a/public/index.html
+++ b/public/index.html
@@ -47,6 +47,14 @@
   <aside>
     <input type="search" id="q" placeholder="Search pattern / sku / collection…">
     <div class="controls">
+      <label>Sort
+        <select id="sort">
+          <option value="newest">Newest</option>
+          <option value="title">Title A→Z</option>
+          <option value="color">Color</option>
+          <option value="style">Style</option>
+        </select>
+      </label>
       <label>Density <input type="range" id="density" min="120" max="340" value="190"></label>
       <label>Text size <input type="range" id="tscale" min="80" max="150" value="100"></label>
       <label><input type="checkbox" id="imgonly"> Image-only wall</label>
@@ -61,7 +69,18 @@
 <script>
 const API = location.origin;
 let ALL=[], activeFacets={};
+let SORT = localStorage.getItem('crz_sort') || 'newest';
 const fmtDate = d => d ? new Date(d).toLocaleString(undefined,{year:'numeric',month:'short',day:'numeric',hour:'numeric',minute:'2-digit'}) : '';
+function hue(hex){ if(!hex) return 999; const m=/^#?([0-9a-f]{6})$/i.exec(hex); if(!m) return 999; const n=parseInt(m[1],16), r=(n>>16&255)/255, g=(n>>8&255)/255, b=(n&255)/255; const mx=Math.max(r,g,b), mn=Math.min(r,g,b), d=mx-mn; if(!d) return -1; let h; if(mx===r) h=((g-b)/d)%6; else if(mx===g) h=(b-r)/d+2; else h=(r-g)/d+4; return (h*60+360)%360; }
+function sortRows(rows){
+  const by={
+    newest:(a,b)=> new Date(b.crawled_at||0)-new Date(a.crawled_at||0),
+    title:(a,b)=> (a.pattern_name||a.mfr_sku||'').localeCompare(b.pattern_name||b.mfr_sku||''),
+    color:(a,b)=> hue(a.dominant_color_hex||a.color_hex)-hue(b.dominant_color_hex||b.color_hex),
+    style:(a,b)=> ((a.ai_styles&&a.ai_styles[0])||'zz').localeCompare((b.ai_styles&&b.ai_styles[0])||'zz'),
+  };
+  return rows.slice().sort(by[SORT]||by.newest);
+}
 
 async function load(){
   const [p,f] = await Promise.all([
@@ -100,7 +119,7 @@ function matches(p){
   return true;
 }
 function render(){
-  const grid=document.getElementById('grid'); const rows=ALL.filter(matches);
+  const grid=document.getElementById('grid'); const rows=sortRows(ALL.filter(matches));
   document.getElementById('empty').style.display=rows.length?'none':'block';
   grid.innerHTML = rows.map(p=>{
     const hex=p.dominant_color_hex||p.color_hex;
@@ -116,6 +135,8 @@ function render(){
   }).join('');
 }
 document.getElementById('q').oninput=render;
+const sortEl=document.getElementById('sort'); sortEl.value=SORT;
+sortEl.onchange=e=>{ SORT=e.target.value; localStorage.setItem('crz_sort',SORT); render(); };
 document.getElementById('density').oninput=e=>document.documentElement.style.setProperty('--card',e.target.value+'px');
 document.getElementById('tscale').oninput=e=>document.documentElement.style.setProperty('--scale',(e.target.value/100));
 document.getElementById('imgonly').onchange=e=>document.body.classList.toggle('imgonly',e.target.checked);

← fd39508 initial scaffold: crezana-internal (Crezana onboard)  ·  back to Crezana Internal  ·  jsonl-backed data layer: self-contained bundle for Kamatera d05fb36 →