[object Object]

← back to Commercialrealestate

Adopt column-manager.js in gov-agents + sales (TK-10120 sweep)

a01bf5ea00acb0cbb4fbbdd480ee3a4c81b50732 · 2026-08-01 22:25:21 -0700 · Steve Abrams

The 2 CRCP grid pages that share mls's exact column-state pattern get the shared
column-manager panel (grouped by their real c.g + in-panel search + drag-reorder),
wired as a view over each page's own VISCOL/COLORDER. Same 4-edit swap as mls:
replace buildFieldToggles, remove the now-duplicate #fFields change handler,
refresh the panel after a thead reorder, load the script. Graceful fallback to the
old grouped toggle list if the module hasn't loaded.

Verified live (:9943, real-mouse) on BOTH: panel mounts with real groups
(gov-agents Identity/License/Location; sales Property/Sale/Unit/Details), toggle
re-renders the grid, sort works (confirmed on a varied column), resize works,
0 console errors.

Survey finding: of the ~14 surveyed CRCP pages, only these 2 + mls share the
retrofittable pattern — the rest use different column systems (lending-grid.js
engine, COLORDER-without-VISCOL, or no column manager), so the '18-page sweep' was
a false premise; a blind sweep would have broken them.

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

Files touched

Diff

commit a01bf5ea00acb0cbb4fbbdd480ee3a4c81b50732
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 1 22:25:21 2026 -0700

    Adopt column-manager.js in gov-agents + sales (TK-10120 sweep)
    
    The 2 CRCP grid pages that share mls's exact column-state pattern get the shared
    column-manager panel (grouped by their real c.g + in-panel search + drag-reorder),
    wired as a view over each page's own VISCOL/COLORDER. Same 4-edit swap as mls:
    replace buildFieldToggles, remove the now-duplicate #fFields change handler,
    refresh the panel after a thead reorder, load the script. Graceful fallback to the
    old grouped toggle list if the module hasn't loaded.
    
    Verified live (:9943, real-mouse) on BOTH: panel mounts with real groups
    (gov-agents Identity/License/Location; sales Property/Sale/Unit/Details), toggle
    re-renders the grid, sort works (confirmed on a varied column), resize works,
    0 console errors.
    
    Survey finding: of the ~14 surveyed CRCP pages, only these 2 + mls share the
    retrofittable pattern — the rest use different column systems (lending-grid.js
    engine, COLORDER-without-VISCOL, or no column manager), so the '18-page sweep' was
    a false premise; a blind sweep would have broken them.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/gov-agents.html | 27 ++++++++++++++++++++-------
 public/sales.html      | 27 ++++++++++++++++++++-------
 2 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/public/gov-agents.html b/public/gov-agents.html
index 4ecfaf3..83fb79b 100644
--- a/public/gov-agents.html
+++ b/public/gov-agents.html
@@ -144,12 +144,24 @@ function cell(a,c){
   if(k==='name') return '<b>'+esc(a[k])+'</b>';
   const v=a[k]; return v==null||v===''?'<span class="miss">—</span>':esc(v);
 }
