[object Object]

← back to Rentv

crm: wider LinkedIn match (first+last token, catches middle-initial/nickname variants) — 10→16

37698626fa6095f7f531e73751b71c2ac25c9fb1 · 2026-08-12 17:51:17 -0700 · Steve Abrams

Files touched

Diff

commit 37698626fa6095f7f531e73751b71c2ac25c9fb1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 12 17:51:17 2026 -0700

    crm: wider LinkedIn match (first+last token, catches middle-initial/nickname variants) — 10→16
---
 server.js | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/server.js b/server.js
index 7fff9ea1..777f4a2a 100644
--- a/server.js
+++ b/server.js
@@ -2365,11 +2365,12 @@ function liNorm(s){
   t = t.replace(/\b(aia|ccim|sior|mba|esq|jr|sr|ii|iii|iv|cpm|leed|ap|phd|md|mai|rpa|cfa|cpa|mrics|frics|pe|rid|ncidq|gri|abr|crs)\b/g, ' ').replace(/\s+/g, ' ').trim();
   return t;
 }
+function liFirstLast(s){ const p = liNorm(s).split(' ').filter(Boolean); return p.length >= 2 ? p[0] + ' ' + p[p.length - 1] : ''; }
 function loadLinkedInFollows() {
-  const set = new Set();
+  const names = [];
   try {
     const j = JSON.parse(fs.readFileSync(path.join(DATA, 'linkedin-follows.json'), 'utf8'));
-    for (const x of (Array.isArray(j) ? j : [])) { const n = typeof x === 'string' ? x : (x && x.name); if (n) set.add(liNorm(n)); }
+    for (const x of (Array.isArray(j) ? j : [])) { const n = typeof x === 'string' ? x : (x && x.name); if (n) names.push(n); }
   } catch { /* no json */ }
   try {
     const lines = fs.readFileSync(path.join(DATA, 'linkedin-connections.csv'), 'utf8').split(/\r?\n/);
@@ -2383,11 +2384,13 @@ function loadLinkedInFollows() {
         const f = lines[i].match(/("([^"]|"")*"|[^,]*)(,|$)/g) || [];
         const cell = k => (f[k] || '').replace(/,$/, '').replace(/^"|"$/g, '').replace(/""/g, '"').trim();
         const nm = ni >= 0 ? cell(ni) : [cell(fi), cell(li2)].filter(Boolean).join(' ');
-        if (nm) set.add(liNorm(nm));
+        if (nm) names.push(nm);
       }
     }
   } catch { /* no csv */ }
-  return set;
+  const full = new Set(), fl = new Set();
+  for (const n of names) { const nn = liNorm(n); if (nn) full.add(nn); const f = liFirstLast(n); if (f) fl.add(f); }
+  return { full, fl };
 }
 app.get('/api/crm', adminOnly, async (req, res) => {
   try {
@@ -2408,7 +2411,8 @@ app.get('/api/crm', adminOnly, async (req, res) => {
     const all = [...ownedContacts, ...commercial, ...owners, ...directory].map((c) => ({
       ...c,
       // green-highlight flag: is this person someone Steve already follows/connects with on LinkedIn?
-      li_follow: !!(c.name && liFollows.has(liNorm(c.name))),
+      // exact normalized full-name match, OR first+last-token match (catches middle-initial/nickname variants).
+      li_follow: !!(c.name && (liFollows.full.has(liNorm(c.name)) || (liFirstLast(c.name) && liFollows.fl.has(liFirstLast(c.name))))),
       // one precomputed lowercase search blob per contact (no per-keystroke JSON.stringify).
       _s: [c.name, c.firm, c.email, c.phone, c.city, c.title, c.interest, c.source, c.dre_license, c.ain, c.list_category, c.address].filter(Boolean).join(' ').toLowerCase(),
     }));

← 389655dc crm: strip credential suffixes (AIA/CCIM/SIOR/MBA…) when mat  ·  back to Rentv  ·  fix(desk): answer /favicon.ico with 204 to silence console 4 37140366 →