[object Object]

← back to Commercialrealestate

CRCP agent-profile: agents now show their real listings across ALL data paths — commercial (broker_listing), condo (broker_condo edge + condoBrokers() overlay by name/co-agent), and an overlay-only profile for residential agents not in the broker graph; fixes empty agent pages found in the 5x click-through test

0b756296d5701686e48c560b001165ab253ee802 · 2026-08-19 10:15:13 -0700 · Steve Abrams

Files touched

Diff

commit 0b756296d5701686e48c560b001165ab253ee802
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 19 10:15:13 2026 -0700

    CRCP agent-profile: agents now show their real listings across ALL data paths — commercial (broker_listing), condo (broker_condo edge + condoBrokers() overlay by name/co-agent), and an overlay-only profile for residential agents not in the broker graph; fixes empty agent pages found in the 5x click-through test
---
 scripts/serve.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 50 insertions(+), 3 deletions(-)

diff --git a/scripts/serve.js b/scripts/serve.js
index 747c11c..7489d07 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -350,13 +350,60 @@ app.get('/api/agent-profile', async (req, res) => {
          FROM broker b LEFT JOIN firm f ON f.id = b.firm_id`;
     const b = (await brokerdb.pool.query(
       id ? `${sel} WHERE b.id = $1 LIMIT 1`
-         : `${sel} WHERE lower(b.name) = lower($1) ORDER BY (b.phone IS NOT NULL OR b.email IS NOT NULL) DESC NULLS LAST LIMIT 1`,
+         : `${sel} WHERE lower(b.name) = lower($1)
+             ORDER BY ((SELECT count(*) FROM broker_listing bl WHERE bl.broker_id=b.id)
+                     + (SELECT count(*) FROM broker_condo bc WHERE bc.broker_id=b.id)) DESC,
+                      (b.phone IS NOT NULL OR b.email IS NOT NULL) DESC NULLS LAST LIMIT 1`,
       [id || name])).rows[0];
-    if (!b) return res.json({ name, firm: null, listings: [], found: false });
-    const listings = (await brokerdb.pool.query(
+    if (!b) {
+      // The agent may exist ONLY in the condo overlay (a residential listing agent not in the broker
+      // graph). Recover their profile + condos from condoBrokers() so their page isn't empty.
+      const ov = condoBrokers(); const nlc = name.toLowerCase(); let contact = null;
+      const ids = name ? Object.keys(ov).filter(cid => { const e = ov[cid] || {};
+        const ae = Array.isArray(e.agents) ? e.agents.find(a => a && String(a.name || '').toLowerCase() === nlc) : null;
+        const hit = (e.broker_name && String(e.broker_name).toLowerCase() === nlc) || !!ae;
+        if (hit && !contact) contact = { firm: (ae && ae.firm) || e.firm_name || null, dre: (ae && ae.dre) || e.broker_dre || null, phone: e.agent_phone || null, email: e.agent_email || null };
+        return hit; }) : [];
+      if (!ids.length) return res.json({ name, firm: null, listings: [], found: false });
+      const oc = (await brokerdb.pool.query(
+        `SELECT address, city, zip, 'Condo' AS type, price, NULL::int AS units, NULL::numeric AS cap_rate
+           FROM condo_card WHERE id::text = ANY($1::text[]) ORDER BY price DESC NULLS LAST`, [ids]).catch(() => ({ rows: [] }))).rows;
+      return res.json({ found: true, id: null, name, firm: contact && contact.firm, phone: contact && contact.phone,
+        email: contact && contact.email, title: null, agent_type: 'residential', license: contact && contact.dre, office: null, linkedin: null,
+        listings: oc, count: oc.length, closed: [], closed_count: 0, firm_listings: [], firm_count: 0,
+        total_value: oc.reduce((s, l) => s + Number(l.price || 0), 0), closed_value: 0,
+        cities: [...new Set(oc.map(l => l.city).filter(Boolean))], source: 'condo-overlay' });
+    }
+    const commercial = (await brokerdb.pool.query(
       `SELECT l.address, l.city, l.zip, l.type, l.price, l.units, l.cap_rate
          FROM broker_listing bl JOIN listing l ON l.id = bl.listing_id
         WHERE bl.broker_id = $1 ORDER BY l.price DESC NULLS LAST`, [b.id])).rows;
+    // Condo listings — a residential agent's book lives in broker_condo → condo (NOT broker_listing),
+    // so an agent surfaced from condos.html would otherwise show an empty page. Mirrors /api/firm.
+    const condoRows = (await brokerdb.pool.query(
+      `SELECT c.address, c.city, NULL::text AS zip, 'Condo' AS type, c.price, NULL::int AS units, NULL::numeric AS cap_rate
+         FROM broker_condo bc JOIN condo c ON c.id = bc.condo_id
+        WHERE bc.broker_id = $1 ORDER BY c.price DESC NULLS LAST`, [b.id]).catch(() => ({ rows: [] }))).rows;
+    // Name-match fallback: broker↔listing edges are sparse; a condo often stores the agent only as a
+    // broker_name STRING (no broker_condo row). So also pull condos where broker_name matches this agent,
+    // so a named-but-unlinked agent (seen on condos.html) still shows their book. Deduped by address+city.
+    // The condo↔agent linkage lives in the id-keyed condoBrokers() overlay (condo_card.broker_name is
+    // overwritten by it), so a name-column match misses co-listing agents. Scan the overlay by name to
+    // recover EVERY condo this agent is on (primary or co-agent), then fetch those cards.
+    let overlayCondos = [];
+    try {
+      const ov = condoBrokers(); const nlc = String(b.name || '').toLowerCase();
+      const ids = b.name ? Object.keys(ov).filter(cid => { const e = ov[cid] || {};
+        return (e.broker_name && String(e.broker_name).toLowerCase() === nlc)
+            || (Array.isArray(e.agents) && e.agents.some(a => a && String(a.name || '').toLowerCase() === nlc)); }) : [];
+      if (ids.length) overlayCondos = (await brokerdb.pool.query(
+        `SELECT address, city, zip, 'Condo' AS type, price, NULL::int AS units, NULL::numeric AS cap_rate
+           FROM condo_card WHERE id::text = ANY($1::text[]) ORDER BY price DESC NULLS LAST`, [ids]).catch(() => ({ rows: [] }))).rows;
+    } catch (_) {}
+    const seen = new Set();
+    const listings = commercial.concat(condoRows, overlayCondos)
+      .filter(l => { const k = (l.address || '') + '|' + (l.city || ''); if (seen.has(k)) return false; seen.add(k); return true; })
+      .sort((x, y) => Number(y.price || 0) - Number(x.price || 0));
     // Past / closed (sold) listings — the agent's track record, mirrors /api/broker.
     const closed = (await brokerdb.pool.query(
       `SELECT address, city, sold_price, sold_date, type, source

← 58c2ad4 auto-data-snapshot: 2026-08-19T10:08:05 (1 data files) — dat  ·  back to Commercialrealestate  ·  CRCP: backfill broker contacts for Lyon Stahl/WESTMAC/BuildO fb68fcb →