[object Object]

← back to Commercialrealestate

auto-save: 2026-07-02T10:17:10 (1 files) — public/index.html

ad43c2536760a62bc4a1dc083227eff274cc2ba9 · 2026-07-02 10:17:19 -0700 · Steve Abrams

Files touched

Diff

commit ad43c2536760a62bc4a1dc083227eff274cc2ba9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 2 10:17:19 2026 -0700

    auto-save: 2026-07-02T10:17:10 (1 files) — public/index.html
---
 public/index.html | 35 ++++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/public/index.html b/public/index.html
index 3bdbb36..15dce00 100644
--- a/public/index.html
+++ b/public/index.html
@@ -545,6 +545,12 @@ const FIELDS = [
   {k:'demo',       label:'Demographics',      card:1, col:0, def:1},
   {k:'rent',       label:'Rent estimate',     card:1, col:1, def:1},
   {k:'links',      label:'Listing links',     card:1, col:0, def:1},
+  {k:'beds',       label:'Beds (condo)',       card:1, col:1, def:0},
+  {k:'baths',      label:'Baths (condo)',      card:1, col:1, def:0},
+  {k:'sqft',       label:'Sq Ft',              card:1, col:1, def:0},
+  {k:'hoa',        label:'HOA / mo (condo)',   card:1, col:1, def:0},
+  {k:'warr',       label:'Warrantability',     card:1, col:1, def:0},
+  {k:'broker',     label:'Broker (condo)',     card:1, col:1, def:0},
 ];
 const VIS = (function(){ let s={}; try{ s=JSON.parse(localStorage.getItem('cre_fields')||'{}'); }catch(e){} const o={}; FIELDS.forEach(f=>o[f.k]=(f.k in s)?!!s[f.k]:!!f.def); return o; })();
 function vis(k){ return !!VIS[k]; }
@@ -589,6 +595,12 @@ function colCell(k,p){ const f=p.finance||{};
     case 'cashNeeded': return fmt(f.cashNeeded);
     case 'cashFlow': return fmt(f.cashFlow);
     case 'rent': { const r=rentEstimate(p); return r?'$'+r.std.toLocaleString():'—'; }
+    case 'beds': return p.beds??'—';
+    case 'baths': return p.baths??'—';
+    case 'sqft': return p.sqft?(+p.sqft).toLocaleString():'—';
+    case 'hoa': return p.hoa?'$'+(+p.hoa).toLocaleString():'—';
+    case 'warr': return esc(p.warrantable_status||'—');
+    case 'broker': return esc(p.broker_name||'—');
     default: return '—';
   }
 }
