← back to Commercialrealestate
feat(index): empty-state quick-start chips (DTD verdict D, 2/3 Qwen+Claude; Codex dissent B=a11y, queued next) — the wait-for-selection screen now offers one-click starters (Condos · Multifamily · ◆Non-QM · ⚑Unwarrantable · ✓In budget · ⭐My shortlist when non-empty) that apply a filter instantly, fixing the collapsed-tabs first-action dead-end. Contrarian-gated, 0 errors.
ac724c3ce195e76e7e2b20cc2a10397c4650c8ae · 2026-07-02 13:49:37 -0700 · Steve Abrams
Officer: vp-engineering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
Diff
commit ac724c3ce195e76e7e2b20cc2a10397c4650c8ae
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 2 13:49:37 2026 -0700
feat(index): empty-state quick-start chips (DTD verdict D, 2/3 Qwen+Claude; Codex dissent B=a11y, queued next) — the wait-for-selection screen now offers one-click starters (Condos · Multifamily · ◆Non-QM · ⚑Unwarrantable · ✓In budget · ⭐My shortlist when non-empty) that apply a filter instantly, fixing the collapsed-tabs first-action dead-end. Contrarian-gated, 0 errors.
Officer: vp-engineering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
public/index.html | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/public/index.html b/public/index.html
index a0c3726..e3d3d2e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -198,6 +198,9 @@
.showmore-wrap { grid-column:1/-1; text-align:center; padding:20px 0 6px; }
.showmore { background:var(--card); border:1px solid var(--line); color:var(--blue); border-radius:10px; padding:9px 20px; font-size:13px; cursor:pointer; }
.showmore:hover { border-color:var(--blue); }
+ .quickstart { display:flex; flex-wrap:wrap; gap:8px; justify-content:center; margin:16px 0; }
+ .qchip { background:var(--card); border:1px solid var(--line); color:var(--ink); border-radius:18px; padding:7px 14px; font-size:13px; cursor:pointer; }
+ .qchip:hover { border-color:var(--blue); color:var(--blue); }
/* compare overlay */
#cmpov { display:none; position:fixed; inset:0; background:rgba(0,0,0,.6); z-index:60; align-items:flex-start; justify-content:center; padding:40px 14px; overflow:auto; }
#cmpov.on { display:flex; }
@@ -1099,6 +1102,15 @@ function paintPage(){
else { g.className='grid'; g.innerHTML=slice.map(card).join('')+moreBtn(); enrichAssessor(); }
applyHeat(slice);
}
+function applyQuick(k){
+ if(k==='condo'){ F.types=new Set(['Condo']); }
+ else if(k==='mf'){ F.types=new Set(['Multifamily']); }
+ else if(k==='nonqm'){ F.nonqm=true; }
+ else if(k==='unw'){ F.unwarrantable=true; F.types=new Set(['Condo']); }
+ else if(k==='afford'){ F.afford=true; if($('#fAfford')) $('#fAfford').checked=true; }
+ else if(k==='short'){ F.shortlist=true; }
+ syncChips(); render();
+}
function median(a){ if(!a.length) return null; const s=a.slice().sort((x,y)=>x-y); const m=Math.floor(s.length/2); return s.length%2?s[m]:(s[m-1]+s[m])/2; }
function renderStats(r){
const sb=$('#statsbar'); if(!sb) return;
@@ -1126,7 +1138,16 @@ function render(){
// Wait-for-selection: on a clean load (no filter chosen) don't dump 2,134 cards — prompt for a pick.
if(fc===0 && !F.cityQuery){
g.className='grid';
- g.innerHTML=`<div class="empty">👈 Expand a filter tab on the left to begin.<br><span class="small">${(DATA.ranked.length).toLocaleString()} listings loaded · ${DATA.meta.market}. Open <b>Asset type</b> (Condo · ◆ Non-QM · ⚑ Unwarrantable), <b>City</b>, or <b>Price</b> — results appear instantly, and your active filters show as removable chips up top.</span></div>`;
+ g.innerHTML=`<div class="empty">👈 Expand a filter tab on the left — or start with one of these:
+ <div class="quickstart">
+ <button class="qchip" data-quick="condo">🏢 Condos</button>
+ <button class="qchip" data-quick="mf">🏠 Multifamily</button>
+ <button class="qchip" data-quick="nonqm">◆ Non-QM</button>
+ <button class="qchip" data-quick="unw">⚑ Unwarrantable</button>
+ <button class="qchip" data-quick="afford">✓ In budget</button>
+ ${SHORT.size?`<button class="qchip" data-quick="short">⭐ My shortlist (${SHORT.size})</button>`:''}
+ </div>
+ <span class="small">${(DATA.ranked.length).toLocaleString()} listings loaded · ${DATA.meta.market} — results appear instantly; your active filters show as removable chips up top.</span></div>`;
$('#count').innerHTML=`<b>${(DATA.ranked.length).toLocaleString()}</b> loaded · choose a filter to view`;
$('#statsbar').innerHTML='';
saveState(); return;
@@ -1381,7 +1402,7 @@ fetch('data/ranked.json').then(r=>r.json()).then(async d=>{
$('#examples').innerHTML = EX.map(x=>`<button class="ex">${x}</button>`).join('');
$('#examples').onclick=e=>{ if(e.target.classList.contains('ex')){ $('#q').value=e.target.textContent; $('#q').focus(); } };
$('#chatform').onsubmit=e=>{ e.preventDefault(); const t=$('#q').value.trim(); if(!t) return; $('#q').value=''; ask(t); };
- $('#grid').onclick=e=>{ const more=e.target.closest('#showmore'); if(more){ _shown=Math.min(_shown+PAGE(), _rows.length); paintPage(); return; } const st=e.target.closest('[data-star]'); if(st){ toggleStar(st.dataset.star); st.classList.toggle('on',SHORT.has(st.dataset.star)); const sb=$('#shortbtn'); if(sb) sb.textContent='⭐ Shortlist ('+SHORT.size+')'; if(F.shortlist) render(); return; } const th=e.target.closest('.listtbl th[data-sortk]'); if(th){ const k=th.dataset.sortk; if(colSort&&colSort.key===k) colSort.dir*=-1; else colSort={key:k,dir:['type','firm','warr','broker','status','zip','addr','rec'].includes(k)?1:-1}; render(); return; } const b=e.target.closest('.lcbtn'); if(b){ fetchLiveComps(b.dataset.id); return; } const h=e.target.closest('.histbtn'); if(h) fetchHistory(h); };
+ $('#grid').onclick=e=>{ const qk=e.target.closest('[data-quick]'); if(qk){ applyQuick(qk.dataset.quick); return; } const more=e.target.closest('#showmore'); if(more){ _shown=Math.min(_shown+PAGE(), _rows.length); paintPage(); return; } const st=e.target.closest('[data-star]'); if(st){ toggleStar(st.dataset.star); st.classList.toggle('on',SHORT.has(st.dataset.star)); const sb=$('#shortbtn'); if(sb) sb.textContent='⭐ Shortlist ('+SHORT.size+')'; if(F.shortlist) render(); return; } const th=e.target.closest('.listtbl th[data-sortk]'); if(th){ const k=th.dataset.sortk; if(colSort&&colSort.key===k) colSort.dir*=-1; else colSort={key:k,dir:['type','firm','warr','broker','status','zip','addr','rec'].includes(k)?1:-1}; render(); return; } const b=e.target.closest('.lcbtn'); if(b){ fetchLiveComps(b.dataset.id); return; } const h=e.target.closest('.histbtn'); if(h) fetchHistory(h); };
$('#shortbtn').onclick=()=>{ F.shortlist=!F.shortlist; render(); };
$('#cmpbtn').onclick=openCompare;
$('#heat').value=heat; $('#heat').onchange=()=>{ heat=$('#heat').value; localStorage.setItem('cre_heat',heat); render(); };
← 793d08b perf(index): paginated DOM rendering (DTD verdict D, UNANIMO
·
back to Commercialrealestate
·
a11y(index): accessibility pass (DTD verdict A, 2/3 Codex+Cl 589fdb5 →