← back to Commercialrealestate
CRCP: contrarian fixes on broker rollup — disclose 123 broker-licensed non-supervisors, persist broker sort, guard agent re-fetch, reorder cleanBrokerName address-strip
71cc61781c7885b861fd67171a38d61dffbe74b1 · 2026-07-10 23:02:08 -0700 · Steve Abrams
Files touched
M package-lock.jsonM package.jsonM public/licensed-agents.htmlM scripts/serve.js
Diff
commit 71cc61781c7885b861fd67171a38d61dffbe74b1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Jul 10 23:02:08 2026 -0700
CRCP: contrarian fixes on broker rollup — disclose 123 broker-licensed non-supervisors, persist broker sort, guard agent re-fetch, reorder cleanBrokerName address-strip
---
package-lock.json | 4 ++--
package.json | 2 +-
public/licensed-agents.html | 24 +++++++++++++++---------
scripts/serve.js | 10 ++++++----
4 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 9164f7f..0f14ecc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "commercialrealestate",
- "version": "0.6.0",
+ "version": "0.6.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "commercialrealestate",
- "version": "0.6.0",
+ "version": "0.6.1",
"dependencies": {
"better-sqlite3": "^12.11.1",
"express": "^4.22.2",
diff --git a/package.json b/package.json
index 64644f4..4280764 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "commercialrealestate",
- "version": "0.6.0",
+ "version": "0.6.1",
"private": true,
"description": "LA County CRE investment explorer — multi-firm sourced, Census + assessor enriched, Qwen-analyzed",
"scripts": {
diff --git a/public/licensed-agents.html b/public/licensed-agents.html
index 493beee..9df5234 100644
--- a/public/licensed-agents.html
+++ b/public/licensed-agents.html
@@ -139,17 +139,22 @@ function pills(){
$('#pills').innerHTML=defs.map(([k,l,f])=>`<span class="pill ${filt===k?'on':''}" data-f="${k}">${l} <b>${n(f)}</b></span>`).join('');
document.querySelectorAll('.pill').forEach(p=>p.onclick=()=>{filt=p.dataset.f;pills();render();});
}
+let aLoaded=false, AGENT_NOTE='';
async function load(){
- let d; try{ d=await (await fetch('/api/agents/licensed')).json(); }catch(e){ $('#note').textContent='Failed to load /api/agents/licensed'; return; }
- DATA=d.agents||[]; META=d.meta||{};
- const c=META.counts||{};
- const proof=META.is_proof_run?` <b style="color:var(--warn)">⚠ PROOF RUN — ${c.in_snapshot||DATA.length} of ${META.total_agents} agents processed so far</b>`:'';
- $('#note').innerHTML=`Source: <b>${esc(d.source||'?')}</b> · ${esc(d.label||'')}${proof} `+
- (c.verified!=null?`— <span class="verified">${c.verified} verified</span>, ${c.probable||0} probable, ${c.unmatched||0} unmatched, ${c.errors||0} errors. ${esc(META.cost||'')}`:'');
+ if(!aLoaded){
+ let d; try{ d=await (await fetch('/api/agents/licensed')).json(); }catch(e){ $('#note').textContent='Failed to load /api/agents/licensed'; return; }
+ DATA=d.agents||[]; META=d.meta||{};
+ const c=META.counts||{};
+ const proof=META.is_proof_run?` <b style="color:var(--warn)">⚠ PROOF RUN — ${c.in_snapshot||DATA.length} of ${META.total_agents} agents processed so far</b>`:'';
+ AGENT_NOTE=`Source: <b>${esc(d.source||'?')}</b> · ${esc(d.label||'')}${proof} `+
+ (c.verified!=null?`— <span class="verified">${c.verified} verified</span>, ${c.probable||0} probable, ${c.unmatched||0} unmatched, ${c.errors||0} errors. ${esc(META.cost||'')}`:'');
+ aLoaded=true;
+ }
+ $('#note').innerHTML=AGENT_NOTE;
pills(); render();
}
// ── Brokers view (rolled up from /api/brokers/licensed — the "broker database") ──
-let mode='agents', BROKERS=[], bLoaded=false, bSort='agents';
+let mode='agents', BROKERS=[], bLoaded=false, bSort=localStorage.getItem('laBSort')||'agents';
const AGENT_SORTS=[['name','Name A→Z'],['status','Status'],['type','License type'],['exp','Expiration (soonest)'],['brokerage','Brokerage'],['conf','Match confidence']];
const BROKER_SORTS=[['agents','Agent count (most)'],['verified','Verified (most)'],['name','Broker A→Z']];
function fillSort(opts,cur){ $('#sortsel').innerHTML=opts.map(([v,l])=>`<option value="${v}">Sort: ${l}</option>`).join(''); $('#sortsel').value=cur; }
@@ -158,7 +163,8 @@ async function loadBrokers(){
let d; try{ d=await (await fetch('/api/brokers/licensed')).json(); }catch(e){ $('#note').textContent='Failed to load /api/brokers/licensed'; return; }
BROKERS=d.brokers||[]; bLoaded=true;
const m=d.meta||{};
- $('#note').innerHTML=`Source: <b>${esc(d.source||'?')}</b> · ${esc(d.label||'')} — <b>${m.broker_count||BROKERS.length}</b> distinct brokers ${esc(m.cost||'')}`;
+ $('#note').innerHTML=`Source: <b>${esc(d.source||'?')}</b> · ${esc(d.label||'')} — <b>${m.broker_count||BROKERS.length}</b> supervising brokers (derived from the agents' responsible-broker field). ${esc(m.cost||'')}`+
+ ` <span style="color:var(--mut)">Broker-licensed individuals in the set who supervise no one here appear under <b>Agents → Brokers</b> pill.</span>`;
}
function brokerCard(b){
const total=b.agent_count||0;
@@ -197,7 +203,7 @@ document.querySelectorAll('.mode').forEach(x=>x.onclick=()=>setMode(x.dataset.m)
// controls
$('#q').oninput=e=>{q=e.target.value.toLowerCase().trim();draw();};
-$('#sortsel').onchange=e=>{ if(mode==='brokers'){bSort=e.target.value;} else {sortKey=e.target.value;localStorage.setItem('laSort',sortKey);} draw();};
+$('#sortsel').onchange=e=>{ if(mode==='brokers'){bSort=e.target.value;localStorage.setItem('laBSort',bSort);} else {sortKey=e.target.value;localStorage.setItem('laSort',sortKey);} draw();};
$('#density').oninput=e=>{document.documentElement.style.setProperty('--cw',e.target.value+'px');localStorage.setItem('laDens',e.target.value);};
// restore persisted
(()=>{const s=localStorage.getItem('laSort'); if(s){sortKey=s;$('#sortsel').value=s;}
diff --git a/scripts/serve.js b/scripts/serve.js
index 7bdf59a..75bdc48 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -254,10 +254,12 @@ app.get('/api/agents/licensed', (req, res) => {
// name; each broker carries its supervised-agent count + license-verification breakdown.
const BROKERS_LABEL = 'Brokers derived from the CA DRE responsible-broker field of the licensed-agent set — supervising broker + how many verified/probable/unmatched agents report to each ($0, govt data).';
// The DRE responsible_broker field is "NAME <street#> ADDRESS CITY, CA <zip>". Cut from the
-// first street-number run through the trailing state — CA addresses reliably end in "…, CA".
-// This is robust to numbered streets ("8TH ST") and directionals ("W GLENOAKS") that a
-// caps-only pattern misses, while names without a ", CA" address (e.g. "Century 21 Peak") stay whole.
-const cleanBrokerName = s => String(s || '').replace(/\s{2,}.*$/, '').replace(/\s\d+\s.*,\s*CA\b.*$/, '').trim();
+// first street-number run through the trailing state — CA addresses reliably end in "…, CA" —
+// THEN collapse any remaining double-space tail. Order matters: strip the address first so a
+// stray double-space inside the address can't truncate the name before the address is removed.
+// Handles numbered streets ("8TH ST") and directionals ("W GLENOAKS") on the current snapshot;
+// names without a ", CA" address (e.g. "Century 21 Peak") pass through unchanged.
+const cleanBrokerName = s => String(s || '').replace(/\s\d+\s.*,\s*CA\b.*$/, '').replace(/\s{2,}.*$/, '').trim();
app.get('/api/brokers/licensed', (req, res) => {
try {
const { meta = {}, agents = [] } = JSON.parse(fs.readFileSync(path.join(ROOT, 'data', 'licensed-agents.json'), 'utf8'));
← 93c2642 CRCP: broker-level rollup view — /api/brokers/licensed + Age
·
back to Commercialrealestate
·
auto-save: 2026-07-11T00:10:56 (1 files) — data/condos-redfi 2bb63e5 →