[object Object]

← 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 →