← back to Rentv
Audience perf: precompute per-contact search index once on load (was JSON.stringify over 2,162 contacts per keystroke) — Cody's queued follow-up
fc57490b94e73382b7386f94e59aadeb7080d72e · 2026-08-05 13:53:57 -0700 · Steve Abrams
Files touched
Diff
commit fc57490b94e73382b7386f94e59aadeb7080d72e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 5 13:53:57 2026 -0700
Audience perf: precompute per-contact search index once on load (was JSON.stringify over 2,162 contacts per keystroke) — Cody's queued follow-up
---
public/audience.html | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/public/audience.html b/public/audience.html
index 86719e1f..e75b6d42 100644
--- a/public/audience.html
+++ b/public/audience.html
@@ -132,6 +132,9 @@ $('user').onclick=()=>location.href='/';
async function jget(u,fb){try{const r=await fetch(u,{credentials:'same-origin'});if(!r.ok)return fb;return await r.json();}catch(e){return fb;}}
(async()=>{
A_.data=await jget('/api/audience',{stats:{},segments:[],contacts:[]});
+ // Precompute a lean lowercase search string per contact ONCE (was JSON.stringify per keystroke
+ // over 2,162 contacts — a real perf drag; Cody-flagged). Search now filters on this index.
+ (A_.data.contacts||[]).forEach(c=>{c._q=[c.name,c.email,c.phone,c.firm,c.title,c.interest,c.persona,c.source,(c.segments||[]).join(' ')].filter(Boolean).join(' ').toLowerCase();});
const st=A_.data.stats||{};
$('cP').textContent=A_.data.contacts.length;$('cS').textContent=A_.data.segments.length;$('cC').textContent=A_.data.contacts.length;
$('fresh').textContent=(st.crm||0).toLocaleString()+' CRM contacts · '+(st.newsletter||0)+' newsletter · '+(st.subleaseLinked||0)+' sublease';
@@ -156,7 +159,7 @@ function filteredContacts(term){
let r=A_.data.contacts.slice();
if(A_.filterSeg)r=r.filter(c=>c.segments.includes(A_.filterSeg));
const v=(term||'').trim().toLowerCase();
- if(v)r=r.filter(c=>JSON.stringify(c).toLowerCase().includes(v));
+ if(v)r=r.filter(c=>(c._q||"").includes(v));
return sortContacts(r);
}
// ── 1. Clients & Prospects (master-detail) ──
@@ -253,7 +256,7 @@ function profiles(){
<div class="h3">Timeline</div><div class="pill">🕓 Added ${esc(fmtWhen(c.created_at))}</div>`;
$('cdetail').querySelectorAll('.seg-chip').forEach(ch=>ch.onclick=()=>{A_.filterSeg=ch.dataset.k;A_.selP=0;switchView('prospects');prospects();});
}
- function draw(){let r=all;const v=($('cq').value||'').trim().toLowerCase();if(v)r=all.filter(c=>JSON.stringify(c).toLowerCase().includes(v));
+ function draw(){let r=all;const v=($('cq').value||'').trim().toLowerCase();if(v)r=all.filter(c=>(c._q||"").includes(v));
$('ccount').textContent=`${r.length} contact${r.length===1?'':'s'}`;
$('crows').innerHTML=r.map((c,i)=>`<div class="row${c===all[A_.selC]?' sel':''}" data-i="${A_.data.contacts.indexOf(c)}"><div class="nm">${personaEmoji(c)} ${esc(c.name)}</div><div class="f">${esc([c.firm,c.email].filter(Boolean).join(' · ')||'—')}</div></div>`).join('')||'<div class="fresh">No matches.</div>';
$('crows').querySelectorAll('.row').forEach(row=>row.onclick=()=>{A_.selC=+row.dataset.i;$('crows').querySelectorAll('.row').forEach(x=>x.classList.remove('sel'));row.classList.add('sel');detail(A_.data.contacts[A_.selC]);});
← f44b4fff RENTV closings: merge article corpus into /api/closings (Sal
·
back to Rentv
·
search-index: HomesOnSpec LIVE from Kamatera (InventoryHome, 75b7680c →