[object Object]

← back to Nationalrealestate

usreal: surface residential brokers — dedicated /residential-brokers.html desk (forces asset_class=residential) + asset_class filter on brokers.html + nav links (TK-10590, residential-IN counterpart to RENTV commercial-OUT)

6e7676caadb4b381a0c9e14462fb7723bd254333 · 2026-08-15 08:07:18 -0700 · Steve Abrams

Files touched

Diff

commit 6e7676caadb4b381a0c9e14462fb7723bd254333
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 15 08:07:18 2026 -0700

    usreal: surface residential brokers — dedicated /residential-brokers.html desk (forces asset_class=residential) + asset_class filter on brokers.html + nav links (TK-10590, residential-IN counterpart to RENTV commercial-OUT)
---
 public/brokers.html             |  13 ++++-
 public/commercial-brokers.html  |   3 +-
 public/residential-brokers.html | 110 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 123 insertions(+), 3 deletions(-)

diff --git a/public/brokers.html b/public/brokers.html
index 6d5b7f3..2d6b3a2 100644
--- a/public/brokers.html
+++ b/public/brokers.html
@@ -62,7 +62,8 @@
   <nav>
     <a href="/">Map</a>
     <a href="/markets.html">Markets</a><a href="/commercial.html">Commercial</a>
-    <a href="/brokers.html" class="active">Brokers</a>
+    <a href="/brokers.html" class="active">All Brokers</a>
+    <a href="/residential-brokers.html">Residential</a>
     <a href="/sources.html">Sources</a>
     <a href="/watchlist.html">Watchlist</a>
     <a href="/admin.html">Admin</a>
@@ -78,6 +79,11 @@
     <option value="inactive">Inactive</option>
     <option value="expired application">Expired application</option>
   </select>
+  <select id="srvAssetClass" title="Commercial / residential filter">
+    <option value="">All types</option>
+    <option value="residential">Residential</option>
+    <option value="commercial">Commercial</option>
+  </select>
   <span id="pager">
     <button id="pgPrev">‹ Prev</button>
     <span id="pgInfo">—</span>
@@ -95,7 +101,7 @@
 const PAGE = 500;
 const LS = { get(k){ try { return localStorage.getItem(k); } catch { return null; } },
              set(k,v){ try { localStorage.setItem(k,v); } catch {} } };