+// Column show/hide + in-panel search & drag-reorder via the shared column-manager.js
+// drop-in (TK-10120), as a VIEW over this page's own VISCOL/COLORDER state.
+let __cm=null;
 function buildFieldToggles(){
-  const groups={};
-  COLS.forEach(c=>{ (groups[c.g||'Other']=groups[c.g||'Other']||[]).push(c); });
-  $('#fFields').innerHTML=Object.entries(groups).map(([g,cs])=>
-    `<div class="fgrp">${esc(g)}</div>`+cs.map(c=>`<label class="ftog"><input type="checkbox" data-ck="${c.k}" ${colVis(c.k)?'checked':''}><span>${esc(c.l)}</span></label>`).join('')
-  ).join('');
+  if(!window.ColumnManager){ // fallback if the module hasn't loaded (keeps page usable)
+    const groups={};
+    COLS.forEach(c=>{ (groups[c.g||'Other']=groups[c.g||'Other']||[]).push(c); });
+    $('#fFields').innerHTML=Object.entries(groups).map(([g,cs])=>
+      `<div class="fgrp">${esc(g)}</div>`+cs.map(c=>`<label class="ftog"><input type="checkbox" data-ck="${c.k}" ${colVis(c.k)?'checked':''}><span>${esc(c.l)}</span></label>`).join('')
+    ).join(''); return; }
+  const cols=COLS.map(c=>({k:c.k,l:c.l,g:c.g||'Other'}));
+  if(!__cm){
+    __cm=ColumnManager.init({ mount:'#fFields', footer:false, hint:false, cols,
+      state:{ visible:k=>colVis(k), setVisible:(k,v)=>{VISCOL[k]=v;saveCols();},
+              order:()=>{syncColOrder();return COLORDER.slice();}, setOrder:a=>{COLORDER=a;saveColOrder();},
+              reset:()=>{VISCOL={};saveCols();COLORDER=[];saveColOrder();} },
+      onChange:()=>render() });
+  } else { __cm.setCols(cols); }
 }
 function filtered(){
   let rows=AGENTS.slice();
@@ -240,13 +252,13 @@ $('#thead').addEventListener('drop',e=>{ if(!dragKey) return; const th=e.target.
     const r=th.getBoundingClientRect(), after=(e.clientX-r.left)>r.width/2;
     syncColOrder(); const from=COLORDER.indexOf(dragKey); if(from>=0){
       COLORDER.splice(from,1); let to=COLORDER.indexOf(tgt); if(after) to+=1; COLORDER.splice(to,0,dragKey);
-      saveColOrder(); lastColSig=''; render();
+      saveColOrder(); lastColSig=''; render(); if(__cm)__cm.refresh();
     }
   }
   dragKey=null; });
 $('#thead').addEventListener('dragend',()=>{ document.querySelectorAll('#thead th.dragging,#thead th.dropL,#thead th.dropR').forEach(x=>x.classList.remove('dragging','dropL','dropR')); dragKey=null; });
 // ── left-rail column show/hide toggles ──
-$('#fFields').addEventListener('change',e=>{const cb=e.target.closest('input[data-ck]');if(!cb)return;VISCOL[cb.dataset.ck]=cb.checked;saveCols();render();});
+// (#fFields change is handled inside column-manager.js now — its onChange calls render)
 $('#fAll').addEventListener('click',()=>{COLS.forEach(c=>VISCOL[c.k]=true);saveCols();buildFieldToggles();render();});
 $('#fNone').addEventListener('click',()=>{COLS.forEach(c=>VISCOL[c.k]=false);saveCols();buildFieldToggles();render();});
 // Reset the whole table layout back to the tight default: visibility, order, widths.
