← back to Commercialrealestate
CRCP listings + just-listed: add Max units (and Units/size min+max + built after/before on mls) to match the main grid
b36a4def2fb0d28d5ff109eaca52c8fbb3168d1b · 2026-08-17 16:07:19 -0700 · steve
Files touched
M public/just-listed.htmlM public/mls.html
Diff
commit b36a4def2fb0d28d5ff109eaca52c8fbb3168d1b
Author: steve <steve@designerwallcoverings.com>
Date: Mon Aug 17 16:07:19 2026 -0700
CRCP listings + just-listed: add Max units (and Units/size min+max + built after/before on mls) to match the main grid
---
public/just-listed.html | 5 ++++-
public/mls.html | 14 ++++++++++++--
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/public/just-listed.html b/public/just-listed.html
index 73838b3..6fccdc3 100644
--- a/public/just-listed.html
+++ b/public/just-listed.html
@@ -90,6 +90,7 @@ td a.f:hover{text-decoration:underline}
<div class="rsec"><h4>Min price $</h4><input type="number" id="pMin" placeholder="e.g. 1000000" step="250000"></div>
<div class="rsec"><h4>Max price $</h4><input type="number" id="pMax" placeholder="e.g. 5000000" step="250000"></div>
<div class="rsec"><h4>Min units</h4><input type="number" id="uMin" placeholder="e.g. 5"></div>
+ <div class="rsec"><h4>Max units</h4><input type="number" id="uMax" placeholder="any"></div>
</aside>
<div class="main">
<div id="stats"></div>
@@ -119,7 +120,7 @@ const esc=s=>(s==null?'':String(s)).replace(/&/g,'&').replace(/</g,'<').r
const money=n=>n==null||isNaN(+n)?null:'$'+Math.round(+n).toLocaleString();
let DATA=[],META={},q='',sortKey='price',sortDir=-1,ME=null;
let dens=(function(){const v=parseInt(localStorage.getItem('jlDens'),10);return(v>=2&&v<=10)?v:6;})();
-const F={status:new Set(),type:new Set(),city:new Set(),pMin:null,pMax:null,uMin:null};
+const F={status:new Set(),type:new Set(),city:new Set(),pMin:null,pMax:null,uMin:null,uMax:null};
const tog=(s,v)=>{s.has(v)?s.delete(v):s.add(v);};
const statusClass=s=>{const t=String(s||'').toLowerCase();return t.startsWith('active')?'active':(t.includes('pending')||t.includes('contract'))?'pending':'other';};
const statusShort=s=>String(s||'—').replace(/\s*\(.*\)$/,'').trim();
@@ -168,6 +169,7 @@ function pass(r){
if(F.pMin!=null&&!(+r.price>=F.pMin))return false;
if(F.pMax!=null&&!(+r.price<=F.pMax))return false;
if(F.uMin!=null&&!(+r.units>=F.uMin))return false;
+ if(F.uMax!=null&&!(r.units!=null&&+r.units<=F.uMax))return false;
return passSearch(r);
}
function filtered(){
@@ -242,6 +244,7 @@ $('#fCity').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c
$('#pMin').addEventListener('input',()=>{F.pMin=$('#pMin').value?+$('#pMin').value:null;render();});
$('#pMax').addEventListener('input',()=>{F.pMax=$('#pMax').value?+$('#pMax').value:null;render();});
$('#uMin').addEventListener('input',()=>{F.uMin=$('#uMin').value?+$('#uMin').value:null;render();});
+$('#uMax').addEventListener('input',()=>{F.uMax=$('#uMax').value?+$('#uMax').value:null;render();});
$('#q').addEventListener('input',()=>{q=$('#q').value.trim();render();});
$('#sort').addEventListener('change',()=>{const v=$('#sort').value.split(':');setSort(v[0],v[1]==='asc'?1:-1);});
$('#dens').addEventListener('input',()=>{dens=+$('#dens').value;localStorage.setItem('jlDens',dens);render();});
diff --git a/public/mls.html b/public/mls.html
index a433501..3b60f83 100644
--- a/public/mls.html
+++ b/public/mls.html
@@ -143,6 +143,8 @@
<div class="rsec"><h4>Source of truth</h4><div class="chips" id="fSource"></div><div style="font-size:10px;color:var(--mut);margin-top:5px;line-height:1.4">firm-direct = the brokerage's own listing · broker-of-record = real listing broker known · aggregator = CREXi/portal only</div></div>
<div class="rsec"><h4>Brokerage <span style="font-size:10px;font-weight:400;color:var(--mut)">(listing broker)</span></h4><div class="chips" id="fFirm"></div></div>
<div class="rsec"><h4>Price</h4><div class="row2"><div class="fld"><label>Min $</label><input type="number" id="pMin" placeholder="any" step="50000"></div><div class="fld"><label>Max $</label><input type="number" id="pMax" placeholder="any" step="50000"></div></div></div>
+ <div class="rsec"><h4>Units / size</h4><div class="row2"><div class="fld"><label>Min units</label><input type="number" id="uMin" placeholder="any" min="0" step="1"></div><div class="fld"><label>Max units</label><input type="number" id="uMax" placeholder="any" min="0" step="1"></div></div>
+ <div class="row2" style="margin-top:8px"><div class="fld"><label>Built after</label><input type="number" id="yMin" placeholder="any" min="1900" max="2026" step="1"></div><div class="fld"><label>Built before</label><input type="number" id="yMax" placeholder="any" min="1900" max="2026" step="1"></div></div></div>
</aside>
<div class="wrap">
<div class="statsbar" id="statsbar"></div>
@@ -219,7 +221,7 @@ const COLS=[
];
let DATA=[], view='table', sortKey='dom', sortDir=1, q='', lastColSig=''; // default landing: freshest listings first (days-on-market asc); localStorage overrides for returning users
const _rd=(function(){ const v=parseInt(localStorage.getItem('mlsRecentDays'),10); return (v>0&&v<=365)?v:7; })();
-const F={types:new Set(),cities:new Set(),status:new Set(),source:new Set(),firms:new Set(),pmin:null,pmax:null,unwarrantable:false,nonqm:false,shortlist:false,justsold:false,justlisted:false,inescrow:false,fellout:false,pricecut:false,recentDays:_rd};
+const F={types:new Set(),cities:new Set(),status:new Set(),source:new Set(),firms:new Set(),pmin:null,pmax:null,umin:null,umax:null,ymin:null,ymax:null,unwarrantable:false,nonqm:false,shortlist:false,justsold:false,justlisted:false,inescrow:false,fellout:false,pricecut:false,recentDays:_rd};
const daysSince=d=>{ if(!d) return Infinity; const t=Date.parse(d); return isNaN(t)?Infinity:(Date.now()-t)/86400000; };
// Real listing age: Redfin DAYS ON MARKET when present (gap-immune), else our first-seen date.
const listAge=r=>{ if(r.dom!=null&&isFinite(+r.dom)) return +r.dom; return daysSince(r.first_seen); };
@@ -374,7 +376,7 @@ function isNonQM(r){ const t=(r.type||'').toLowerCase();
if(r.type==='Condo' && r.warrantable_status && r.warrantable_status!=='fha_approved') return true;
if(/multi/.test(t) && r.units>=1 && r.units<=9) return true;
if(/mixed/.test(t)) return true; return false; }
-function anySelection(){ return F.types.size||F.cities.size||F.status.size||F.source.size||F.firms.size||F.pmin!=null||F.pmax!=null||F.unwarrantable||F.nonqm||F.shortlist||F.justsold||F.justlisted||F.inescrow||F.fellout||F.pricecut||(q&&q.length); }
+function anySelection(){ return F.types.size||F.cities.size||F.status.size||F.source.size||F.firms.size||F.pmin!=null||F.pmax!=null||F.umin!=null||F.umax!=null||F.ymin!=null||F.ymax!=null||F.unwarrantable||F.nonqm||F.shortlist||F.justsold||F.justlisted||F.inescrow||F.fellout||F.pricecut||(q&&q.length); }
function passFacets(r){
if(F.types.size && !F.types.has(r.type)) return false;
if(F.cities.size && !F.cities.has(r.city)) return false;
@@ -383,6 +385,10 @@ function passFacets(r){
if(F.status.size){ const s=String(r.status||'').startsWith('Active')?'Active':'Off-market'; if(!F.status.has(s)) return false; }
if(F.pmin!=null && !(+r.price>=F.pmin)) return false;
if(F.pmax!=null && !(+r.price<=F.pmax)) return false;
+ if(F.umin!=null && !(r.units!=null && +r.units>=F.umin)) return false;
+ if(F.umax!=null && !(r.units!=null && +r.units<=F.umax)) return false;
+ if(F.ymin!=null && !(r.year_built && +r.year_built>=F.ymin)) return false;
+ if(F.ymax!=null && !(r.year_built && +r.year_built<=F.ymax)) return false;
if(F.unwarrantable && !(r.type==='Condo' && r.warrantable_status && r.warrantable_status!=='fha_approved')) return false;
if(F.nonqm && !isNonQM(r)) return false;
// Just Listed = still on the market (not sold/withdrawn) & listed within N days (real DAYS ON MARKET).
@@ -573,6 +579,10 @@ $('#fSource').addEventListener('click',e=>{const c=e.target.closest('.chip');if(
$('#fFirm').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;tog(F.firms,c.dataset.fm);c.classList.toggle('active');render();});
$('#pMin').addEventListener('input',()=>{F.pmin=$('#pMin').value?+$('#pMin').value:null;render();});
$('#pMax').addEventListener('input',()=>{F.pmax=$('#pMax').value?+$('#pMax').value:null;render();});
+$('#uMin').addEventListener('input',()=>{F.umin=$('#uMin').value?+$('#uMin').value:null;render();});
+$('#uMax').addEventListener('input',()=>{F.umax=$('#uMax').value?+$('#uMax').value:null;render();});
+$('#yMin').addEventListener('input',()=>{F.ymin=$('#yMin').value?+$('#yMin').value:null;render();});
+$('#yMax').addEventListener('input',()=>{F.ymax=$('#yMax').value?+$('#yMax').value:null;render();});
// (#fFields change is handled inside column-manager.js now — its onChange calls render)
// ── panel drag-to-reorder columns (Steve rule: resort order here in the panel too) ──
let pDragKey=null;
← 706f95a CRCP properties: add Max units + Built-before to the units/s
·
back to Commercialrealestate
·
CRCP listings: stage price/units/year filters behind a Go bu 6a61f29 →