[object Object]

← back to Rentv

feat(firm): dedicated firm PROFILE view on /firm?name= — header + KPI strip (total volume, deals, markets, top asset class, largest deal) + that firm's deals as full cards, with a back link. This completes the 'Broker/Firm profile page' vision (REPOINT #1); index leaderboard unchanged. Client-side, reuses build() data, verified computation + both modes 200.

8d3f54a368c70bdcefcff0c92ca53de4e7a0de22 · 2026-08-06 15:32:29 -0700 · Steve

Files touched

Diff

commit 8d3f54a368c70bdcefcff0c92ca53de4e7a0de22
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 15:32:29 2026 -0700

    feat(firm): dedicated firm PROFILE view on /firm?name= — header + KPI strip (total volume, deals, markets, top asset class, largest deal) + that firm's deals as full cards, with a back link. This completes the 'Broker/Firm profile page' vision (REPOINT #1); index leaderboard unchanged. Client-side, reuses build() data, verified computation + both modes 200.
---
 public/firm.html | 45 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 39 insertions(+), 6 deletions(-)

diff --git a/public/firm.html b/public/firm.html
index 29e8b9aa..25b2ffb4 100644
--- a/public/firm.html
+++ b/public/firm.html
@@ -64,6 +64,20 @@
   .deal:hover h4{color:var(--red)}
   .note{color:var(--sub);font-size:12px;padding:6px 2px 0}
   .empty{padding:40px 0;text-align:center;color:var(--sub)}
+  /* per-firm profile mode (?name=) */
+  .back{display:inline-block;margin:6px 0 2px;color:var(--sub);font-size:12.5px;font-weight:700}
+  .back:hover{color:var(--red)}
+  .kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:14px 0 6px}
+  .kpi{background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px 16px}
+  .kpi .k{font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--sub)}
+  .kpi .v{font-family:'Playfair Display',serif;font-size:24px;font-weight:800;margin-top:3px}
+  .kpi .v.red{color:var(--red)}
+  .plist{padding:8px 0 60px}
+  .pcard{display:grid;grid-template-columns:1fr auto;gap:12px;align-items:center;background:#fff;border:1px solid var(--line);border-radius:12px;padding:15px 18px;margin-bottom:10px}
+  .pcard:hover{border-color:var(--red)}.pcard:hover h4{color:var(--red)}
+  .pcard h4{font:700 15.5px/1.3 'Inter'}
+  .pcard .m{color:var(--sub);font-size:12px;margin-top:3px}
+  .pcard .amt{font-family:'Playfair Display',serif;font-weight:800;font-size:18px;color:var(--red);white-space:nowrap}
 </style></head><body>
 
 <header class="top">
@@ -155,18 +169,37 @@ function render(){
 function setSort(s){ sort=s; $('sVol').classList.toggle('on',s==='vol'); $('sCnt').classList.toggle('on',s==='cnt'); render(); }
 $('sVol').onclick=()=>setSort('vol'); $('sCnt').onclick=()=>setSort('cnt');
 
+// Per-firm profile view (?name=<firm>): a dedicated header + KPI strip + that firm's deals as full cards,
+// distinct from the leaderboard index. This IS the "Broker/Firm profile page" (REPOINT feature #1).
+function renderProfile(g){
+  document.querySelector('.sortbar').style.display='none';
+  $('note').textContent='';
+  $('h1').textContent=g.name;
+  $('blurb').textContent=`Every tracked deal RENTV has ${g.name} in — ${g.count} deal${g.count===1?'':'s'} across ${g.nMetro} market${g.nMetro===1?'':'s'}.`;
+  const largest=g.deals.reduce((a,b)=>(b.amount||0)>(a.amount||0)?b:a, g.deals[0]);
+  const kpis=[
+    ['Total volume', g.vol?usd(g.vol):'—', true],
+    ['Deals', String(g.count), false],
+    ['Markets', String(g.nMetro), false],
+    ['Top asset class', g.top?g.top[0]:'—', false],
+    ['Largest deal', (largest&&largest.amount)?usd(largest.amount):'—', true],
+  ];
+  $('metros').innerHTML =
+    `<a class="back" href="/firm">← All dealmakers</a>`+
+    `<div class="kpis">${kpis.map(k=>`<div class="kpi"><div class="k">${esc(k[0])}</div><div class="v${k[2]?' red':''}">${esc(k[1])}</div></div>`).join('')}</div>`+
+    `<div class="plist">${g.deals.map(d=>`<a class="pcard" href="${d.id?('/news/'+encodeURIComponent(d.id)):'/deals'}"><div><h4>${esc(d.title)}</h4><div class="m">${[d.property_type,d.txn_type,[d.city,d.state].filter(Boolean).join(', '),d.size_label].filter(Boolean).map(esc).join(' · ')}</div></div><div class="amt">${esc(d.amount_label||(d.amount?usd(d.amount):'—'))}</div></a>`).join('')}</div>`;
+}
+
 (async function(){
   let deals=[]; try{ deals=((await (await fetch('/api/deals?limit=200')).json()).deals||[]); }catch(e){ $('metros').innerHTML='<div class="empty">Deal data unavailable.</div>'; return; }
   FIRMS=build(deals);
   const p=new URLSearchParams(location.search);
   if(p.get('sort')==='cnt')sort='cnt';
-  setSort(sort);
-  // Deep-link: /firm?name=<firm> or ?open=<firm> opens+scrolls that firm (from a deal-card acquirer link).
+  // Deep-link: /firm?name=<firm> (from a deal-card acquirer link) → dedicated firm profile; else leaderboard.
   const openF=p.get('name')||p.get('open');
-  if(openF){
-    const el=$('metros').querySelector(`.metro[data-k="${CSS.escape(openF)}"]`);
-    if(el){ el.classList.add('open'); el.scrollIntoView({block:'center'}); $('h1').textContent=openF; $('blurb').textContent=`Every tracked deal RENTV has ${openF} in.`; }
-  }
+  const firm=openF && FIRMS.find(f=>f.name.toLowerCase()===String(openF).toLowerCase());
+  if(firm){ renderProfile(firm); }
+  else { setSort(sort); if(openF){ $('note').textContent=`No tracked deals attributed to "${openF}" yet — acquirer names populate as new deals are crawled.`; } }
 })();
 </script>
 </body></html>

← 04e22ebe consulting portal: revert stray theme-aware --dim/--panel in  ·  back to Rentv  ·  auto-data-snapshot: 2026-08-06T15:32:11 (7 data files) — dat b63cb339 →