@@ -639,7 +651,7 @@ function loopnetLink(p){
 }
 // Rich filter state
 const F = { types:new Set(), firms:new Set(), cityQuery:'', priceMin:null, priceMax:null,
-            capMin:0, unitsMin:null, yearMin:null, status:'all', afford:false };
+            capMin:0, unitsMin:null, yearMin:null, status:'all', afford:false, unwarrantable:false };
 
 function recompute(){
   const A = SCEN[scenario];
@@ -719,7 +731,7 @@ function card(p){
       <div><div class="rank">#${p.rank} · score</div><div class="addr">${p.address}</div><div class="city">${p.city}, CA ${p.zip||''}</div></div>
       ${vis('score')?`<div class="score">${p.composite}<small>fin ${p.financeScore}${p.qwenScore!=null?' · ai '+p.qwenScore:''}</small></div>`:''}
     </div>
-    <div class="badges">${vis('deal')?dealBadge(p):''}${vis('firm')?firmB:''}${vis('type')?`<span class="b type">${p.type}</span>`:''}${vis('units')?`<span class="b">${p.units} unit${p.units>1?'s':''}</span>`:''}${vis('price')?`<span class="b">${fmt(p.price)}</span>`:''}${vis('ppu')&&ppu(p)?`<span class="b" title="Price per unit">$${ppu(p).toLocaleString()}/unit</span>`:''}${vis('pricePerSf')&&ppsf(p)?`<span class="b" title="Price per building SF">$${ppsf(p).toLocaleString()}/sf</span>`:''}${vis('cap')?capB:''}${vis('verified')?verB:''}${vis('yield')&&f.grossYield?`<span class="b ok" title="Annual gross rent ÷ price">${f.grossYield}% rent yld</span>`:''}${vis('grm')&&grm(p)!=null?`<span class="b" title="Gross rent multiplier — price ÷ annual gross rent (lower = cheaper vs rent)">GRM ${grm(p)}</span>`:''}${vis('yearBuilt')&&p.year_built?`<span class="b" title="Year built">🏗 ${p.year_built}</span>`:''}${vis('dom')&&daysOnMarket(p)!=null?`<span class="b" title="Days since listed">${daysOnMarket(p)}d on mkt</span>`:''}${vis('status')?statusB:''} ${vis('afford')?aff:''} ${vis('rec')?rec:''}</div>
+    <div class="badges">${vis('deal')?dealBadge(p):''}${vis('firm')?firmB:''}${vis('type')?`<span class="b type">${p.type}</span>`:''}${vis('units')?`<span class="b">${p.units} unit${p.units>1?'s':''}</span>`:''}${vis('price')?`<span class="b">${fmt(p.price)}</span>`:''}${vis('ppu')&&ppu(p)?`<span class="b" title="Price per unit">$${ppu(p).toLocaleString()}/unit</span>`:''}${vis('pricePerSf')&&ppsf(p)?`<span class="b" title="Price per building SF">$${ppsf(p).toLocaleString()}/sf</span>`:''}${vis('cap')?capB:''}${vis('verified')?verB:''}${vis('yield')&&f.grossYield?`<span class="b ok" title="Annual gross rent ÷ price">${f.grossYield}% rent yld</span>`:''}${vis('grm')&&grm(p)!=null?`<span class="b" title="Gross rent multiplier — price ÷ annual gross rent (lower = cheaper vs rent)">GRM ${grm(p)}</span>`:''}${vis('yearBuilt')&&p.year_built?`<span class="b" title="Year built">🏗 ${p.year_built}</span>`:''}${vis('dom')&&daysOnMarket(p)!=null?`<span class="b" title="Days since listed">${daysOnMarket(p)}d on mkt</span>`:''}${vis('beds')&&p.beds!=null?`<span class="b">${p.beds} bd</span>`:''}${vis('baths')&&p.baths!=null?`<span class="b">${p.baths} ba</span>`:''}${vis('sqft')&&p.sqft?`<span class="b">${(+p.sqft).toLocaleString()} sf</span>`:''}${vis('hoa')&&p.hoa?`<span class="b" title="HOA dues">$${(+p.hoa).toLocaleString()}/mo</span>`:''}${vis('warr')&&p.warrantable_status?`<span class="b ${p.warrantable_status==='fha_approved'?'ok':'warn'}" title="Condo warrantability (FHA-approval proxy)">${p.warrantable_status==='fha_approved'?'✓ warrantable':'⚑ '+p.warrantable_status}</span>`:''}${vis('broker')&&p.broker_name?`<span class="b" title="Listing broker">🧑‍💼 ${esc(p.broker_name)}</span>`:''}${vis('status')?statusB:''} ${vis('afford')?aff:''} ${vis('rec')?rec:''}</div>
     ${vis('brokerflag')?brokerFlagBadge:''}
     ${vis('statusline')?statusLine(p):''}
     ${vis('txhist')?txHistHTML(bh):''}
@@ -756,6 +768,7 @@ function applyFilters(list){
     if(F.yearMin!=null && !(p.year_built && p.year_built>=F.yearMin)) return false;
     if(F.status==='active' && /Under Contract|Pending|OUT OF BUDGET/i.test(p.status)) return false;
     if(F.afford && !p.finance.affordable) return false;
+    if(F.unwarrantable && !(p.isCondo && p.warrantable_status && p.warrantable_status!=='fha_approved')) return false;
     return true;
   });
 }
@@ -764,7 +777,7 @@ function activeFilterCount(){
   if(F.types.size) n++; if(F.firms.size) n++; if(F.cityQuery) n++;
   if(F.priceMin!=null||F.priceMax!=null) n++; if(F.capMin>0) n++;
   if(F.unitsMin!=null) n++; if(F.yearMin!=null) n++;
-  if(F.status!=='all') n++; if(F.afford) n++;
+  if(F.status!=='all') n++; if(F.afford) n++; if(F.unwarrantable) n++;
   return n;
 }
 
