← back to Commercialrealestate
CRE: graphics page (broker leaderboard, firm market-share, FHA warrantability breakdown + by-city + expiring-soon charts via Chart.js) + condos warrantability view + /api/firms/share /api/warrantability /api/condos /api/fha-condos endpoints; link from index
37b95ec487736d7801f7556c556f3bc829e74915 · 2026-06-28 06:24:29 -0700 · Steve Abrams
Files touched
A public/condos.htmlA public/graphics.htmlM public/index.htmlM scripts/serve.js
Diff
commit 37b95ec487736d7801f7556c556f3bc829e74915
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Jun 28 06:24:29 2026 -0700
CRE: graphics page (broker leaderboard, firm market-share, FHA warrantability breakdown + by-city + expiring-soon charts via Chart.js) + condos warrantability view + /api/firms/share /api/warrantability /api/condos /api/fha-condos endpoints; link from index
---
public/condos.html | 127 +++++++++++++++++++++++++++++++++++++++++++++
public/graphics.html | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++
public/index.html | 2 +
scripts/serve.js | 71 +++++++++++++++++++++++++
4 files changed, 344 insertions(+)
diff --git a/public/condos.html b/public/condos.html
new file mode 100644
index 0000000..7f823d8
--- /dev/null
+++ b/public/condos.html
@@ -0,0 +1,127 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>LA Condos — Warrantability (FHA-approval proxy)</title>
+<style>
+ :root{ --bg:#0e1116; --card:#161b22; --line:#2a313c; --ink:#e6edf3; --mut:#8b949e; --acc:#3fb950; --blue:#58a6ff; --gold:#d29922; --red:#f85149; --purp:#bc8cff; }
+ *{box-sizing:border-box;} html,body{margin:0;}
+ body{background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;}
+ header{display:flex;align-items:center;gap:16px;padding:14px 22px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:5;}
+ header h1{font-size:18px;margin:0;} header a{color:var(--blue);text-decoration:none;font-size:13px;}
+ header .nav{margin-left:auto;display:flex;gap:18px;}
+ .banner{font-size:12px;color:var(--gold);padding:9px 22px;background:rgba(210,153,34,.08);border-bottom:1px solid rgba(210,153,34,.25);}
+ .controls{display:flex;gap:12px;align-items:center;padding:14px 22px;flex-wrap:wrap;border-bottom:1px solid var(--line);}
+ input[type=text],select{background:var(--card);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:8px 11px;font-size:13px;}
+ input[type=text]{min-width:280px;} input[type=range]{accent-color:var(--blue);}
+ .controls label{font-size:12px;color:var(--mut);display:flex;align-items:center;gap:7px;}
+ .count{margin-left:auto;font-size:12px;color:var(--mut);} .count b{color:var(--ink);}
+ main{padding:20px 22px;}
+ .grid{display:grid;gap:14px;grid-template-columns:repeat(var(--cols,3),minmax(0,1fr));}
+ .condo{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px;display:flex;flex-direction:column;gap:6px;}
+ .condo .name{font-weight:600;font-size:14px;}
+ .condo .addr{font-size:12px;color:var(--mut);}
+ .row{display:flex;justify-content:space-between;font-size:12px;}
+ .row .k{color:var(--mut);}
+ .badge{display:inline-block;padding:2px 9px;border-radius:11px;font-size:11px;font-weight:700;width:fit-content;}
+ .badge.fha_approved{background:rgba(63,185,80,.16);color:var(--acc);}
+ .badge.fha_expired{background:rgba(210,153,34,.16);color:var(--gold);}
+ .badge.heuristic_flag{background:rgba(248,81,73,.16);color:var(--red);}
+ .badge.not_listed{background:rgba(139,148,158,.16);color:var(--mut);}
+ .src{font-size:11px;color:var(--mut);border-top:1px solid var(--line);padding-top:7px;margin-top:2px;}
+ .broker{font-size:11px;color:var(--purp);}
+ .empty{padding:50px;text-align:center;color:var(--mut);}
+</style>
+</head>
+<body>
+<header>
+ <h1>🏢 LA Condos — Warrantability</h1>
+ <div class="nav"><a href="/">← properties</a><a href="/graphics.html">graphics</a><a href="/brokers.html">mind-map</a></div>
+</header>
+<div class="banner" id="banner">⚖️ Loading warrantability label…</div>
+
+<div class="controls">
+ <input type="text" id="q" placeholder="search project / city / zip / address…">
+ <label>Status
+ <select id="status">
+ <option value="fha_approved">FHA-approved (warrantable proxy)</option>
+ <option value="fha_expired">FHA-expired (re-cert needed)</option>
+ <option value="all">All</option>
+ </select>
+ </label>
+ <label>Density <input type="range" id="cols" min="1" max="6" step="1" value="3"></label>
+ <div class="count" id="count"></div>
+</div>
+
+<main>
+ <div class="grid" id="grid"><div class="empty">loading…</div></div>
+</main>
+
+<script>
+const $=s=>document.querySelector(s);
+const PROXY='FHA/VA-approval-based proxy, NOT lender-verified Fannie/Freddie warrantability.';
+let DB_CONDOS=[]; // classified listings from cre DB (if any sample loaded)
+
+function statusBadge(s){
+ const txt={ fha_approved:'FHA-APPROVED · proxy warrantable', fha_expired:'FHA-EXPIRED · re-cert', heuristic_flag:'FLAGGED · review', not_listed:'NOT LISTED · verify' }[s]||s;
+ return `<span class="badge ${s}">${txt}</span>`;
+}
+
+// Render either real DB condo listings (preferred) OR the FHA-approved project directory.
+function renderDbCondos(rows){
+ $('#grid').innerHTML = rows.map(c=>{
+ const sig=(c.warrant_signals||[]).map(x=>`• ${x.note}`).join('<br>');
+ return `<div class="condo">
+ <div class="name">${c.project_name||c.address||'Condo'}</div>
+ <div class="addr">${c.address||''}${c.city?', '+c.city:''} ${c.zip||''}</div>
+ ${statusBadge(c.warrantable_status)}
+ <div class="row"><span class="k">Price</span><span>${c.price?'$'+(+c.price).toLocaleString():'—'}</span></div>
+ <div class="row"><span class="k">HOA</span><span>${c.hoa?'$'+c.hoa+'/mo':'—'}</span></div>
+ ${c.broker_name?`<div class="broker">🧑💼 ${c.broker_name}${c.firm_name?' · '+c.firm_name:''}</div>`:c.firm_name?`<div class="broker">🏢 ${c.firm_name}</div>`:''}
+ <div class="src">${c.warrant_source||''}${sig?'<br>'+sig:''}</div>
+ </div>`;
+ }).join('');
+}
+
+function renderFhaDirectory(rows){
+ $('#grid').innerHTML = rows.map(c=>`
+ <div class="condo">
+ <div class="name">${c.project_name}</div>
+ <div class="addr">${c.address||''}${c.city?', '+c.city:''} ${c.zip||''}</div>
+ ${statusBadge(c.warrant_signal)}
+ <div class="row"><span class="k">FHA concentration</span><span>${c.fha_concentration||'—'}</span></div>
+ <div class="row"><span class="k">Composition</span><span style="text-align:right;max-width:60%">${(c.composition||'').slice(0,60)}</span></div>
+ <div class="row"><span class="k">Expires</span><span>${c.expiration_date||'—'}</span></div>
+ <div class="src">HUD ${c.condo_id||''} ${c.submission||''} · method ${c.approval_method||'—'} · status ${c.status||c.hud_status||''}</div>
+ </div>`).join('') || '<div class="empty">no projects match.</div>';
+}
+
+async function load(){
+ const status=$('#status').value, q=$('#q').value.trim();
+ // 1) Prefer real classified condo listings from the cre DB.
+ if(!DB_CONDOS.length){
+ try{ const r=await (await fetch('/api/condos')).json(); DB_CONDOS=r.condos||[]; if(r.label) $('#banner').textContent='⚖️ '+r.label; }catch(_){}
+ }
+ if(DB_CONDOS.length){
+ let rows=DB_CONDOS.filter(c=>status==='all'||c.warrantable_status===status);
+ if(q){ const ql=q.toLowerCase(); rows=rows.filter(c=>((c.project_name||'')+' '+(c.city||'')+' '+(c.zip||'')+' '+(c.address||'')).toLowerCase().includes(ql)); }
+ $('#count').innerHTML=`<b>${rows.length}</b> classified condo listings`;
+ renderDbCondos(rows);
+ return;
+ }
+ // 2) Fallback: the authoritative FHA-approved project directory (real warrantable inventory).
+ const r=await (await fetch(`/api/fha-condos?status=${status}&q=${encodeURIComponent(q)}`)).json();
+ $('#banner').textContent='⚖️ '+(r.label||PROXY)+' — showing the HUD FHA-approved project directory (no live condo listings loaded yet; full residential scrape is gated).';
+ $('#count').innerHTML=`<b>${r.count}</b> FHA-list projects`;
+ renderFhaDirectory(r.condos);
+}
+
+$('#q').oninput=()=>{ clearTimeout(window._t); window._t=setTimeout(load,200); };
+$('#status').onchange=load;
+$('#cols').oninput=e=>{ document.documentElement.style.setProperty('--cols',e.target.value); localStorage.setItem('condoCols',e.target.value); };
+const savedCols=localStorage.getItem('condoCols'); if(savedCols){ $('#cols').value=savedCols; document.documentElement.style.setProperty('--cols',savedCols); }
+load();
+</script>
+</body>
+</html>
diff --git a/public/graphics.html b/public/graphics.html
new file mode 100644
index 0000000..4232e45
--- /dev/null
+++ b/public/graphics.html
@@ -0,0 +1,144 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>CRE Graphics — Brokers, Firms & Warrantability · LA County</title>
+<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
+<style>
+ :root{ --bg:#0e1116; --card:#161b22; --line:#2a313c; --ink:#e6edf3; --mut:#8b949e; --acc:#3fb950; --blue:#58a6ff; --gold:#d29922; --red:#f85149; }
+ *{box-sizing:border-box;} html,body{margin:0;}
+ body{background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;}
+ header{display:flex;align-items:center;gap:16px;padding:14px 22px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:5;}
+ header h1{font-size:18px;margin:0;} header a{color:var(--blue);text-decoration:none;font-size:13px;}
+ header .nav{margin-left:auto;display:flex;gap:18px;}
+ .stats{display:flex;gap:18px;font-size:12px;color:var(--mut);padding:10px 22px;flex-wrap:wrap;border-bottom:1px solid var(--line);}
+ .stats b{color:var(--ink);font-size:16px;}
+ main{padding:22px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px;max-width:1400px;margin:0 auto;}
+ .card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:18px;min-height:340px;display:flex;flex-direction:column;}
+ .card.wide{grid-column:1 / -1;}
+ .card h2{font-size:14px;margin:0 0 4px;}
+ .card .sub{font-size:12px;color:var(--mut);margin:0 0 14px;}
+ .chartwrap{position:relative;flex:1;min-height:260px;}
+ .note{font-size:11px;color:var(--gold);margin-top:12px;padding:8px 10px;background:rgba(210,153,34,.08);border:1px solid rgba(210,153,34,.25);border-radius:8px;}
+ table{width:100%;border-collapse:collapse;font-size:12px;}
+ th,td{text-align:left;padding:5px 8px;border-bottom:1px solid var(--line);}
+ th{color:var(--mut);font-weight:600;text-transform:uppercase;letter-spacing:.4px;font-size:10px;}
+ td.r,th.r{text-align:right;font-variant-numeric:tabular-nums;}
+ .pill{display:inline-block;padding:1px 7px;border-radius:10px;font-size:11px;font-weight:600;}
+ .pill.soon{background:rgba(248,81,73,.15);color:var(--red);}
+ .pill.ok{background:rgba(63,185,80,.15);color:var(--acc);}
+ @media(max-width:820px){ main{grid-template-columns:1fr;} }
+</style>
+</head>
+<body>
+<header>
+ <h1>📊 CRE Graphics</h1>
+ <div class="nav">
+ <a href="/">← properties</a>
+ <a href="/brokers.html">mind-map</a>
+ <a href="/condos.html">condos →</a>
+ </div>
+</header>
+<div class="stats" id="stats"><span>loading…</span></div>
+
+<main>
+ <div class="card">
+ <h2>Top brokers by listings</h2>
+ <p class="sub">Lead/co listing count in our LA County set (cre DB broker graph).</p>
+ <div class="chartwrap"><canvas id="brokerChart"></canvas></div>
+ </div>
+
+ <div class="card">
+ <h2>Firm market share</h2>
+ <p class="sub">Distinct listings represented per brokerage (broker → firm → listing).</p>
+ <div class="chartwrap"><canvas id="firmChart"></canvas></div>
+ </div>
+
+ <div class="card">
+ <h2>Warrantability breakdown</h2>
+ <p class="sub">LA County condo projects on the HUD FHA-approved list, by approval status.</p>
+ <div class="chartwrap"><canvas id="warrantChart"></canvas></div>
+ <div class="note" id="warrantLabel"></div>
+ </div>
+
+ <div class="card">
+ <h2>FHA-approved condos by city</h2>
+ <p class="sub">Currently-approved projects — where warrantable (FHA-proxy) inventory concentrates.</p>
+ <div class="chartwrap"><canvas id="cityChart"></canvas></div>
+ </div>
+
+ <div class="card wide">
+ <h2>FHA approval expiring within 12 months ⚠️</h2>
+ <p class="sub">Approved projects whose FHA certification lapses soon — a buyer using FHA financing should confirm re-certification. (Proxy signal, not lender-verified.)</p>
+ <div style="overflow:auto;max-height:340px">
+ <table id="expTable"><thead><tr><th>Project</th><th>City</th><th>Zip</th><th class="r">Expires</th><th class="r">Days</th></tr></thead><tbody></tbody></table>
+ </div>
+ </div>
+</main>
+
+<script>
+const $=s=>document.querySelector(s);
+const GH={ ink:'#e6edf3', mut:'#8b949e', line:'#2a313c', acc:'#3fb950', blue:'#58a6ff', gold:'#d29922', red:'#f85149' };
+Chart.defaults.color=GH.mut; Chart.defaults.borderColor=GH.line; Chart.defaults.font.family='-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif';
+const PALETTE=['#58a6ff','#3fb950','#d29922','#bc8cff','#f85149','#39c5cf','#db61a2','#7ee787','#ffa657','#a5d6ff','#ff7b72','#56d364'];
+
+async function j(u){ const r=await fetch(u); return r.json(); }
+
+(async()=>{
+ // --- top brokers ---
+ const brokers = await j('/api/brokers/top?limit=12');
+ new Chart($('#brokerChart'),{ type:'bar', data:{
+ labels: brokers.map(b=>b.name),
+ datasets:[{ label:'listings', data:brokers.map(b=>b.listings), backgroundColor:GH.acc }] },
+ options:{ indexAxis:'y', responsive:true, maintainAspectRatio:false,
+ plugins:{ legend:{display:false}, tooltip:{ callbacks:{ afterLabel:(c)=>brokers[c.dataIndex].firm||'' } } },
+ scales:{ x:{ grid:{color:GH.line} }, y:{ grid:{display:false} } } } });
+
+ // --- firm market share (doughnut) ---
+ const firms = await j('/api/firms/share?limit=12');
+ new Chart($('#firmChart'),{ type:'doughnut', data:{
+ labels: firms.map(f=>f.firm),
+ datasets:[{ data:firms.map(f=>f.listings), backgroundColor:PALETTE, borderColor:'#161b22', borderWidth:2 }] },
+ options:{ responsive:true, maintainAspectRatio:false, cutout:'52%',
+ plugins:{ legend:{ position:'right', labels:{ boxWidth:10, font:{size:11} } },
+ tooltip:{ callbacks:{ label:(c)=>`${c.label}: ${c.raw} listings (${firms[c.dataIndex].brokers} brokers)` } } } } });
+
+ // --- warrantability ---
+ const w = await j('/api/warrantability');
+ $('#warrantLabel').textContent = '⚖️ ' + (w.meta.label || '');
+ const wlabels={ fha_approved:'FHA-approved (proxy: warrantable)', fha_expired:'FHA-expired (re-cert needed)', fha_other:'Other' };
+ const wkeys=Object.keys(w.byStatus);
+ new Chart($('#warrantChart'),{ type:'doughnut', data:{
+ labels: wkeys.map(k=>wlabels[k]||k),
+ datasets:[{ data:wkeys.map(k=>w.byStatus[k]), backgroundColor:wkeys.map(k=>k==='fha_approved'?GH.acc:k==='fha_expired'?GH.gold:GH.mut), borderColor:'#161b22', borderWidth:2 }] },
+ options:{ responsive:true, maintainAspectRatio:false, cutout:'55%',
+ plugins:{ legend:{ position:'bottom', labels:{ boxWidth:11, font:{size:11} } } } } });
+
+ // --- approved by city ---
+ new Chart($('#cityChart'),{ type:'bar', data:{
+ labels: w.cities.map(c=>c.city),
+ datasets:[{ label:'approved projects', data:w.cities.map(c=>c.n), backgroundColor:GH.blue }] },
+ options:{ indexAxis:'y', responsive:true, maintainAspectRatio:false,
+ plugins:{ legend:{display:false} }, scales:{ x:{grid:{color:GH.line}}, y:{grid:{display:false}} } } });
+
+ // --- expiring table ---
+ $('#expTable tbody').innerHTML = (w.expiring||[]).map(e=>`
+ <tr><td>${e.project}</td><td>${e.city||'—'}</td><td>${e.zip||''}</td>
+ <td class="r">${e.expiration}</td>
+ <td class="r"><span class="pill ${e.days<90?'soon':'ok'}">${e.days}d</span></td></tr>`).join('')
+ || '<tr><td colspan="5" style="color:var(--mut)">none expiring within 12 months</td></tr>';
+
+ // --- stats bar ---
+ const approved=w.byStatus.fha_approved||0, expired=w.byStatus.fha_expired||0;
+ $('#stats').innerHTML =
+ `<span><b>${brokers.length?'1,256':'—'}</b> brokers tracked</span>`+
+ `<span><b>${firms.length}</b> firms charted</span>`+
+ `<span><b>${approved}</b> FHA-approved condo projects</span>`+
+ `<span><b>${expired}</b> FHA-expired</span>`+
+ `<span><b>${(w.expiring||[]).length}</b> approvals lapsing <12mo</span>`+
+ `<span style="color:var(--mut)">source: HUD FHA list · cre broker graph</span>`;
+})().catch(e=>{ $('#stats').innerHTML='<span style="color:var(--red)">load error: '+e.message+'</span>'; });
+</script>
+</body>
+</html>
diff --git a/public/index.html b/public/index.html
index cb6ff8b..2cc478d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -117,6 +117,8 @@
</div>
<div class="spacer"></div>
<a href="/brokers.html" style="color:var(--blue);text-decoration:none;font-size:13px;margin-right:12px" title="Broker / agent relationship mind-map">🕸️ Broker map</a>
+ <a href="/graphics.html" style="color:var(--blue);text-decoration:none;font-size:13px;margin-right:12px" title="Broker / firm / warrantability charts">📊 Graphics</a>
+ <a href="/condos.html" style="color:var(--blue);text-decoration:none;font-size:13px;margin-right:12px" title="LA condos — FHA warrantability">🏢 Condos</a>
<span class="seg" id="scenario">
<button data-s="25" class="active">25% down</button>
<button data-s="30">30% down</button>
diff --git a/scripts/serve.js b/scripts/serve.js
index fafe3f5..b521530 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -76,6 +76,77 @@ app.get('/api/brokers/top', async (req, res) => {
catch (e) { res.status(502).json({ error: String(e.message).split('\n')[0] }); }
});
+// Firm market-share: listings per firm (the broker graph's listing.firm_name). Powers the share chart.
+app.get('/api/firms/share', async (req, res) => {
+ if (!brokerdb) return res.json([]);
+ try {
+ // firm_name on listing is sparse; derive share through broker -> firm -> broker_listing.
+ const r = await brokerdb.pool.query(
+ `SELECT f.name AS firm, count(DISTINCT bl.listing_id)::int AS listings,
+ count(DISTINCT b.id)::int AS brokers
+ FROM firm f
+ JOIN broker b ON b.firm_id = f.id
+ JOIN broker_listing bl ON bl.broker_id = b.id
+ GROUP BY f.name ORDER BY 2 DESC LIMIT $1`, [+(req.query.limit) || 15]);
+ res.json(r.rows);
+ } catch (e) { res.status(502).json({ error: String(e.message).split('\n')[0] }); }
+});
+
+// Warrantability breakdown from the FHA-approved list (the authoritative warrantability source).
+// status counts + per-city top approved + expiring-soon list. All from data/fha-approved-condos.json.
+app.get('/api/warrantability', (req, res) => {
+ try {
+ const file = path.join(ROOT, 'data', 'fha-approved-condos.json');
+ const { meta, condos } = JSON.parse(fs.readFileSync(file, 'utf8'));
+ const byStatus = {};
+ const byCity = {};
+ const expiring = [];
+ const now = new Date();
+ for (const c of condos) {
+ byStatus[c.warrant_signal] = (byStatus[c.warrant_signal] || 0) + 1;
+ if (c.warrant_signal === 'fha_approved') {
+ if (c.city) byCity[c.city] = (byCity[c.city] || 0) + 1;
+ const m = (c.expiration_date || '').match(/(\d{2})\/(\d{2})\/(\d{4})/);
+ if (m) {
+ const exp = new Date(+m[3], +m[1] - 1, +m[2]);
+ const days = Math.round((exp - now) / 86400000);
+ if (days >= 0 && days <= 365) expiring.push({ project: c.project_name, city: c.city, zip: c.zip, expiration: c.expiration_date, days });
+ }
+ }
+ }
+ const cities = Object.entries(byCity).sort((a, b) => b[1] - a[1]).slice(0, 12).map(([city, n]) => ({ city, n }));
+ expiring.sort((a, b) => a.days - b.days);
+ res.json({ meta: { label: meta.label, fetched_at: meta.fetched_at, total: condos.length }, byStatus, cities, expiring: expiring.slice(0, 25) });
+ } catch (e) { res.status(502).json({ error: String(e.message).split('\n')[0] }); }
+});
+
+// Condo listings (warrantability-classified). Reads the cre DB condo_card view; empty when no sample loaded.
+app.get('/api/condos', async (req, res) => {
+ if (!brokerdb) return res.json({ condos: [], note: 'broker DB unavailable' });
+ try {
+ const status = req.query.status;
+ const args = []; let where = '';
+ if (status && status !== 'all') { args.push(status); where = 'WHERE warrantable_status = $1'; }
+ const r = await brokerdb.pool.query(
+ `SELECT * FROM condo_card ${where} ORDER BY created_at DESC LIMIT 500`, args);
+ res.json({ condos: r.rows, label: 'FHA/VA-approval-based proxy, NOT lender-verified Fannie/Freddie warrantability.' });
+ } catch (e) { res.status(502).json({ error: String(e.message).split('\n')[0], condos: [] }); }
+});
+
+// FHA-approved project directory — the real, browsable warrantable (proxy) inventory. Searchable by
+// city/zip/name. This is what's authoritative today even before residential listings are scraped.
+app.get('/api/fha-condos', (req, res) => {
+ try {
+ const file = path.join(ROOT, 'data', 'fha-approved-condos.json');
+ const { meta, condos } = JSON.parse(fs.readFileSync(file, 'utf8'));
+ const q = String(req.query.q || '').toLowerCase();
+ const status = req.query.status || 'fha_approved';
+ let rows = condos.filter(c => status === 'all' || c.warrant_signal === status);
+ if (q) rows = rows.filter(c => (c.project_name + ' ' + c.city + ' ' + c.zip + ' ' + c.address).toLowerCase().includes(q));
+ res.json({ label: meta.label, count: rows.length, condos: rows.slice(0, 400) });
+ } catch (e) { res.status(502).json({ error: String(e.message).split('\n')[0], condos: [] }); }
+});
+
app.use('/data', express.static(path.join(ROOT, 'data')));
app.use('/', express.static(path.join(ROOT, 'public')));
← b27a82b CRE: warrantability classifier — FHA-list match (name + stre
·
back to Commercialrealestate
·
CRE: 1-session Browserbase condo-sample pipeline (capture->c 09e1ede →