← back to Patterndesignlab
Fix contrarian findings: working detail-page search, sort+density on designer grid, density on admin grid
dbfae5145c3845958fba586f1f9e40afbe172bb3 · 2026-07-04 11:38:01 -0700 · Steve
Files touched
M public/admin.htmlM public/design.htmlM public/designer.htmlM public/index.htmlM public/styles.css
Diff
commit dbfae5145c3845958fba586f1f9e40afbe172bb3
Author: Steve <steve@designerwallcoverings.com>
Date: Sat Jul 4 11:38:01 2026 -0700
Fix contrarian findings: working detail-page search, sort+density on designer grid, density on admin grid
---
public/admin.html | 6 +++++-
public/design.html | 5 ++++-
public/designer.html | 54 ++++++++++++++++++++++++++++++++++++++++++----------
public/index.html | 3 +++
public/styles.css | 2 +-
5 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/public/admin.html b/public/admin.html
index 1cc66e3..2a98506 100644
--- a/public/admin.html
+++ b/public/admin.html
@@ -27,6 +27,9 @@
<option value="newest">Newest</option><option value="oldest">Oldest</option>
<option value="title">Title A→Z</option><option value="designer">Designer</option>
</select></div>
+ <div style="margin-left:14px"><label for="density" style="font-size:12px;color:#6b6b6b">Density</label>
+ <input id="density" type="range" min="160" max="360" step="20" value="240"
+ oninput="document.documentElement.style.setProperty('--admin-min',this.value+'px');localStorage.setItem('pdl.admdensity',this.value)"></div>
</div>
<div class="admgrid" id="grid"></div>
</div>
@@ -74,7 +77,8 @@ async function loadApps(){
}).join('');
}
-async function load(){ const j=await (await fetch('/api/admin/designs')).json(); rows=j.designs; document.getElementById('count').textContent=j.count+' designs'; render(); }
+async function load(){ const j=await (await fetch('/api/admin/designs')).json(); rows=j.designs; document.getElementById('count').textContent=j.count+' designs'; restoreDensity(); render(); }
+function restoreDensity(){ const v=localStorage.getItem('pdl.admdensity'); if(v){ document.documentElement.style.setProperty('--admin-min',v+'px'); const el=document.getElementById('density'); if(el) el.value=v; } }
function render(){
const s=document.getElementById('sort').value;
const r=[...rows];
diff --git a/public/design.html b/public/design.html
index bd9eef5..103cd07 100644
--- a/public/design.html
+++ b/public/design.html
@@ -9,7 +9,10 @@
<body>
<header class="top"><div class="wrap">
<a class="brand" href="/">Pattern Design<span class="dot">.</span>Lab</a>
- <form class="searchbox" onsubmit="location.href='/?';return false"><input type="search" placeholder="Search…" onkeydown="if(event.key==='Enter'){location.href='/'}"></form>
+ <form class="searchbox" onsubmit="event.preventDefault();var v=this.querySelector('input').value.trim();location.href='/'+(v?('?q='+encodeURIComponent(v)):'');return false;">
+ <input type="search" placeholder="Search patterns, styles, colorways…" autocomplete="off">
+ <button class="go" type="submit" aria-label="search">→</button>
+ </form>
<nav class="navlinks"><a href="/">Explore</a><a href="/trends">Trends</a><a href="/designer-signup">Sell your designs</a></nav>
</div></header>
diff --git a/public/designer.html b/public/designer.html
index f36e849..b2d99c4 100644
--- a/public/designer.html
+++ b/public/designer.html
@@ -17,10 +17,39 @@
const slug = decodeURIComponent(location.pathname.split('/').pop());
function esc(s){ return (s==null?'':String(s)).replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c])); }
function fmtPrice(lo){ return lo==null?'Inquire':'$'+lo; }
+let DESIGNS = [];
+
+// same sort vocabulary as the main grid, so every product grid behaves identically (DW standing rule)
+function sortDesigns(list, mode){
+ const a = list.slice();
+ const cmp = {
+ newest: (x,y)=> new Date(y.created_at||0) - new Date(x.created_at||0),
+ color: (x,y)=> String(x.dominant_hex||'').localeCompare(String(y.dominant_hex||'')),
+ style: (x,y)=> String(x.style||'').localeCompare(String(y.style||'')),
+ title_az:(x,y)=> String(x.title||'').localeCompare(String(y.title||'')),
+ price_up:(x,y)=> (x.price_min??1e9) - (y.price_min??1e9),
+ price_dn:(x,y)=> (y.price_min??-1) - (x.price_min??-1),
+ }[mode] || (()=>0);
+ return a.sort(cmp);
+}
+function renderGrid(){
+ const mode = document.getElementById('sort').value;
+ localStorage.setItem('pdl.sort', mode);
+ const rows = sortDesigns(DESIGNS, mode);
+ document.getElementById('grid').innerHTML = rows.map(x=>`
+ <a class="card" href="/design/${x.slug}">
+ <div class="thumb"><div class="tile" style="background-image:url('${x.img}')"></div>
+ ${x.seamless?'<span class="badge">Seamless</span>':'<span class="badge">Mural</span>'}</div>
+ <div class="cardbody"><h3>${esc(x.title)}</h3>
+ <div class="meta"><span class="swatch" style="background:${x.dominant_hex||'#ccc'}"></span><span>${esc(x.style||'')}</span><span>·</span><span>${esc(x.colorway||'')}</span></div>
+ <div class="price">${fmtPrice(x.price_min)}</div></div>
+ </a>`).join('');
+}
(async()=>{
const r = await fetch('/api/designers/'+encodeURIComponent(slug));
if(!r.ok){ document.getElementById('root').innerHTML='<p style="padding:40px 0">Designer not found. <a href="/">Explore</a></p>'; return; }
const {designer:d, designs, count} = await r.json();
+ DESIGNS = designs || [];
document.title = d.name + ' · Pattern Design Lab';
document.getElementById('root').innerHTML = `
<div class="dhero">
@@ -30,18 +59,23 @@ function fmtPrice(lo){ return lo==null?'Inquire':'$'+lo; }
<p style="font-size:13px;color:#6b6b6b;margin-top:6px">${esc(d.country||'')} · ${count} design${count===1?'':'s'}</p></div>
</div>
<div class="bar"><span class="count">${count} designs</span>
- <label for="density">Density</label>
- <input id="density" type="range" min="2" max="6" step="1" value="4" oninput="document.documentElement.style.setProperty('--cols',this.value);localStorage.setItem('pdl.density',this.value)"></div>
- <div class="grid">${designs.map(x=>`
- <a class="card" href="/design/${x.slug}">
- <div class="thumb"><div class="tile" style="background-image:url('${x.img}')"></div>
- ${x.seamless?'<span class="badge">Seamless</span>':'<span class="badge">Mural</span>'}</div>
- <div class="cardbody"><h3>${esc(x.title)}</h3>
- <div class="meta"><span class="swatch" style="background:${x.dominant_hex||'#ccc'}"></span><span>${esc(x.style||'')}</span><span>·</span><span>${esc(x.colorway||'')}</span></div>
- <div class="price">${fmtPrice(x.price_min)}</div></div>
- </a>`).join('')}</div>
+ <div><label for="sort">Sort</label>
+ <select id="sort" onchange="renderGrid()">
+ <option value="newest">Newest</option>
+ <option value="color">Color</option>
+ <option value="style">Style</option>
+ <option value="title_az">Title A→Z</option>
+ <option value="price_up">Price ↑</option>
+ <option value="price_dn">Price ↓</option>
+ </select></div>
+ <div><label for="density">Density</label>
+ <input id="density" type="range" min="2" max="6" step="1" value="4" oninput="document.documentElement.style.setProperty('--cols',this.value);localStorage.setItem('pdl.density',this.value)"></div></div>
+ <div class="grid" id="grid"></div>
<div style="padding:24px 0"><a href="/" style="color:var(--accent-ink)">← Back to all patterns</a></div>`;
+ // restore persisted controls (DW spec)
+ const ss=localStorage.getItem('pdl.sort'); if(ss){const el=document.getElementById('sort'); if(el&&[...el.options].some(o=>o.value===ss)) el.value=ss;}
const dd=localStorage.getItem('pdl.density'); if(dd){document.documentElement.style.setProperty('--cols',dd);const el=document.getElementById('density');if(el)el.value=dd;}
+ renderGrid();
})();
</script>
</body>
diff --git a/public/index.html b/public/index.html
index 5fbd7cd..37d6418 100644
--- a/public/index.html
+++ b/public/index.html
@@ -122,6 +122,9 @@ function renderFacets(){
function toggleFacet(key,val){ state[key] = state[key]===val ? '' : val; load(); }
function setSeamless(v){ state.seamless=v; load(); }
+// honor an inbound ?q= search (e.g. from the detail-page search bar)
+const urlq = new URLSearchParams(location.search).get('q');
+if(urlq){ state.q = urlq; const qi = document.getElementById('q'); if(qi) qi.value = urlq; }
loadControls(); loadFacets(); load();
</script>
</body>
diff --git a/public/styles.css b/public/styles.css
index 73ba750..a012ada 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -108,7 +108,7 @@ header.top{position:sticky;top:0;z-index:20;background:rgba(250,249,247,.86);bac
/* admin */
.adminbar{display:flex;align-items:center;gap:12px;padding:20px 0}
.when{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;color:var(--muted);background:var(--chip);border-radius:999px;padding:3px 9px}
-.admgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;padding-bottom:60px}
+.admgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--admin-min,240px),1fr));gap:14px;padding-bottom:60px}
.acard{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden;box-shadow:var(--shadow)}
.acard .t{height:150px;background-size:cover;background-position:center}
.acard .b{padding:10px 12px 12px}
← 35e033e iter-2: trend boards, designer onboarding funnel, correct 1:
·
back to Patterndesignlab
·
iter-3: deepen credible catalog to 243, harden settlement sc 76df8eb →