[object Object]

← back to Rentv 2026

index: honor ?cat= so each hamburger section shows its own filtered feed

214915365f26811d2941dc502a62ec4c1fed9135 · 2026-07-23 14:06:12 -0700 · Steve Abrams

Section links (/sales,/development,…) 301 to /?cat=X but index.html ignored
the param and rendered the identical homepage — every hamburger link showed the
same info. Now the client filters the live feed to the active category, shows a
section masthead with story count + All-news escape, highlights the active drawer
link, sets the title, and handles the empty-section case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 214915365f26811d2941dc502a62ec4c1fed9135
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 23 14:06:12 2026 -0700

    index: honor ?cat= so each hamburger section shows its own filtered feed
    
    Section links (/sales,/development,…) 301 to /?cat=X but index.html ignored
    the param and rendered the identical homepage — every hamburger link showed the
    same info. Now the client filters the live feed to the active category, shows a
    section masthead with story count + All-news escape, highlights the active drawer
    link, sets the title, and handles the empty-section case.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/index.html | 46 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/public/index.html b/public/index.html
index 5cffc7a5..e6242a4a 100644
--- a/public/index.html
+++ b/public/index.html
@@ -29,6 +29,16 @@
   .grp{padding:14px 18px 4px}.grp .gt{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:#6b747d;margin-bottom:6px}
   .drawer nav a{display:block;padding:9px 18px;color:#dbe1e7;font-size:14.5px;border-left:3px solid transparent}
   .drawer nav a:hover{background:#141a21;border-left-color:var(--red);color:#fff}
+  .drawer nav a.active{background:#141a21;border-left-color:var(--red);color:#fff;font-weight:700}
+  .sechat{display:none;align-items:baseline;gap:14px;flex-wrap:wrap;padding:20px 0 4px;border-bottom:1px solid #e8e4e0;margin-bottom:4px}
+  .sechat.on{display:flex}
+  .sechat h1{font-family:'Playfair Display',serif;font-size:clamp(30px,3vw,42px);font-weight:700;letter-spacing:-.01em}
+  .sechat .ct{color:var(--sub);font-size:13px;font-weight:600}
+  .sechat .all{margin-left:auto;color:var(--red);font-weight:800;font-size:12px;letter-spacing:.06em;text-transform:uppercase}
+  .sechat .all:hover{text-decoration:underline}
+  .empty{padding:60px 0 40px;text-align:center;color:var(--sub)}
+  .empty h3{font-family:'Playfair Display',serif;font-size:26px;color:var(--ink);margin-bottom:8px}
+  .empty a{color:var(--red);font-weight:800}
   .drawer .df{margin-top:auto;padding:16px 18px;border-top:1px solid #1c232b;font-size:11px;color:#6b747d}
   .datastrip{border-bottom:1px solid #e8e4e0;background:#f7f5f2}
   .datastrip .in{max-width:1240px;margin:0 auto;display:flex;gap:34px;padding:9px 22px;font-size:11px;font-weight:500;letter-spacing:.06em;color:#4a4f55;overflow:hidden;white-space:nowrap}
@@ -104,7 +114,7 @@
 
 <div class="fold">
   <div class="datastrip"><div class="in" id="datastrip"></div></div>
-  <main class="wrap"><section class="hero"><div class="lead" id="lead"></div><div class="aside" id="aside"></div></section></main>
+  <main class="wrap"><div class="sechat" id="sechat"></div><section class="hero"><div class="lead" id="lead"></div><div class="aside" id="aside"></div></section></main>
   <div class="selbar" id="selbar"></div>
   <div class="breaking"><div class="in"><div class="lbl"><span class="dot"></span> Breaking</div>
     <div class="ticker" id="ticker"><div class="fade l"></div><div class="fade r"></div><span class="paused">Paused · hover to browse</span><div class="track" id="track"></div></div></div></div>
@@ -130,19 +140,45 @@ $('dclose').onclick=closeDrawer;$('scrim').onclick=closeDrawer;
 $('user').onclick=()=>location.href='/subscribe';
 async function jget(url){try{const r=await fetch(url,{credentials:'same-origin'});if(!r.ok)return null;return await r.json();}catch(e){return null;}}
 let ALL=[];
+// Canonical section labels — the drawer/footer link to /sales, /development, … which the
+// server 301s to /?cat=Sales, /?cat=Development, …  Match the exact cat tags in the live feed.
+const SECTIONS={sales:'Sales',leases:'Leases',financing:'Financing',development:'Development',multifamily:'Multifamily',industrial:'Industrial',retail:'Retail'};
+const rawCat=(new URLSearchParams(location.search).get('cat')||'').trim();
+const CAT=rawCat?(SECTIONS[rawCat.toLowerCase()]||rawCat):''; // canonical label, or '' for the full homepage
+if(CAT){
+  document.title=CAT+' — RENTV.com Commercial Real Estate News';
+  // Highlight the matching section in the drawer so the open section is obvious.
+  document.querySelectorAll('.drawer nav a').forEach(a=>{
+    if((a.getAttribute('href')||'').toLowerCase()==='/'+CAT.toLowerCase()) a.classList.add('active');
+  });
+}
 async function load(){
   const [d,pd]=await Promise.all([jget('/api/news'),jget('/api/posts?status=published')]);
   const news=(d&&d.items)||[], orig=((pd&&pd.items)||[]).map(p=>({id:p.id,title:p.title,cat:p.cat,image:p.image,author:p.author,orig:true}));
   ALL=orig.concat(news);
-  if(!ALL.length){$('lead').innerHTML='<div class="tag">RENTV.com</div><h1>Live commercial real estate news, refreshing…</h1><div class="byl">If this persists, the wire is briefly unavailable — please reload.</div>';return;}
+  // A "section" IS the live feed filtered to one category (server-side model in server.js). When
+  // ?cat= is set we render only that slice — that's what makes each hamburger link show DIFFERENT info.
+  const view=CAT?ALL.filter(x=>x&&String(x.cat||'').toLowerCase()===CAT.toLowerCase()):ALL;
+  const sh=$('sechat');
+  if(CAT&&sh){
+    sh.innerHTML=`<h1>${esc(CAT)}</h1><span class="ct">${view.length} ${view.length===1?'story':'stories'}</span><a class="all" href="/">All news ↗</a>`;
+    sh.classList.add('on');
+  }
+  if(!view.length){
+    // Either the wire is empty (homepage) or this section has no current stories.
+    $('lead').innerHTML='';$('aside').innerHTML='';$('river').innerHTML='';$('track').innerHTML='';
+    if(CAT){$('lead').innerHTML=`<div class="empty"><h3>No current ${esc(CAT)} stories</h3><div>This section refreshes from the live wire. <a href="/">Browse all news →</a></div></div>`;}
+    else{$('lead').innerHTML='<div class="tag">RENTV.com</div><h1>Live commercial real estate news, refreshing…</h1><div class="byl">If this persists, the wire is briefly unavailable — please reload.</div>';}
+    return;
+  }
   // lead with the top story that HAS an image (never a blank-image placeholder); keep the rest in order
-  const li=ALL.findIndex(x=>x&&x.image); const lead=li>=0?ALL[li]:ALL[0];
-  const rest=ALL.filter((_,i)=>i!==(li>=0?li:0));
+  const li=view.findIndex(x=>x&&x.image); const lead=li>=0?view[li]:view[0];
+  const rest=view.filter((_,i)=>i!==(li>=0?li:0));
   const l=lead;
   $('lead').innerHTML=`<div class="im"${bg(l)}></div><span class="tag">${esc(l.cat||'RENTV')}${l.orig?'<span class="chipo">Original</span>':''}</span><h1>${esc(l.title)}</h1><div class="byl">${l.orig?'By '+esc(l.author||'RENTV Staff')+' · RENTV Original':'By RENTV Staff · live from rentv.com'}</div>`;
   $('lead').style.cursor='pointer';$('lead').onclick=()=>location.href=LK(l);
   $('aside').innerHTML=rest.slice(0,4).map(n=>`<a href="${LK(n)}"><div class="im"${bg(n)}></div><div><span class="tag" style="color:var(--red)">${esc(n.cat||'')}</span><h4>${esc(n.title)}</h4></div></a>`).join('');
-  const items=ALL.slice(0,16), mk=n=>`<a class="it" data-id="${esc(n.id)}" data-cat="${esc(n.cat||'')}" data-title="${esc(n.title)}" data-href="${LK(n)}"><span class="c">${esc(n.cat||'')}</span>${esc(n.title)}</a>`;
+  const items=view.slice(0,16), mk=n=>`<a class="it" data-id="${esc(n.id)}" data-cat="${esc(n.cat||'')}" data-title="${esc(n.title)}" data-href="${LK(n)}"><span class="c">${esc(n.cat||'')}</span>${esc(n.title)}</a>`;
   $('track').innerHTML=items.map(mk).join('')+items.map(mk).join('');
   const selbar=$('selbar');
   $('track').querySelectorAll('.it').forEach(a=>a.addEventListener('click',e=>{e.preventDefault();

← 65b2eece auto-save: 2026-07-23T13:51:43 (3 files) — data/markets.json  ·  back to Rentv 2026  ·  Localize all rentv.com photos + drop link-outs (serve everyt f934418a →