← back to Commercialrealestate
condos viewer: city + firm datums drill to filtered view (no dead-end text rule) — reuse F.cities/F.firms facet state, stopPropagation so it doesn't open modal
aa830f95f5d5f1ad3be7746e2ca883c4e68c485f · 2026-08-03 09:48:38 -0700 · Steve Abrams
Files touched
Diff
commit aa830f95f5d5f1ad3be7746e2ca883c4e68c485f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Aug 3 09:48:38 2026 -0700
condos viewer: city + firm datums drill to filtered view (no dead-end text rule) — reuse F.cities/F.firms facet state, stopPropagation so it doesn't open modal
---
public/condos.html | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/public/condos.html b/public/condos.html
index 8f79531..f7d1179 100644
--- a/public/condos.html
+++ b/public/condos.html
@@ -40,6 +40,8 @@
.viewseg button:last-child{border-right:0;} .viewseg button.active{background:#1f6feb;color:#fff;}
.gridwrap{padding:24px;}
.grid{display:grid;gap:22px;grid-template-columns:repeat(var(--cols,3),minmax(0,1fr));}
+ .drill{color:var(--blue,#4aa3ff);cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px;}
+ .drill:hover{text-decoration:underline;}
.grid.listhost{display:block;}
.condo{background:var(--card);border:2px solid #fff;border-radius:12px;padding:20px;display:flex;flex-direction:column;gap:6px;}
.condo .name{font-weight:600;font-size:14px;}
@@ -222,6 +224,14 @@ function reinitResize(){
function fmtDateTime(v){ if(v==null||v==='') return ''; const d=new Date(v); if(isNaN(d)) return ''; return d.toLocaleString(undefined,{year:'numeric',month:'short',day:'numeric',hour:'numeric',minute:'2-digit'}); }
function isoOf(v){ const d=new Date(v); return isNaN(d)?'':d.toISOString(); }
function dateTimeSpan(v){ const s=fmtDateTime(v); return s?`<span title="${esc(isoOf(v))}">${esc(s)}</span>`:''; }
+// ── drill-down: no datum is dead text (Steve HARD rule). A city/firm value on a card or
+// list cell is a clickable link that filters the grid to that value — reusing the existing
+// F.cities / F.firms facet state so the rail chip lights up too. ──
+function drillSpan(kind,val){ if(val==null||val==='') return ''; return `<span class="drill" data-drill="${esc(kind)}" data-val="${esc(val)}" title="Filter to ${esc(val)}">${esc(val)}</span>`; }
+function applyDrill(kind,val){ if(val==null||val==='') return;
+ if(kind==='city'){ F.cities.add(val); } else if(kind==='firm'){ F.firms.add(val); } else return;
+ buildRail(); apply();
+}
function fval(c,f){ return f.calc?f.calc(c):c[f.k]; }
function fcell(c,f){ const v=fval(c,f);
if(f.badge) return statusBadge(c.warrantable_status||c.warrant_signal||'—');
@@ -229,6 +239,8 @@ function fcell(c,f){ const v=fval(c,f);
if(f.money) return '$'+(+v).toLocaleString()+(f.suf||'');
if(f.num) return (+v).toLocaleString();
if(f.date){ return dateTimeSpan(v)||'—'; }
+ if(f.k==='firm_name') return drillSpan('firm',v);
+ if(f.k==='city') return drillSpan('city',v);
return esc(v)+(f.suf||'');
}
@@ -320,7 +332,7 @@ function dbCards(rows){
const vis=orderedFields().filter(f=>fvis(f.k)&&!CARD_SKIP.has(f.k));
return rows.map(c=>`<div class="condo">
<div class="chead"><div class="name">${esc(c.project_name||c.address||'Condo')}</div><button class="starbtn ${SHORT.has(c.id)?'on':''}" data-star="${c.id}" aria-label="${SHORT.has(c.id)?'Remove from':'Add to'} shortlist" aria-pressed="${SHORT.has(c.id)}" title="Shortlist">★</button></div>
- <div class="addr">${esc(c.address||'')}${c.city?', '+esc(c.city):''} ${esc(c.zip||'')}</div>
+ <div class="addr">${esc(c.address||'')}${c.city?', '+drillSpan('city',c.city):''} ${esc(c.zip||'')}</div>
${whenChip(c)}
${fvis('warrantable_status')?statusBadge(c.warrantable_status||c.warrant_signal):''}
${vis.map(f=>`<div class="row"><span class="k">${esc(f.l)}</span><span>${fcell(c,f)}</span></div>`).join('')}
@@ -493,6 +505,8 @@ $('#ov').addEventListener('click',e=>{ if(e.target.id==='ov') $('#ov').classList
document.addEventListener('keydown',e=>{ if(e.key==='Escape') $('#ov').classList.remove('on'); });
// row/card click opens detail (but not when the click is a star, header, or link)
$('#grid').addEventListener('click',e=>{
+ const dr=e.target.closest('[data-drill]');
+ if(dr){ e.stopPropagation(); applyDrill(dr.dataset.drill,dr.dataset.val); return; } // datum drill → filter, not modal
if(e.target.closest('[data-star]')||e.target.closest('th')||e.target.tagName==='A'||e.target.tagName==='BUTTON'||e.target.tagName==='TEXTAREA'||e.target.closest('.notewrap')) return;
const row=e.target.closest('tr[data-id]')||e.target.closest('.condo');
if(row){ let id=row.dataset.id; if(id==null){ const st=row.querySelector('[data-star]'); id=st&&st.dataset.star; } if(id!=null) openDetail(id); }
← e0227f6 condos viewer: DRY date rendering into fmtDateTime/dateTimeS
·
back to Commercialrealestate
·
condos viewer: make list-view sticky-column city drillable t 64d69b9 →