← back to Rentv Sheet Enrich
Live console: real SVG phone/website/LinkedIn icons + value-as-plaintext links; persist density; harden phone-vs-domain detection
15117d10cae50e75721f65e42b25a213edabcfac · 2026-08-13 15:19:54 -0700 · Steve Abrams
Files touched
Diff
commit 15117d10cae50e75721f65e42b25a213edabcfac
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 13 15:19:54 2026 -0700
Live console: real SVG phone/website/LinkedIn icons + value-as-plaintext links; persist density; harden phone-vs-domain detection
---
public_live/index.html | 42 ++++++++++++++++++++++++++++++------------
1 file changed, 30 insertions(+), 12 deletions(-)
diff --git a/public_live/index.html b/public_live/index.html
index 91a27d5..7d2796a 100644
--- a/public_live/index.html
+++ b/public_live/index.html
@@ -75,7 +75,11 @@
td.saved{background:var(--green);color:var(--greenink)}
td.enriched{box-shadow:inset 3px 0 0 #7bc043;background:rgba(123,192,67,.09)}
td a{color:var(--accent);text-decoration:none} .namecell{font-weight:600}
- a.callbtn{margin-left:5px;text-decoration:none;cursor:pointer;font-size:.95em;opacity:.75} a.callbtn:hover{opacity:1}
+ a.iconbtn,a.callbtn{display:inline-flex;align-items:center;justify-content:center;margin-left:5px;text-decoration:none;cursor:pointer;opacity:.72;vertical-align:middle}
+ a.iconbtn:hover,a.callbtn:hover{opacity:1}
+ a.iconbtn svg,a.callbtn svg{width:1.05em;height:1.05em;display:block}
+ a.callbtn{color:#16a34a} a.iconbtn.web{color:var(--accent)} a.iconbtn.li{color:#0a66c2}
+ .linkval{color:inherit}
.rowbtn{background:var(--accent);color:#04121f;border:0;border-radius:6px;padding:3px 8px;font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap}
.grouphdr td{background:#12161c;color:var(--accent);font-weight:700;font-size:12px;padding:6px 9px;position:sticky;top:26px}
/* cards */
@@ -163,7 +167,7 @@ const g=(r,i)=> (i>=0&&i<r.length)?String(r[i]||'').trim():'';
function hrefOf(v){ v=String(v||'').trim();
const m=v.match(/https?:\/\/[^\s"')]+/i); if(m) return m[0].replace(/[),.]+$/,'');
// bare domain or linkedin/site path (no spaces) -> add https:// so it's clickable
- if(/^(www\.)?[a-z0-9-]+(\.[a-z0-9-]+)+(\/\S*)?$/i.test(v)) return 'https://'+v;
+ if(/^(www\.)?([a-z0-9-]+\.)+[a-z]{2,}(\/\S*)?$/i.test(v)) return 'https://'+v; // TLD must be alphabetic — a dotted phone (310.555.9876) is NOT a domain
return '';
}
@@ -203,9 +207,12 @@ async function loadData(){
o.splice(o.indexOf(nc)+1,0,fn,ln); ORDER=o;
}
}
- // default sort = Last Name ascending on load (if the tab has one)
- const lnIdx=DATA.headers.findIndex(h=>/^last name$/i.test(String(h).trim()));
- SORT = lnIdx>=0 ? {col:lnIdx, dir:1} : null;
+ // restore saved sort for THIS view; else default to Last Name ascending
+ const savedSort=LS.get('sort.'+K, undefined);
+ if(savedSort!==undefined){ SORT=savedSort; }
+ else { const lnIdx=DATA.headers.findIndex(h=>/^last name$/i.test(String(h).trim())); SORT = lnIdx>=0 ? {col:lnIdx, dir:1} : null; }
+ // restore saved density
+ const dens=LS.get('dens',null); if(dens) document.documentElement.style.setProperty('--fs',dens+'px');
buildRail(); render(); setSync('ok');
}
function setSync(state){
@@ -278,10 +285,10 @@ function renderChips(){ const c=$('#chips'); let h='';
}
function wireRail(){
$('#search').oninput=e=>{SEARCH=e.target.value;clearTimeout(window._st);window._st=setTimeout(()=>{buildFacets();render()},180)};
- $('#sortsel').onchange=e=>{SORT=e.target.value===''?null:{col:+e.target.value,dir:SORT?SORT.dir:1};LS.set('sort',SORT);render()};
- $('#sortdir').onclick=()=>{if(SORT){SORT.dir*=-1;LS.set('sort',SORT);$('#sortdir').textContent=SORT.dir<0?'▼':'▲';render()}};
+ $('#sortsel').onchange=e=>{SORT=e.target.value===''?null:{col:+e.target.value,dir:SORT?SORT.dir:1};LS.set('sort.'+curKey(),SORT);render()};
+ $('#sortdir').onclick=()=>{if(SORT){SORT.dir*=-1;LS.set('sort.'+curKey(),SORT);$('#sortdir').textContent=SORT.dir<0?'▼':'▲';render()}};
$('#groupsel').onchange=e=>{GROUP=e.target.value;LS.set('group',GROUP);render()};
- $('#dens').oninput=e=>{document.documentElement.style.setProperty('--fs',e.target.value+'px')};
+ $('#dens').oninput=e=>{document.documentElement.style.setProperty('--fs',e.target.value+'px');LS.set('dens',e.target.value)};
$('#fitcols').onclick=fitAllCols; $('#resetcols').onclick=resetLayout;
$('#alls').onclick=()=>{SELECTED=new Set(META.tabs.map(t=>t.gid));loadData()};
$('#nones').onclick=()=>{SELECTED=new Set([GID]);loadData()};
@@ -377,15 +384,26 @@ function cardsHTML(rows,mode){
return h+'</div>';
}
function badge(ic,v){return `<span class="badge${v?' ok':''}">${ic}${v?' ✓':''}</span>`}
+// real inline-SVG icons (inherit currentColor, scale to text) — not emoji glyphs
+const ICON={
+ phone:'<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M6.6 10.8c1.4 2.8 3.8 5.2 6.6 6.6l2.2-2.2c.28-.28.68-.36 1.02-.24 1.12.4 2.33.62 3.58.62.55 0 1 .45 1 1V20c0 .55-.45 1-1 1C10.4 21 3 13.6 3 4.4c0-.55.45-1 1-1h3.4c.55 0 1 .45 1 1 0 1.25.22 2.46.62 3.58.12.34.04.74-.24 1.02l-2.18 2.18z"/></svg>',
+ web:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.6 2.8 2.6 15.2 0 18M12 3c-2.6 2.8-2.6 15.2 0 18"/></svg>',
+ li:'<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M4.98 3.5A2.5 2.5 0 1 0 5 8.5 2.5 2.5 0 0 0 4.98 3.5zM3 9h4v12H3zM9 9h3.8v1.64h.05c.53-1 1.83-2.06 3.77-2.06 4.03 0 4.78 2.65 4.78 6.1V21h-4v-5.4c0-1.29-.02-2.95-1.8-2.95-1.8 0-2.07 1.4-2.07 2.85V21H9z"/></svg>'};
+function iconLink(url,kind){
+ const svg=kind==='li'?ICON.li:ICON.web, label=kind==='li'?'Open LinkedIn profile':'Open website';
+ return `<a class="iconbtn ${kind}" href="${esc(url)}" target="_blank" rel="noopener" title="${label}">${svg}</a>`;
+}
function cellRender(v){v=String(v==null?'':v);const u=hrefOf(v);
- if(u)return `<a href="${esc(u)}" target="_blank" rel="noopener">${esc(v.length>44?u.replace(/^https?:\/\/(www\.)?/,''):v)}</a>`;
- if(/\d{3}[-.\s]?\d{3}[-.\s]?\d{4}/.test(v))return phoneLinkify(v); // phone numbers -> tap-to-call
+ if(u){ const li=/linkedin\.com/i.test(u); // value stays plain text; icon opens the link
+ const disp=v.replace(/^https?:\/\/(www\.)?/i,'').replace(/\/+$/,'');
+ return `<span class="linkval">${esc(disp)}</span>${iconLink(u, li?'li':'web')}`; }
+ if(/\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/.test(v))return phoneLinkify(v); // phone numbers (incl. "(310) 555-1234") -> tap-to-call
return esc(v)}
function phoneLinkify(v){
const re=/(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/g; let out='',last=0,m;
while((m=re.exec(v))){ out+=esc(v.slice(last,m.index));
let d=m[0].replace(/\D/g,''); if(d.length===10)d='1'+d;
- out+=esc(m[0])+`<a class="callbtn" href="tel:+${esc(d)}" title="Call ${esc(m[0])}">📞</a>`; last=m.index+m[0].length; }
+ out+=esc(m[0])+`<a class="callbtn" href="tel:+${esc(d)}" title="Call ${esc(m[0])}">${ICON.phone}</a>`; last=m.index+m[0].length; }
out+=esc(v.slice(last)); return out||esc(v);
}
function esc(s){return String(s==null?'':s).replace(/[&<>"]/g,m=>({'&':'&','<':'<','>':'>','"':'"'}[m]))}
@@ -404,7 +422,7 @@ function wireGrid(){
const up=ev=>{ document.removeEventListener('mousemove',mv);document.removeEventListener('mouseup',up);
th.classList.remove('dragging');ths.forEach(t=>t.classList.remove('droptarget'));
if(moved){const tg=at(ev.clientX);if(tg&&+tg.dataset.c!==col)moveCol(col,+tg.dataset.c);}
- else{SORT=(SORT&&SORT.col===col)?{col,dir:-SORT.dir}:{col,dir:1};LS.set('sort',SORT);render();$('#sortsel')&&($('#sortsel').value=col);} };
+ else{SORT=(SORT&&SORT.col===col)?{col,dir:-SORT.dir}:{col,dir:1};LS.set('sort.'+curKey(),SORT);render();$('#sortsel')&&($('#sortsel').value=col);} };
document.addEventListener('mousemove',mv);document.addEventListener('mouseup',up);
}));
// resizable columns — drag a column's right edge; width persists per tab
← 58c6056 Server: send no-store/no-cache headers so the browser always
·
back to Rentv Sheet Enrich
·
Live console: icons open Website/LinkedIn in a popup window 44d2687 →