-const S = { state: LS.get('usreBrk.state') || '', q: '', firm: '', status: LS.get('usreBrk.status') || '', offset: 0, total: 0 };
+const S = { state: LS.get('usreBrk.state') || '', q: '', firm: '', status: LS.get('usreBrk.status') || '', assetClass: LS.get('usreBrk.assetClass') || '', offset: 0, total: 0 };
 
 const FIELDS = [
   { k: 'license_no',     l: 'License #', def: 1, col: 1 },
@@ -137,6 +143,7 @@ async function loadPage() {
   if (S.q) p.set('q', S.q);
   if (S.firm) p.set('firm', S.firm);
   if (S.status) p.set('status', S.status);
+  if (S.assetClass) p.set('asset_class', S.assetClass);
   p.set('limit', PAGE); p.set('offset', S.offset);
   const d = await fetch('/api/brokers?' + p).then(r => r.json());
   S.total = d.total || 0;
@@ -275,6 +282,8 @@ document.getElementById('srvQ').addEventListener('input', deb(e => { S.q = e.tar
 document.getElementById('srvFirm').addEventListener('input', deb(e => { S.firm = e.target.value.trim(); S.offset = 0; loadPage(); }, 350));
 { const el = document.getElementById('srvStatus'); el.value = S.status;
   el.addEventListener('change', e => { S.status = e.target.value; LS.set('usreBrk.status', S.status); S.offset = 0; loadPage(); }); }
+{ const el = document.getElementById('srvAssetClass'); if (el) { el.value = S.assetClass;
+  el.addEventListener('change', e => { S.assetClass = e.target.value; LS.set('usreBrk.assetClass', S.assetClass); S.offset = 0; loadPage(); }); } }
 document.getElementById('pgPrev').addEventListener('click', () => { S.offset = Math.max(S.offset - PAGE, 0); loadPage(); });
 document.getElementById('pgNext').addEventListener('click', () => { S.offset += PAGE; loadPage(); });
 
diff --git a/public/commercial-brokers.html b/public/commercial-brokers.html
index 70a3de2..d027874 100644
--- a/public/commercial-brokers.html
+++ b/public/commercial-brokers.html
@@ -42,7 +42,8 @@
     <a href="/">Map</a><a href="/markets.html">Markets</a>
     <a href="/commercial.html">Commercial</a>
     <a href="/commercial-feed.html">Feed</a>
-    <a href="/commercial-brokers.html" class="active">Brokers</a>
+    <a href="/commercial-brokers.html" class="active">Commercial Brokers</a>
+    <a href="/residential-brokers.html">Residential Brokers</a>
   </nav>
 </div>
 <main>
diff --git a/public/residential-brokers.html b/public/residential-brokers.html
new file mode 100644
index 0000000..77ec503
--- /dev/null
+++ b/public/residential-brokers.html
@@ -0,0 +1,110 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%230a0d13'/%3E%3Ctext x='8' y='12' font-size='10' text-anchor='middle' fill='%23c8a24b'%3ERE%3C/text%3E%3C/svg%3E">
+<title>USRealEstate — Residential Brokers &amp; Agents</title>
+<style>
+  :root{--bg:#0a0d13;--panel:#11151d;--line:#222936;--fg:#e8ecf2;--muted:#8a93a3;--gold:#c8a24b;}
+  *{box-sizing:border-box;}
+  html,body{margin:0;background:var(--bg);color:var(--fg);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;}
+  #topbar{display:flex;align-items:center;gap:14px;padding:10px 18px;background:rgba(17,21,29,.94);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20;backdrop-filter:blur(6px);}
+  .brand{font-size:16px;font-weight:700;white-space:nowrap;} .brand b{color:var(--gold);}
+  nav{display:flex;gap:14px;font-size:13px;} nav a{color:var(--muted);text-decoration:none;} nav a:hover,nav a.active{color:var(--gold);}
+  main{padding:16px 18px 60px;max-width:1200px;margin:0 auto;}
+  .lede{color:var(--muted);font-size:13px;line-height:1.5;margin:6px 0 14px;}
+  #controls{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-bottom:14px;}
+  #tabs{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden;}
+  #tabs button{background:var(--panel);color:var(--muted);border:0;border-right:1px solid var(--line);padding:7px 14px;font-size:13px;cursor:pointer;}
+  #tabs button:last-child{border-right:0;} #tabs button.active{background:var(--gold);color:var(--bg);font-weight:700;}
+  input,select{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:13px;}
+  #count{font-size:12px;color:var(--muted);margin-left:auto;}
+  #grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:10px;}
+  .card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:13px;}
+  .card.firm{cursor:pointer;transition:border-color .12s;} .card.firm:hover{border-color:var(--gold);}
+  .card .n{font-size:15px;font-weight:600;line-height:1.25;}
+  .card .m{font-size:12px;color:var(--muted);margin-top:6px;display:flex;flex-wrap:wrap;gap:10px;}
+  .agents{color:var(--gold);font-weight:700;}
+  .lt{font-size:10px;letter-spacing:.05em;text-transform:uppercase;color:var(--gold);border:1px solid var(--line);border-radius:5px;padding:1px 6px;}
+  .roster{margin-top:9px;padding-top:9px;border-top:1px solid var(--line);font-size:12px;color:#c3ccd8;display:none;}
+  .roster div{padding:2px 0;}
+  #loading{padding:40px;text-align:center;color:var(--muted);}
+</style>
+<link rel="stylesheet" href="/nav-drawer.css">
+<link rel="stylesheet" href="/geo-search.css">
+  <link rel="stylesheet" href="/nav-agent/nav-agent.css"><!-- nav-agent -->
+</head>
+<body>
+<div id="topbar">
+  <div class="brand"><b>USRealEstate</b> — Residential Brokers &amp; Agents</div>
+  <nav>
+    <a href="/">Map</a><a href="/markets.html">Markets</a>
+    <a href="/commercial-brokers.html">Commercial Brokers</a>
+    <a href="/residential-brokers.html" class="active">Residential Brokers</a>
+    <a href="/brokers.html">All Brokers</a>
+  </nav>
+</div>
+<main>
+  <p class="lede">Residential real estate firms &amp; licensed agents across the region, from the California DRE
+    current-licensee registry. This directory is the residential counterpart to the commercial desk — the
+    ~204k residential-classified agents that don't appear on the commercial (RENTV) surface. Firms rank by
+    licensed-agent count.</p>
+  <div id="controls">
+    <div id="tabs"><button data-t="firms" class="active">Firms</button><button data-t="brokers">Agents</button></div>
+    <select id="city">
+      <option value="LOS ANGELES">Los Angeles</option><option value="BEVERLY HILLS">Beverly Hills</option>
+      <option value="SANTA MONICA">Santa Monica</option><option value="LONG BEACH">Long Beach</option>
+      <option value="BURBANK">Burbank</option><option value="GLENDALE">Glendale</option>
+      <option value="PASADENA">Pasadena</option><option value="">All CA</option>
+    </select>
+    <input id="q" placeholder="Search name / firm / license…" style="width:220px">
+    <span id="count"></span>
+  </div>
+  <div id="grid"></div>
+  <div id="loading">Loading…</div>
+</main>
+<script>
+const $=s=>document.querySelector(s);
+const state={tab:'firms',city:'LOS ANGELES',q:''};
+async function j(u){const r=await fetch(u);return r.json();}
+// Residential desk — force asset_class=residential on every registry call so this surface only ever
+// shows residential firms/agents (the mirror of RENTV forcing commercial). TK-10590.
+function params(){const p=new URLSearchParams({state:'CA',limit:200,asset_class:'residential'});if(state.city)p.set('city',state.city);if(state.q)p.set('q',state.q);return p;}
+async function loadFirms(){
+  const d=await j('/api/firms?'+params());
+  $('#count').textContent=d.total.toLocaleString()+' firms';
+  $('#grid').innerHTML=d.rows.map(f=>`<div class="card firm" data-id="${f.id}" data-name="${(f.name||'').replace(/"/g,'&quot;')}">
+    <div class="n">${f.name||'—'}</div>
+    <div class="m"><span class="agents">${f.agent_count||0} agents</span><span>${f.hq_city||''}</span>${f.license_no?`<span>DRE ${f.license_no}</span>`:''}</div>
+    <div class="roster"></div></div>`).join('');
+  document.querySelectorAll('.card.firm').forEach(c=>c.onclick=()=>toggleRoster(c));
+}
+async function toggleRoster(c){
+  const r=c.querySelector('.roster');
+  if(r.style.display==='block'){r.style.display='none';return;}
+  r.style.display='block';
+  if(r.dataset.loaded){return;}
+  r.innerHTML='loading roster…';
+  const d=await j('/api/brokers?state=CA&limit=40&asset_class=residential&firm='+encodeURIComponent(c.dataset.name));
+  r.dataset.loaded='1';
+  r.innerHTML=d.rows.slice(0,30).map(b=>`<div>${b.name} <span style="color:var(--muted)">· ${b.license_type||''} · DRE ${b.license_no||''}</span></div>`).join('')||'<div style="color:var(--muted)">no roster rows</div>';
+}
+async function loadBrokers(){
+  const d=await j('/api/brokers?'+params());
+  $('#count').textContent=d.total.toLocaleString()+' agents';
+  $('#grid').innerHTML=d.rows.map(b=>`<div class="card">
+    <div class="n">${b.name||'—'}</div>
+    <div class="m"><span class="lt">${b.license_type||''}</span><span>${b.firm_name||'—'}</span><span>${b.city||''}</span>${b.license_no?`<span>DRE ${b.license_no}</span>`:''}</div></div>`).join('');
+}
+async function load(){$('#loading').style.display='block';state.tab==='firms'?await loadFirms():await loadBrokers();$('#loading').style.display='none';}
+$('#tabs').querySelectorAll('button').forEach(b=>b.onclick=()=>{$('#tabs').querySelectorAll('button').forEach(x=>x.classList.remove('active'));b.classList.add('active');state.tab=b.dataset.t;load();});
+$('#city').onchange=()=>{state.city=$('#city').value;load();};
+$('#q').oninput=()=>{state.q=$('#q').value;clearTimeout(window._t);window._t=setTimeout(load,250);};
+load();
+</script>
+<script src="/nav-drawer.js"></script>
+<script src="/geo-search.js"></script>
+  <script src="/nav-agent/nav-agent.js" defer></script><!-- nav-agent -->
+</body>
+</html>

← fd66310 auto-data-snapshot: 2026-08-14T12:24:03 (2 data files) — dat  ·  back to Nationalrealestate  ·  usreal brokers: honest count (show 'first 200 of N' not full 5a16e24 →