← back to Commercialrealestate
perf(mls): paginated table (DTD verdict B, UNANIMOUS 3/3 Codex+Qwen+Claude) — 200 rows / 60 grid cards per page with 'Show N more'; count + stats strip computed over the FULL filtered set, only the DOM windowed. Cuts a 1823-row MLS render to 200. Pass-20 milestone: all 4 surfaces smoke-clean (0 console errors, no regressions from 19 passes). Contrarian-gated.
8ceee4774e3289881cddf078fb3e75365d2a95b0 · 2026-07-02 17:12:48 -0700 · Steve Abrams
Officer: vp-engineering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
Diff
commit 8ceee4774e3289881cddf078fb3e75365d2a95b0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 2 17:12:48 2026 -0700
perf(mls): paginated table (DTD verdict B, UNANIMOUS 3/3 Codex+Qwen+Claude) — 200 rows / 60 grid cards per page with 'Show N more'; count + stats strip computed over the FULL filtered set, only the DOM windowed. Cuts a 1823-row MLS render to 200. Pass-20 milestone: all 4 surfaces smoke-clean (0 console errors, no regressions from 19 passes). Contrarian-gated.
Officer: vp-engineering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
public/mls.html | 46 ++++++++++++++++++++++++++++------------------
1 file changed, 28 insertions(+), 18 deletions(-)
diff --git a/public/mls.html b/public/mls.html
index 48a0ebd..3223c2a 100644
--- a/public/mls.html
+++ b/public/mls.html
@@ -114,6 +114,7 @@
<div class="statsbar" id="statsbar"></div>
<div id="tableView" class="tblwrap"><table class="mls"><thead id="thead"></thead><tbody id="tbody"></tbody></table></div>
<div id="gridView" class="grid hide"></div>
+ <div id="moreWrap" style="text-align:center;padding:16px 0"></div>
</div>
</div>
<div id="ov"><div id="modal"><button class="ph-x" aria-label="Close" onclick="document.getElementById('ov').classList.remove('on')">✕</button><div id="mbody"></div></div></div>
@@ -150,6 +151,28 @@ const SHORT=new Set((function(){ try{ return JSON.parse(localStorage.getItem('cr
function saveShort(){ try{ localStorage.setItem('cre_shortlist', JSON.stringify([...SHORT])); }catch(e){} }
function toggleStar(id){ SHORT.has(id)?SHORT.delete(id):SHORT.add(id); saveShort(); }
function updateShortBtn(){ const sb=$('#shortbtn'); if(sb){ sb.textContent='⭐ Shortlist ('+SHORT.size+')'; sb.classList.toggle('shorton',F.shortlist); } }
+// pagination (perf, parity with the Explorer)
+let _rows=[], _cols=[], _shown=0;
+const PAGE=()=> view==='table'?200:60;
+const star=r=>`<button class="starbtn ${SHORT.has(r.id)?'on':''}" data-star="${r.id}" aria-label="${SHORT.has(r.id)?'Remove from':'Add to'} shortlist" aria-pressed="${SHORT.has(r.id)}" title="Shortlist">★</button><button class="copybtn" data-copy="${r.id}" aria-label="Copy summary" title="Copy summary">📋</button>`;
+function paintBody(){
+ const rows=_rows.slice(0,_shown), cols=_cols;
+ if(view==='table'){
+ $('#tableView').classList.remove('hide'); $('#gridView').classList.add('hide');
+ $('#tbody').innerHTML=rows.map(r=>`<tr class="prow" data-addr="${esc(r.address||'')}" data-city="${esc(r.city||'')}" style="cursor:pointer"><td class="startd">${star(r)}</td>`+cols.map(c=>{
+ const cls=(c.t==='money'||c.t==='n'||c.t==='pct')?' class="n"':''; return `<td${cls}>${cellHTML(r,c)}</td>`;
+ }).join('')+'</tr>').join('');
+ } else {
+ $('#tableView').classList.add('hide'); $('#gridView').classList.remove('hide');
+ const bodyCols=cols.filter(c=>!['address','city','zip','type'].includes(c.k));
+ $('#gridView').innerHTML=rows.map(r=>`<div class="gc prow" data-addr="${esc(r.address||'')}" data-city="${esc(r.city||'')}" style="cursor:pointer">
+ <div style="display:flex;justify-content:space-between;gap:8px"><div class="addr">${esc(r.address||'—')}</div>${star(r)}</div><div class="sub">${esc(r.city||'')} ${esc(r.zip||'')} · ${esc(r.type||'')}</div>`
+ +bodyCols.map(c=>`<div class="row"><span class="k">${esc(c.l)}</span><span class="v">${cellHTML(r,c)}</span></div>`).join('')
+ +`</div>`).join('');
+ }
+ const mw=$('#moreWrap'); if(mw) mw.innerHTML=_shown<_rows.length?`<button class="csvbtn" id="showmore">Show ${Math.min(PAGE(),_rows.length-_shown)} more · showing ${_shown} of ${_rows.length}</button>`:'';
+ applyHeat(rows);
+}
// heat cues + copy summary (parity with the Explorer)
let heat=localStorage.getItem('cre_heat_mls')||'none';
function heatVal(r){ return heat==='cap'?(r.cap_rate!=null&&r.cap_rate!==''?+r.cap_rate:null):heat==='score'?(r.composite!=null?+r.composite:null):null; }
@@ -252,7 +275,7 @@ function render(){
updateShortBtn();
if(!anySelection()){
$('#count').textContent=`${DATA.length} loaded · pick a filter or search to view`;
- $('#statsbar').innerHTML='';
+ $('#statsbar').innerHTML=''; const _mw=$('#moreWrap'); if(_mw) _mw.innerHTML='';
$('#tableView').classList.add('hide'); $('#gridView').classList.remove('hide');
$('#gridView').className='grid'; $('#gridView').innerHTML=`<div class="mlsempty">👈 Expand a filter tab or search to begin.<br><span style="font-size:12px">${DATA.length} listings + condos loaded. Pick <b>Asset class</b> (◆ Non-QM · ⚑ Unwarrantable · Condo), a city, status, a price range, or ⭐ Shortlist.</span></div>`;
return;
@@ -260,24 +283,10 @@ function render(){
const rows=filtered(); const cols=visCols();
renderStats(rows);
$('#count').textContent=`${rows.length} of ${DATA.length} properties`;
- const star=r=>`<button class="starbtn ${SHORT.has(r.id)?'on':''}" data-star="${r.id}" aria-label="${SHORT.has(r.id)?'Remove from':'Add to'} shortlist" aria-pressed="${SHORT.has(r.id)}" title="Shortlist">★</button><button class="copybtn" data-copy="${r.id}" aria-label="Copy summary" title="Copy summary">📋</button>`;
- // table head
+ // table head (full column set; independent of pagination)
$('#thead').innerHTML='<tr><th class="starth">⭐</th>'+cols.map(c=>`<th data-k="${c.k}" class="${sortKey===c.k?(sortDir>0?'asc':'desc'):''}">${esc(c.l)}</th>`).join('')+'</tr>';
- if(view==='table'){
- $('#tableView').classList.remove('hide'); $('#gridView').classList.add('hide');
- $('#tbody').innerHTML=rows.map(r=>`<tr class="prow" data-addr="${esc(r.address||'')}" data-city="${esc(r.city||'')}" style="cursor:pointer"><td class="startd">${star(r)}</td>`+cols.map(c=>{
- const cls=(c.t==='money'||c.t==='n'||c.t==='pct')?' class="n"':'';
- return `<td${cls}>${cellHTML(r,c)}</td>`;
- }).join('')+'</tr>').join('');
- } else {
- $('#tableView').classList.add('hide'); $('#gridView').classList.remove('hide');
- const bodyCols=cols.filter(c=>!['address','city','zip','type'].includes(c.k));
- $('#gridView').innerHTML=rows.map(r=>`<div class="gc prow" data-addr="${esc(r.address||'')}" data-city="${esc(r.city||'')}" style="cursor:pointer">
- <div style="display:flex;justify-content:space-between;gap:8px"><div class="addr">${esc(r.address||'—')}</div>${star(r)}</div><div class="sub">${esc(r.city||'')} ${esc(r.zip||'')} · ${esc(r.type||'')}</div>`
- +bodyCols.map(c=>`<div class="row"><span class="k">${esc(c.l)}</span><span class="v">${cellHTML(r,c)}</span></div>`).join('')
- +`</div>`).join('');
- }
- applyHeat(rows);
+ _rows=rows; _cols=cols; _shown=Math.min(PAGE(), rows.length); // paginate the DOM; count+stats use the full set
+ paintBody();
}
$('#thead').addEventListener('click',e=>{ const th=e.target.closest('th'); if(!th) return; const k=th.dataset.k; if(!k) return;
if(sortKey===k) sortDir*=-1; else { sortKey=k; sortDir=(k==='address'||k==='city'||k==='firm'||k==='type')?1:-1; } render(); });
@@ -307,6 +316,7 @@ function exportCSV(){ const rows=filtered(); const cols=visCols(); const cv=(r,c
const a=document.createElement('a'); a.href=URL.createObjectURL(new Blob([csv],{type:'text/csv'})); a.download='cre-mls-'+rows.length+'.csv'; a.click(); URL.revokeObjectURL(a.href); }
$('#csv').addEventListener('click',exportCSV);
$('#shortbtn').addEventListener('click',()=>{ F.shortlist=!F.shortlist; render(); });
+$('#moreWrap').addEventListener('click',e=>{ if(e.target.closest('#showmore')){ _shown=Math.min(_shown+PAGE(), _rows.length); paintBody(); } });
$('#heat').value=heat; $('#heat').addEventListener('change',()=>{ heat=$('#heat').value; localStorage.setItem('cre_heat_mls',heat); render(); });
$('#view').addEventListener('click',e=>{ const b=e.target.closest('button'); if(!b) return;
document.querySelectorAll('#view button').forEach(x=>x.classList.toggle('on',x===b)); view=b.dataset.v; render(); });
← 9b10d00 feat(mls): heat cues + copy summary parity (DTD verdict D, U
·
back to Commercialrealestate
·
auto-save: 2026-07-02T17:51:29 (1 files) — public/broker-gri 253a437 →