@@ -797,10 +810,10 @@ function buildFacets(){
   DATA.ranked.forEach(p=>{ tCount[p.type]=(tCount[p.type]||0)+1; const fl=firmLabel(p); fCount[fl]=(fCount[fl]||0)+1; if(p.city) cCount[p.city]=(cCount[p.city]||0)+1; });
   const chipHTML=(obj,key)=>Object.entries(obj).sort((a,b)=>b[1]-a[1])
     .map(([k,c])=>`<span class="chip" data-${key}="${k.replace(/"/g,'&quot;')}">${k}<span class="ct">${c}</span></span>`).join('');
-  // Condos live on their own page (separate schema) — surface them as asset-class chips that navigate.
-  const condoChips='<span class="chip" data-nav="/condos" style="border-color:rgba(210,153,34,.55);color:var(--warn)" title="LA condos — FHA warrantability">🏢 Condos →</span>'
-    +'<span class="chip" data-nav="/condos?status=unwarrantable" style="border-color:rgba(248,81,73,.55);color:var(--bad)" title="Non-FHA-approved condos — Arcstone non-QM target">⚑ Unwarrantable →</span>';
-  $('#fType').innerHTML=condoChips+chipHTML(tCount,'t');
+  // Condos are merged into the grid as a real 'Condo' asset type (chip comes from the data below).
+  // Prepend one special in-grid filter: ⚑ Unwarrantable = Condo AND not FHA-approved (Arcstone non-QM target).
+  const unwChip='<span class="chip" data-warr="unwarrantable" style="border-color:rgba(248,81,73,.55);color:var(--bad)" title="Non-FHA-approved condos — filters the grid to Arcstone non-QM targets">⚑ Unwarrantable</span>';
+  $('#fType').innerHTML=unwChip+chipHTML(tCount,'t');
   $('#fFirm').innerHTML=chipHTML(fCount,'fm');
   // top 14 cities as quick chips; the search box covers the rest
   const topCities=Object.entries(cCount).sort((a,b)=>b[1]-a[1]).slice(0,14);
@@ -854,7 +867,7 @@ function setScenario(s){ scenario=s; localStorage.setItem('cre_scenario',s);
   $$('#scenario button').forEach(b=>b.classList.toggle('active',b.dataset.s===s)); recompute(); }
 
 function syncChips(){
-  $$('#fType .chip').forEach(c=>c.classList.toggle('active',F.types.has(c.dataset.t)));
+  $$('#fType .chip').forEach(c=>{ if(c.dataset.warr==='unwarrantable') c.classList.toggle('active',F.unwarrantable); else c.classList.toggle('active',F.types.has(c.dataset.t)); });
   $$('#fFirm .chip').forEach(c=>c.classList.toggle('active',F.firms.has(c.dataset.fm)));
   $$('#fCityChips .chip').forEach(c=>c.classList.toggle('active',F.cityQuery===c.dataset.cy.toLowerCase()));
   $$('#fStatus .chip').forEach(c=>c.classList.toggle('active',F.status===c.dataset.st));
@@ -969,7 +982,11 @@ fetch('data/ranked.json').then(r=>r.json()).then(d=>{
   $('#burger').onclick=openDrawer; $('#dclose').onclick=closeDrawer; $('#scrim').onclick=closeDrawer;
   $('#reset').onclick=resetAll;
   // facet chip handlers (multi-select toggles)
-  $('#fType').onclick=e=>{ const c=e.target.closest('.chip'); if(!c) return; if(c.dataset.nav){ location.href=c.dataset.nav; return; } F.types.has(c.dataset.t)?F.types.delete(c.dataset.t):F.types.add(c.dataset.t); syncChips(); render(); };
+  $('#fType').onclick=e=>{ const c=e.target.closest('.chip'); if(!c) return;
+    if(c.dataset.warr==='unwarrantable'){ F.unwarrantable=!F.unwarrantable; if(F.unwarrantable) F.types=new Set(['Condo']); syncChips(); render(); return; }
+    F.types.has(c.dataset.t)?F.types.delete(c.dataset.t):F.types.add(c.dataset.t);
+    if(!F.types.has('Condo')) F.unwarrantable=false;
+    syncChips(); render(); };
   $('#fFirm').onclick=e=>{ const c=e.target.closest('.chip'); if(!c) return; F.firms.has(c.dataset.fm)?F.firms.delete(c.dataset.fm):F.firms.add(c.dataset.fm); syncChips(); render(); };
   $('#fCityChips').onclick=e=>{ const c=e.target.closest('.chip'); if(!c) return; const cy=c.dataset.cy.toLowerCase(); F.cityQuery=(F.cityQuery===cy)?'':cy; $('#fCity').value=(F.cityQuery?c.dataset.cy:''); syncChips(); render(); };
   $('#fStatus').onclick=e=>{ const c=e.target.closest('.chip'); if(!c) return; F.status=c.dataset.st; syncChips(); render(); };

← 58cbced fix(index): surface Condos + Unwarrantable as the FIRST chip  ·  back to Commercialrealestate  ·  feat(index+mls): merge condos IN-GRID as a real 'Condo' asse 44c3be8 →