@@ -259,6 +271,7 @@ if(railEl){ railEl.addEventListener('click',e=>{ const h=e.target.closest('h4');
 buildSortSel(); buildFieldToggles();
 loadIndex();
 </script>
+  <script src="/column-manager.js" defer></script>
   <script src="/col-resize.js" defer></script>
 </body>
 </html>
diff --git a/public/sales.html b/public/sales.html
index 5cf91dd..c46a529 100644
--- a/public/sales.html
+++ b/public/sales.html
@@ -193,12 +193,24 @@ function buildRail(){
   $('#fBeds').innerHTML=Object.entries(bc).sort((a,b)=>b[1]-a[1]).map(([k,c])=>`<span class="chip" data-bd="${esc(k)}">${esc(k)}<span class="ct">${c}</span></span>`).join('');
   buildFieldToggles();
 }
+// Column show/hide + in-panel search & drag-reorder via the shared column-manager.js
+// drop-in (TK-10120), as a VIEW over this page's own VISCOL/COLORDER state.
+let __cm=null;
 function buildFieldToggles(){
-  const groups={};
-  COLS.forEach(c=>{ (groups[c.g||'Other']=groups[c.g||'Other']||[]).push(c); });
-  $('#fFields').innerHTML=Object.entries(groups).map(([g,cs])=>
-    `<div class="fgrp">${esc(g)}</div>`+cs.map(c=>`<label class="ftog"><input type="checkbox" data-ck="${c.k}" ${colVis(c.k)?'checked':''}><span>${esc(c.l)}</span></label>`).join('')
-  ).join('');
+  if(!window.ColumnManager){ // fallback if the module hasn't loaded (keeps page usable)
+    const groups={};
+    COLS.forEach(c=>{ (groups[c.g||'Other']=groups[c.g||'Other']||[]).push(c); });
+    $('#fFields').innerHTML=Object.entries(groups).map(([g,cs])=>
+      `<div class="fgrp">${esc(g)}</div>`+cs.map(c=>`<label class="ftog"><input type="checkbox" data-ck="${c.k}" ${colVis(c.k)?'checked':''}><span>${esc(c.l)}</span></label>`).join('')
+    ).join(''); return; }
+  const cols=COLS.map(c=>({k:c.k,l:c.l,g:c.g||'Other'}));
+  if(!__cm){
+    __cm=ColumnManager.init({ mount:'#fFields', footer:false, hint:false, cols,
+      state:{ visible:k=>colVis(k), setVisible:(k,v)=>{VISCOL[k]=v;saveCols();},
+              order:()=>{syncColOrder();return COLORDER.slice();}, setOrder:a=>{COLORDER=a;saveColOrder();},
+              reset:()=>{VISCOL={};saveCols();COLORDER=[];saveColOrder();} },
+      onChange:()=>render() });
+  } else { __cm.setCols(cols); }
 }
 function passFacets(r){
   if(F.city.size && !F.city.has(r.city)) return false;
@@ -294,7 +306,7 @@ $('#thead').addEventListener('drop',e=>{ if(!dragKey) return; const th=e.target.
     const r=th.getBoundingClientRect(), after=(e.clientX-r.left)>r.width/2;
     syncColOrder(); const from=COLORDER.indexOf(dragKey); if(from>=0){
       COLORDER.splice(from,1); let to=COLORDER.indexOf(tgt); if(after) to+=1; COLORDER.splice(to,0,dragKey);
-      saveColOrder(); lastColSig=''; render();
+      saveColOrder(); lastColSig=''; render(); if(__cm)__cm.refresh();
     }
   }
   dragKey=null; });
@@ -339,7 +351,7 @@ $('#view').addEventListener('click',e=>{const b=e.target.closest('button');if(!b
 $('#fCity').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;tog(F.city,c.dataset.ci);c.classList.toggle('active');render();});
 $('#fYear').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;tog(F.year,c.dataset.yr);c.classList.toggle('active');render();});
 $('#fBeds').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;tog(F.beds,c.dataset.bd);c.classList.toggle('active');render();});
-$('#fFields').addEventListener('change',e=>{const cb=e.target.closest('input[data-ck]');if(!cb)return;VISCOL[cb.dataset.ck]=cb.checked;saveCols();render();});
+// (#fFields change is handled inside column-manager.js now — its onChange calls render)
 $('#fAll').addEventListener('click',()=>{COLS.forEach(c=>VISCOL[c.k]=true);saveCols();buildFieldToggles();render();});
 $('#fNone').addEventListener('click',()=>{COLS.forEach(c=>VISCOL[c.k]=false);saveCols();buildFieldToggles();render();});
 // Reset the whole table layout back to the tight default: visibility, order, widths.
@@ -369,6 +381,7 @@ fetch('/api/closed-sales?limit=5000').then(r=>r.json()).then(d=>{
   autoCols(); if(window.__buildSortSel)window.__buildSortSel(); buildRail(); render(); drawCharts(d);
 }).catch(()=>{ $('#count').textContent='failed to load /api/closed-sales'; $('#stat').textContent='sold-comps load error.'; });
 </script>
+  <script src="/column-manager.js" defer></script>
   <script src="/col-resize.js" defer></script>
 </body>
 </html>

← 3b9663c column-manager: add external-state + footer/hint opts; adopt  ·  back to Commercialrealestate  ·  auto-save: 2026-08-01T22:40:01 (1 files) — public/lending-gr 4ba8f30 →