SARO Smart Link — Generator

SARO Smart Link — Generator

Create a customer's link page, track trials, and export your customer list.

About

Contact

Core Links

More Links (choose platform → icon fills in)

Plan & Status

Theme

Live Preview

"Copy for WordPress" adds a short instructions comment at the top — paste the result straight into a WordPress Custom HTML block or Elementor HTML widget.

Quick Tips

Fill in the form, pick a theme, and the preview updates live. Click Save Customer Record to add this customer to your Dashboard, where you can track trial/expiry status and export your full list to Excel.

`; } function buildBusiness(name, tagline, desc, loc, photoUrl, links){ const rows = links.map(l => ` ${l.icon}${esc(l.label)}›`).join(''); return ` ${esc(name)}
Let's Connect
${avatarBlock(name, photoUrl, '', '#141414', '#D4AF37')}

${esc(name)||'Your Name'}

${tagline?`

${esc(tagline)}

`:''}
${desc?`

${esc(desc)}

`:''} ${loc?`

${esc(loc)}

`:''} ${rows}

Powered By Saro Smart Link

`; } function generatePageHtml(){ const name = val('bizName'); const tag = val('tagline'); const desc = val('desc'); const loc = val('location'); const photoUrl = val('photoUrl'); const links = buildLinks(); if(currentTheme === 'glassy') return buildGlassy(name, tag, desc, loc, photoUrl, links); return buildBusiness(name, tag, desc, loc, photoUrl, links); } function render(){ const html = generatePageHtml(); document.getElementById('previewFrame').srcdoc = html; const live = val('liveUrl'); const qrBox = document.getElementById('qrBox'); if(live){ document.getElementById('qrImg').src = 'https://api.qrserver.com/v1/create-qr-code/?size=140x140&data=' + encodeURIComponent(live); qrBox.style.display = 'block'; } else { qrBox.style.display = 'none'; } return html; } ['bizName','tagline','desc','location','photoUrl','phone','whatsapp','email','website','maps','instagram','facebook','youtube','liveUrl'] .forEach(id => document.getElementById(id).addEventListener('input', render)); function flashCopied(btn){ const old = btn.textContent; btn.textContent = 'Copied ✓'; btn.classList.add('copied'); setTimeout(()=>{ btn.textContent = old; btn.classList.remove('copied'); }, 1400); } document.getElementById('copyHtml').addEventListener('click', (e)=>{ navigator.clipboard.writeText(generatePageHtml()); flashCopied(e.target); }); document.getElementById('copyWp').addEventListener('click', (e)=>{ const wpHtml = `\n` + generatePageHtml(); navigator.clipboard.writeText(wpHtml); flashCopied(e.target); }); document.getElementById('downloadHtml').addEventListener('click', ()=>{ const html = generatePageHtml(); const name = (val('bizName') || 'smart-link').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/(^-|-$)/g,''); const blob = new Blob([html], {type:'text/html'}); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = name + '.html'; a.click(); }); // ---------- plan dates default ---------- function todayIso(offsetDays){ const d = new Date(); d.setDate(d.getDate() + (offsetDays||0)); return d.toISOString().slice(0,10); } function setDefaultDates(){ setVal('planStart', todayIso(0)); setVal('planEnd', todayIso(7)); } setDefaultDates(); document.getElementById('planStart').addEventListener('change', ()=>{ // keep a 7-day span if end hasn't been manually pushed further if(!document.getElementById('planEnd').dataset.touched){ const start = new Date(val('planStart')); start.setDate(start.getDate()+7); setVal('planEnd', start.toISOString().slice(0,10)); } }); document.getElementById('planEnd').addEventListener('change', ()=>{ document.getElementById('planEnd').dataset.touched = '1'; }); // ---------- save / clear / edit ---------- function clearForm(){ ['bizName','tagline','desc','location','photoUrl','phone','whatsapp','email','website','maps','instagram','facebook','youtube','liveUrl'] .forEach(id => setVal(id, '')); document.getElementById('manualLinks').innerHTML = ''; document.getElementById('planEnd').dataset.touched = ''; setDefaultDates(); editingId = null; document.getElementById('saveBtn').textContent = 'Save Customer Record'; render(); } document.getElementById('clearBtn').addEventListener('click', clearForm); document.getElementById('saveBtn').addEventListener('click', ()=>{ const record = { id: editingId || (Date.now().toString(36)), name: val('bizName'), tagline: val('tagline'), desc: val('desc'), location: val('location'), photoUrl: val('photoUrl'), phone: val('phone'), whatsapp: val('whatsapp'), email: val('email'), website: val('website'), maps: val('maps'), instagram: val('instagram'), facebook: val('facebook'), youtube: val('youtube'), manualLinks: getManualLinks(), theme: currentTheme, planStart: val('planStart'), planEnd: val('planEnd'), liveUrl: val('liveUrl'), createdAt: editingId ? (loadCustomers().find(c=>c.id===editingId)||{}).createdAt || new Date().toISOString() : new Date().toISOString() }; let list = loadCustomers(); if(editingId){ list = list.map(c => c.id === editingId ? record : c); } else { list.push(record); } saveCustomers(list); editingId = record.id; document.getElementById('saveBtn').textContent = 'Update Customer Record'; renderDashboard(); }); function loadRecordIntoForm(rec){ setVal('bizName', rec.name); setVal('tagline', rec.tagline); setVal('desc', rec.desc); setVal('location', rec.location); setVal('photoUrl', rec.photoUrl); setVal('phone', rec.phone); setVal('whatsapp', rec.whatsapp); setVal('email', rec.email); setVal('website', rec.website); setVal('maps', rec.maps); setVal('instagram', rec.instagram); setVal('facebook', rec.facebook); setVal('youtube', rec.youtube); setVal('liveUrl', rec.liveUrl); setVal('planStart', rec.planStart); setVal('planEnd', rec.planEnd); document.getElementById('planEnd').dataset.touched = '1'; document.getElementById('manualLinks').innerHTML = ''; (rec.manualLinks||[]).forEach(m => addManualLinkRow(m)); currentTheme = rec.theme || 'glassy'; document.querySelectorAll('.theme-btn').forEach(b => b.classList.toggle('active', b.dataset.theme === currentTheme)); editingId = rec.id; document.getElementById('saveBtn').textContent = 'Update Customer Record'; render(); } // ---------- import from Google Sheet ---------- function mapSheetThemeToGenerator(sheetTheme){ // Trial form's "Selected Theme" radio already uses these exact labels. return (sheetTheme === 'Premium') ? 'business' : 'glassy'; } function sheetRowToRecord(row, existing){ const stamp = row['Timestamp'] ? new Date(row['Timestamp']).toISOString() : ''; return { id: existing ? existing.id : 'sheet-' + (stamp || row['Business Name'] || Math.random().toString(36).slice(2)), name: row['Business Name'] || row['Full Name'] || '', tagline: existing ? existing.tagline : '', desc: existing ? existing.desc : (row['Full Name'] ? ('Contact: ' + row['Full Name']) : ''), location: row['Business Location'] || '', photoUrl: row['Photo Link'] || (existing ? existing.photoUrl : ''), phone: row['Contact Number'] || '', whatsapp: row['Contact Number'] || '', email: row['Email'] || '', website: row['Website'] || '', maps: existing ? existing.maps : '', instagram: row['Instagram'] || '', facebook: row['Facebook'] || '', youtube: row['YouTube'] || '', manualLinks: existing ? (existing.manualLinks || []) : [], theme: mapSheetThemeToGenerator(row['Selected Theme']), planStart: row['Trial Start'] || '', planEnd: row['Trial End'] || '', liveUrl: existing ? existing.liveUrl : '', // Sheet doesn't collect this — preserve if set locally createdAt: existing ? existing.createdAt : (stamp || new Date().toISOString()), source: 'sheet' }; } async function importFromSheet(){ const statusEl = document.getElementById('sheetSyncStatus'); if(!SHEET_WEBAPP_URL || SHEET_WEBAPP_URL.includes('PASTE_YOUR')){ if(statusEl) statusEl.textContent = 'Sheet URL not configured.'; return; } if(statusEl) statusEl.textContent = 'Syncing from Sheet…'; try { const res = await fetch(SHEET_WEBAPP_URL); const rows = await res.json(); if(!Array.isArray(rows)){ if(statusEl) statusEl.textContent = 'Sheet sync failed.'; return; } const list = loadCustomers(); const byId = {}; list.forEach(c => byId[c.id] = c); rows.forEach(row => { const stamp = row['Timestamp'] ? new Date(row['Timestamp']).toISOString() : ''; const id = 'sheet-' + (stamp || row['Business Name'] || ''); const existing = byId[id]; byId[id] = sheetRowToRecord(row, existing); }); saveCustomers(Object.values(byId)); if(statusEl) statusEl.textContent = 'Synced ' + rows.length + ' record(s) from Sheet.'; } catch(err){ if(statusEl) statusEl.textContent = 'Sheet sync failed — check console.'; console.error('Sheet import error', err); } renderDashboard(); } document.getElementById('refreshSheetBtn').addEventListener('click', importFromSheet); // ---------- dashboard ---------- function computeStatus(rec){ const today = new Date(todayIso(0)); const end = rec.planEnd ? new Date(rec.planEnd) : null; if(end && end < today) return 'expired'; if(rec.liveUrl) return 'live'; if(end){ const diffDays = Math.ceil((end - today) / (1000*60*60*24)); if(diffDays <= 2) return 'expiring'; } return 'trial'; } const STATUS_LABEL = { trial:'Trial Active', expiring:'Expiring Soon', live:'Live', expired:'Expired' }; function renderDashboard(){ const list = loadCustomers(); const search = (document.getElementById('searchBox').value || '').toLowerCase(); const statusFilter = document.getElementById('statusFilter').value; const withStatus = list.map(r => ({ ...r, status: computeStatus(r) })); document.getElementById('kpiTotal').textContent = withStatus.length; document.getElementById('kpiTrial').textContent = withStatus.filter(r=>r.status==='trial').length; document.getElementById('kpiExpiring').textContent = withStatus.filter(r=>r.status==='expiring').length; document.getElementById('kpiLive').textContent = withStatus.filter(r=>r.status==='live').length; const filtered = withStatus.filter(r=>{ const matchesSearch = !search || (r.name||'').toLowerCase().includes(search); const matchesStatus = statusFilter === 'all' || r.status === statusFilter; return matchesSearch && matchesStatus; }); const wrap = document.getElementById('recordsList'); if(filtered.length === 0){ wrap.innerHTML = '

No customers match this filter yet.

'; return; } wrap.innerHTML = filtered.map(r => `
${esc(r.name)||'Untitled'} ${esc(r.tagline)||''} Ends: ${r.planEnd||'—'} ${r.source === 'sheet' ? 'From Sheet' : ''} ${STATUS_LABEL[r.status]}
Location${esc(r.location)||'—'}
Theme${r.theme}
Phone${esc(r.phone)||'—'}
WhatsApp${esc(r.whatsapp)||'—'}
Email${esc(r.email)||'—'}
Website${esc(r.website)||'—'}
Instagram${esc(r.instagram)||'—'}
Facebook${esc(r.facebook)||'—'}
YouTube${esc(r.youtube)||'—'}
Extra Links${(r.manualLinks||[]).map(m=>m.label).join(', ')||'—'}
Live URL${esc(r.liveUrl)||'Not published'}
Start / End${r.planStart||'—'} → ${r.planEnd||'—'}
`).join(''); wrap.querySelectorAll('.record').forEach(el=>{ const id = el.dataset.id; el.querySelector('.edit').addEventListener('click', (e)=>{ e.preventDefault(); const rec = loadCustomers().find(c=>c.id===id); if(rec){ loadRecordIntoForm(rec); document.querySelector('.tab-btn[data-tab="create"]').click(); } }); el.querySelector('.del').addEventListener('click', (e)=>{ e.preventDefault(); if(confirm('Delete this customer record?')){ saveCustomers(loadCustomers().filter(c=>c.id!==id)); renderDashboard(); } }); }); } document.getElementById('searchBox').addEventListener('input', renderDashboard); document.getElementById('statusFilter').addEventListener('change', renderDashboard); document.getElementById('exportExcelBtn').addEventListener('click', ()=>{ const list = loadCustomers().map(r => ({...r, status: computeStatus(r)})); const search = (document.getElementById('searchBox').value || '').toLowerCase(); const statusFilter = document.getElementById('statusFilter').value; const filtered = list.filter(r=>{ const matchesSearch = !search || (r.name||'').toLowerCase().includes(search); const matchesStatus = statusFilter === 'all' || r.status === statusFilter; return matchesSearch && matchesStatus; }); const rows = filtered.map(r => ({ 'Name': r.name, 'Tagline': r.tagline, 'Location': r.location, 'Phone': r.phone, 'WhatsApp': r.whatsapp, 'Email': r.email, 'Website': r.website, 'Instagram': r.instagram, 'Facebook': r.facebook, 'YouTube': r.youtube, 'Extra Links': (r.manualLinks||[]).map(m=>`${m.label}: ${m.url}`).join(' | '), 'Theme': r.theme, 'Start Date': r.planStart, 'End Date': r.planEnd, 'Live URL': r.liveUrl, 'Status': STATUS_LABEL[r.status], 'Created': r.createdAt })); const ws = XLSX.utils.json_to_sheet(rows); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Customers'); XLSX.writeFile(wb, 'SARO_Smart_Link_Customers.xlsx'); }); render(); renderDashboard(); importFromSheet();