← back to Marketing Command Center
clients panel: search across ALL fields + sort dropdown (name/city/state/has-email/IG/LinkedIn), persisted
1a674419cad5591d8ddfb2da290295f74416d592 · 2026-07-16 11:30:24 -0700 · steve
Files touched
M public/panels/clients.htmlM public/panels/clients.js
Diff
commit 1a674419cad5591d8ddfb2da290295f74416d592
Author: steve <steve@designerwallcoverings.com>
Date: Thu Jul 16 11:30:24 2026 -0700
clients panel: search across ALL fields + sort dropdown (name/city/state/has-email/IG/LinkedIn), persisted
---
public/panels/clients.html | 14 +++++++++++++-
public/panels/clients.js | 26 +++++++++++++++++++++++---
2 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/public/panels/clients.html b/public/panels/clients.html
index 2218459..73c3539 100644
--- a/public/panels/clients.html
+++ b/public/panels/clients.html
@@ -20,7 +20,19 @@
<button class="btn gold" id="cl-export" style="font-size:11px;padding:5px 9px;">⬇ Export CSV</button>
<button class="btn ghost" id="cl-reset" style="font-size:11px;padding:5px 9px;">Reset checks</button>
</div>
- <input id="cl-search" type="text" placeholder="Filter by name / company / city…" style="margin-top:12px;width:100%;">
+ <div class="row" style="gap:8px;margin-top:12px;align-items:center;flex-wrap:wrap;">
+ <input id="cl-search" type="text" placeholder="Search all fields — name, company, email, city, state, phone, website…" style="flex:1;min-width:220px;">
+ <label class="muted" for="cl-sort" style="font-size:11.5px;">Sort</label>
+ <select id="cl-sort" style="font-size:12px;">
+ <option value="default">Default order</option>
+ <option value="name">Name / Company A→Z</option>
+ <option value="city">City A→Z</option>
+ <option value="state">State A→Z</option>
+ <option value="email">Has email first</option>
+ <option value="instagram">Has Instagram first</option>
+ <option value="linkedin">Has LinkedIn first</option>
+ </select>
+ </div>
<div class="muted" id="cl-groupnote" style="font-size:11.5px;margin-top:8px;"></div>
</div>
diff --git a/public/panels/clients.js b/public/panels/clients.js
index 3890113..ede787f 100644
--- a/public/panels/clients.js
+++ b/public/panels/clients.js
@@ -20,13 +20,15 @@ window.MCC_PANELS['clients'] = {
function norm(rec, kind) {
if (kind === 'prospects') {
return { id: (rec.website || rec.title || '').toLowerCase(), label: rec.title || '(business)', sub: [rec.city].filter(Boolean).join(' · '),
- email: rec.email || '', website: rec.website || '', phone: rec.phone || '', q: [rec.title, rec.city].filter(Boolean).join(' '), _raw: rec };
+ email: rec.email || '', website: rec.website || '', phone: rec.phone || '', q: [rec.title, rec.city].filter(Boolean).join(' '),
+ _raw: rec, _all: Object.values(rec).filter(v => v && typeof v === 'string').join(' ').toLowerCase() };
}
const label = rec.name || rec.company || '(client)';
const sub = rec.name ? [rec.company, [rec.city, rec.state].filter(Boolean).join(', ')].filter(Boolean).join(' · ')
: [rec.city, rec.state].filter(Boolean).join(', ');
return { id: rec.account || (rec.company + '|' + rec.name).toLowerCase(), label, sub, email: rec.email || '', website: '', phone: rec.phone || '',
- q: [rec.name, rec.company, rec.city].filter(Boolean).join(' '), _raw: rec };
+ q: [rec.name, rec.company, rec.city].filter(Boolean).join(' '),
+ _raw: rec, _all: Object.values(rec).filter(v => v && typeof v === 'string').join(' ').toLowerCase() };
}
const liURL = q => 'https://www.linkedin.com/search/results/all/?keywords=' + encodeURIComponent(q);
const igURL = q => 'https://www.google.com/search?q=' + encodeURIComponent(q + ' instagram');
@@ -60,14 +62,32 @@ window.MCC_PANELS['clients'] = {
$('#cl-groupnote').textContent = `Source: ${data.source || d.file}${data.updated ? ' · updated ' + data.updated : ''}. LinkedIn opens a people/company search; IG opens a Google “<name> instagram” lookup — open the right match and follow.`;
$('#cl-search').value = '';
$('#cl-search').oninput = render;
+ $('#cl-sort').value = sortMode();
+ $('#cl-sort').onchange = () => { try { localStorage.setItem(SK, $('#cl-sort').value); } catch {} render(); };
$('#cl-reset').onclick = () => { if (confirm('Clear contacted checks for ' + d.label + '?')) { save(d.id, new Set()); render(); } };
$('#cl-export').onclick = () => exportCSV(d);
render();
}
+ const SK = 'mcc_cl_sort';
+ const sortMode = () => { try { return localStorage.getItem(SK) || 'default'; } catch { return 'default'; } };
+ function sortRecs(list) {
+ const az = (a, b) => a.label.localeCompare(b.label);
+ const has = f => (a, b) => ((b._raw[f] ? 1 : 0) - (a._raw[f] ? 1 : 0)) || az(a, b);
+ const cmp = {
+ name: az,
+ city: (a, b) => (a._raw.city || '').localeCompare(b._raw.city || '') || az(a, b),
+ state: (a, b) => (a._raw.state || '').localeCompare(b._raw.state || '') || az(a, b),
+ email: (a, b) => ((b.email ? 1 : 0) - (a.email ? 1 : 0)) || az(a, b),
+ instagram: has('instagram'),
+ linkedin: has('linkedin'),
+ }[sortMode()];
+ return cmp ? list.slice().sort(cmp) : list;
+ }
function currentMatches() {
const q = ($('#cl-search').value || '').trim().toLowerCase();
- return q ? RECS.filter(r => r.q.toLowerCase().includes(q)) : RECS;
+ const list = q ? RECS.filter(r => r._all.includes(q)) : RECS;
+ return sortRecs(list);
}
// Review-oriented CSV export of the current group (respects the filter).
← e0ae6f5 clients: browser+local-model contact enrichment (direct Inst
·
back to Marketing Command Center
·
auto-save: 2026-07-16T11:43:05 (1 files) — public/data/clien d885131 →