| var Storage = { |
| get: function(key) { try { return localStorage.getItem(key); } catch(e) { return null; } }, |
| set: function(key, val) { try { localStorage.setItem(key, val); } catch(e) {} } |
| }; |
|
|
| var GOOGLE_CLIENT_ID = '500448449044-hv2rp3k0lsok9ara1bred87c75lnsp7l.apps.googleusercontent.com'; |
| let curUser=null, dbFil='all', googleAccessToken=null, driveFileId=null; |
| let saved = []; |
|
|
| try { const sData = Storage.get('qrs_min'); if (sData) saved = JSON.parse(sData); } catch(e) {} |
|
|
| function switchView(viewId) { |
| document.querySelectorAll('.db-view').forEach(v => v.style.display = 'none'); |
| const target = document.getElementById('view-' + viewId); |
| if(target) target.style.display = 'block'; |
| |
| document.querySelectorAll('.app-sb .sb-btn').forEach(btn => { |
| if(btn.getAttribute('onclick') && btn.getAttribute('onclick').includes(viewId)) { |
| btn.classList.add('act'); |
| } else if (btn.getAttribute('onclick') && btn.getAttribute('onclick').includes('switchView')) { |
| btn.classList.remove('act'); |
| } |
| }); |
| |
| if(viewId === 'stats') renderStats(); |
| if(viewId === 'templates') renderTemplates(); |
| if(viewId === 'domains') renderDomains(); |
| } |
|
|
| function renderDB(){ |
| const filtered = dbFil==='all' ? saved : saved.filter(q=>q.type===dbFil); |
| document.getElementById('stTotal').textContent = saved.length; |
| |
| const uniqueTypes = []; |
| saved.forEach(function(q){ if(uniqueTypes.indexOf(q.type)===-1) uniqueTypes.push(q.type); }); |
| document.getElementById('stTypes').textContent = uniqueTypes.length; |
| document.getElementById('stLast').textContent = saved.length>0 ? saved[0].date : '—'; |
| |
| const dbs = document.getElementById('dbSub'); |
| if(curUser && dbs) dbs.textContent = `Dashboard synced. ${saved.length} assets ready.`; |
| |
| const g = document.getElementById('dbG'); if(!g) return; |
| g.innerHTML = ''; |
| if(!filtered.length){ |
| g.innerHTML=`<div class="db-emp"><div class="ei">🔳</div><h3>Workspace Empty</h3><p>No assets found for this filter.</p><button class="btn-nx" style="margin: 20px auto 0" onclick="window.location.href='qr-editor.html'">Initialize New QR</button></div>`; |
| return; |
| } |
| filtered.forEach(q=>{ |
| const c=document.createElement('div');c.className='dbc'; |
| const nameStr = (q.advSettings && q.advSettings.name) ? q.advSettings.name : q.content; |
| const shortC = nameStr.length>38 ? nameStr.substring(0,38)+'…' : nameStr; |
| c.innerHTML=`<div class="dbt">${q.thumb?`<img src="${q.thumb}" alt="QR" loading="lazy">`:'<div style="font-size:2rem;opacity:.25">🔳</div>'}</div> |
| <div class="dbb"><div class="dtyp">${q.typeIcon} ${q.typeName}</div><h3 title="${q.content}">${shortC}</h3><div class="ddt">📅 ${q.date}</div></div> |
| <div class="dba"><button class="dba-btn" onclick="editQR(${q.id})">✏️ Editor</button><button class="dba-btn" onclick="reDL(${q.id})">⬇ Export</button><button class="dba-btn del" onclick="delQR(${q.id})">🗑 Delete</button></div>`; |
| g.appendChild(c); |
| }); |
| } |
|
|
| function fDB(type,btn){ |
| dbFil=type; |
| document.querySelectorAll('.dbf').forEach(b=>{ if(b===btn) b.classList.add('act'); else b.classList.remove('act'); }); |
| renderDB(); |
| } |
| function delQR(id){ |
| if(!confirm('Permanently purge this QR asset?'))return; |
| let idx = -1; |
| for(let i=0;i<saved.length;i++){ if(saved[i].id===id){idx=i;break;} } |
| if(idx>-1){ |
| saved.splice(idx,1); |
| async function renderStats() { |
| const v = document.getElementById('view-stats'); |
| if(v.innerHTML !== '') return; |
| |
| |
| v.innerHTML = ` |
| <div class="db-hd" style="display:flex; justify-content:space-between; align-items:center; margin-bottom: 20px;"> |
| <div><h1 style="font-size:2.2rem; margin:0;">Stats</h1></div> |
| <button class="dbf" style="border:1px solid var(--pri); color:var(--pri); background:transparent;">Export information</button> |
| </div> |
| |
| <div style="background: rgba(37,99,235,0.05); border: 1px solid rgba(37,99,235,0.2); border-radius: 8px; padding: 12px 20px; display:flex; justify-content:space-between; align-items:center; margin-bottom: 25px; flex-wrap:wrap; gap:10px;"> |
| <div style="display:flex; gap:10px; align-items:center; color: #3b82f6; font-weight:600; font-size:0.95rem;"> |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg> |
| You can set your time zone. <span style="font-weight:400; opacity:0.8;">Current time zone: Asia/Calcutta</span> |
| </div> |
| <div style="display:flex; gap:10px;"> |
| <button style="background:transparent; border:none; color:#3b82f6; font-weight:700; cursor:pointer;">Change</button> |
| <button style="background:#3b82f6; border:none; color:#fff; padding:6px 16px; border-radius:20px; font-weight:600; cursor:pointer; transition:0.2s;">Accept</button> |
| </div> |
| </div> |
| |
| <div style="display:flex; justify-content:space-between; margin-bottom:20px; align-items:center; flex-wrap:wrap; gap:15px;"> |
| <div style="display:flex; gap:15px; align-items:center; flex-wrap:wrap;"> |
| <select style="background:var(--panel); border:1px solid var(--border); color:var(--txt); padding:10px 15px; border-radius:8px; outline:none; font-family:var(--font-main);"> |
| <option>Last 7 days</option> |
| <option>Last 30 days</option> |
| <option>This year</option> |
| </select> |
| <div style="background:var(--panel); border:1px solid var(--border); padding:10px 15px; border-radius:8px; color:var(--txt-m); display:flex; align-items:center; gap:10px; font-size:0.9rem;"> |
| Jun 3, 2026 - Jun 10, 2026 |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> |
| </div> |
| <div style="background:var(--panel); border:1px solid var(--border); padding:10px 15px; border-radius:8px; color:var(--txt-m); display:flex; align-items:center; gap:10px; font-size:0.9rem;"> |
| Time zone: Asia/Calcutta ✎ |
| </div> |
| </div> |
| <button class="dbf" style="display:flex; align-items:center; gap:8px;"> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg> |
| Filter |
| </button> |
| </div> |
| |
| <div style="background: rgba(255,255,255,0.02); border-radius:8px; padding:12px 20px; margin-bottom:25px; font-size:0.85rem; color:var(--txt-m); display:flex; align-items:center;"> |
| <strong style="color:var(--txt); margin-right:8px;">Analyzed period:</strong> 03-06-2026 to 10-06-2026 |
| </div> |
| |
| <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:20px; margin-bottom:30px;" id="stats-cards-container"> |
| <div style="padding: 50px; text-align: center; color: var(--txt-m); grid-column:1/-1;"> |
| <style>@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }</style> |
| <div style="margin: 0 auto 20px auto; width: 40px; height: 40px; border: 4px solid rgba(16, 185, 129, 0.2); border-top: 4px solid #10b981; border-radius: 50%; animation: spin 1s linear infinite;"></div> |
| <p>Syncing analytics data from cloud...</p> |
| </div> |
| </div> |
| |
| <div class="dbc" style="padding: 25px; margin-bottom:25px;"> |
| <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:30px; flex-wrap:wrap; gap:15px;"> |
| <div style="display:flex; gap:20px; align-items:center;"> |
| <label style="display:flex; gap:8px; align-items:center; cursor:pointer;"><input type="checkbox" checked style="accent-color:var(--pri); width:18px; height:18px;"> <span style="font-weight:600; font-size:0.95rem;">Totals</span></label> |
| <label style="display:flex; gap:8px; align-items:center; cursor:pointer;"><input type="checkbox" style="accent-color:var(--pri); width:18px; height:18px;"> <span style="font-weight:600; font-size:0.95rem; color:var(--txt-m);">Uniques</span></label> |
| <label style="display:flex; gap:8px; align-items:center; cursor:pointer;"><input type="checkbox" style="accent-color:var(--pri); width:18px; height:18px;"> <span style="font-weight:600; font-size:0.95rem; color:var(--txt-m);">Visits</span></label> |
| </div> |
| <div style="display:flex; gap:15px; align-items:center;"> |
| <div style="display:flex; background:var(--bg); border:1px solid var(--border); border-radius:8px; overflow:hidden;"> |
| <button style="padding:8px 16px; border:none; background:rgba(37,99,235,0.1); color:#3b82f6; font-weight:600; cursor:pointer;">Day</button> |
| <button style="padding:8px 16px; border:none; border-left:1px solid var(--border); background:transparent; color:var(--txt-m); font-weight:600; cursor:pointer;">Month</button> |
| <button style="padding:8px 16px; border:none; border-left:1px solid var(--border); background:transparent; color:var(--txt-m); font-weight:600; cursor:pointer;">Year</button> |
| </div> |
| <div style="display:flex; gap:8px;"> |
| <button style="width:36px; height:36px; border-radius:8px; border:none; background:var(--bg); color:var(--txt-m); display:flex; align-items:center; justify-content:center; cursor:pointer;"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg></button> |
| <button style="width:36px; height:36px; border-radius:8px; border:none; background:rgba(37,99,235,0.1); color:#3b82f6; display:flex; align-items:center; justify-content:center; cursor:pointer;"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="18" y="3" width="4" height="18"></rect><rect x="10" y="8" width="4" height="13"></rect><rect x="2" y="13" width="4" height="8"></rect></svg></button> |
| </div> |
| </div> |
| </div> |
| <div style="width:100%; height:300px; position:relative;"> |
| <canvas id="statsChart"></canvas> |
| </div> |
| </div> |
| |
| <div style="display:flex; flex-direction:column; gap:10px; margin-bottom:50px;" id="accordions-container"> |
| <!-- Accordions rendered below --> |
| </div> |
| `; |
|
|
| |
| let sb = window.supabaseClient; |
| if (!sb) { |
| try { |
| const res = await fetch('config.json'); |
| const config = await res.json(); |
| if (config.auth && config.auth.url) { |
| if (typeof window.supabase === 'undefined') { |
| await new Promise(resolve => { |
| const script = document.createElement('script'); |
| script.src = "https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"; |
| script.onload = resolve; |
| document.head.appendChild(script); |
| }); |
| } |
| sb = window.supabase.createClient(config.auth.url, config.auth.key); |
| window.supabaseClient = sb; |
| } |
| } catch(e) {} |
| } |
|
|
| const myQrIds = saved.map(q => String(q.id)); |
| let scans = []; |
| |
| if (sb && myQrIds.length > 0) { |
| try { |
| const { data } = await sb.from('qr_scans').select('*').in('qr_id', myQrIds); |
| if (data) scans = data; |
| } catch(e) { |
| console.error('Failed to load stats', e); |
| } |
| } |
|
|
| const totalQRs = saved.length; |
| const totalScans = scans.length; |
| |
| |
| const uniqueHashes = new Set(scans.filter(s => s.visitor_hash).map(s => s.visitor_hash)); |
| const totalUnique = uniqueHashes.size > 0 ? uniqueHashes.size : (totalScans > 0 ? Math.ceil(totalScans * 0.8) : 0); |
| const totalVisits = totalScans; |
|
|
| const cardsHtml = ` |
| <div class="st-card" style="padding:35px 25px; border-radius:8px; display:flex; flex-direction:column; justify-content:center; align-items:center; background:var(--panel); border:1px solid var(--border);"> |
| <div class="v" style="font-size:3.5rem; color:var(--txt); -webkit-text-fill-color:var(--txt); background:none; text-shadow:none; font-family:var(--font-main);">\${totalQRs}</div> |
| <div class="l" style="text-transform:none; font-weight:600; margin-top:15px; color:var(--txt-m); display:flex; align-items:center; gap:8px; font-size:1rem;"> |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><rect x="7" y="7" width="3" height="3"></rect><rect x="14" y="7" width="3" height="3"></rect><rect x="7" y="14" width="3" height="3"></rect><rect x="14" y="14" width="3" height="3"></rect></svg> Total QR Codes |
| </div> |
| </div> |
| <div class="st-card" style="padding:35px 25px; border-radius:8px; display:flex; flex-direction:column; justify-content:center; align-items:center; background:var(--panel); border:1px solid var(--border);"> |
| <div class="v" style="font-size:3.5rem; color:var(--txt); -webkit-text-fill-color:var(--txt); background:none; text-shadow:none; font-family:var(--font-main);">\${totalScans}</div> |
| <div class="l" style="text-transform:none; font-weight:600; margin-top:15px; color:var(--txt-m); display:flex; align-items:center; gap:8px; font-size:1rem;"> |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg> Total Scans |
| </div> |
| </div> |
| <div class="st-card" style="padding:35px 25px; border-radius:8px; display:flex; flex-direction:column; justify-content:center; align-items:center; background:var(--panel); border:1px solid var(--border);"> |
| <div class="v" style="font-size:3.5rem; color:var(--txt); -webkit-text-fill-color:var(--txt); background:none; text-shadow:none; font-family:var(--font-main);">\${totalUnique}</div> |
| <div class="l" style="text-transform:none; font-weight:600; margin-top:15px; color:var(--txt-m); display:flex; align-items:center; gap:8px; font-size:1rem;"> |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg> Total Unique Scans |
| </div> |
| </div> |
| <div class="st-card" style="padding:35px 25px; border-radius:8px; display:flex; flex-direction:column; justify-content:center; align-items:center; background:var(--panel); border:1px solid var(--border);"> |
| <div class="v" style="font-size:3.5rem; color:var(--txt); -webkit-text-fill-color:var(--txt); background:none; text-shadow:none; font-family:var(--font-main);">\${totalVisits}</div> |
| <div class="l" style="text-transform:none; font-weight:600; margin-top:15px; color:var(--txt-m); display:flex; align-items:center; gap:8px; font-size:1rem;"> |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg> Total visits |
| </div> |
| </div> |
| `; |
| document.getElementById('stats-cards-container').innerHTML = cardsHtml; |
|
|
| |
| const ctx = document.getElementById('statsChart').getContext('2d'); |
| const today = new Date(); |
| const labels = []; |
| const dataPoints = []; |
| for(let i=6; i>=0; i--) { |
| const d = new Date(); |
| d.setDate(today.getDate() - i); |
| labels.push(d.toLocaleDateString('en-US', {month:'short', day:'2-digit'})); |
| dataPoints.push(0); |
| } |
| |
| scans.forEach(s => { |
| const dStr = new Date(s.scanned_at || s.created_at).toLocaleDateString('en-US', {month:'short', day:'2-digit'}); |
| const idx = labels.indexOf(dStr); |
| if(idx !== -1) dataPoints[idx]++; |
| }); |
|
|
| if (window.myStatsChart) { window.myStatsChart.destroy(); } |
| |
| |
| const isDark = document.body.classList.contains('dark-mode') || getComputedStyle(document.body).backgroundColor === 'rgb(3, 7, 18)'; |
| const gridColor = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'; |
| const textColor = isDark ? '#cbd5e1' : '#64748b'; |
|
|
| window.myStatsChart = new Chart(ctx, { |
| type: 'line', |
| data: { |
| labels: labels, |
| datasets: [{ |
| label: 'Totals', |
| data: dataPoints, |
| borderColor: '#3b82f6', |
| backgroundColor: 'rgba(37,99,235,0.1)', |
| borderWidth: 2, |
| pointBackgroundColor: '#fff', |
| pointBorderColor: '#3b82f6', |
| pointBorderWidth: 2, |
| pointRadius: 4, |
| fill: true, |
| tension: 0.1 |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| plugins: { legend: { display: false }, tooltip: { mode: 'index', intersect: false } }, |
| scales: { |
| y: { beginAtZero: true, grid: { color: gridColor }, ticks: { color: textColor, stepSize: 1 } }, |
| x: { grid: { display: false }, ticks: { color: textColor } } |
| } |
| } |
| }); |
|
|
| |
| const accList = [ |
| "Scans by day", "Scans by operating system", "Scans by country", |
| "Scans by region/city", "Scans by browser", "Scans by user language", "Scans by QR name" |
| ]; |
| let accHtml = ''; |
| accList.forEach(title => { |
| accHtml += ` |
| <div style="background:var(--panel); border:1px solid var(--border); border-radius:8px; overflow:hidden; transition:0.3s;" class="acc-box"> |
| <button style="width:100%; padding:20px; background:transparent; border:none; display:flex; justify-content:space-between; align-items:center; cursor:pointer; color:var(--txt); font-weight:700; font-size:1rem;" onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? 'block' : 'none'; this.querySelector('svg').style.transform = this.nextElementSibling.style.display === 'block' ? 'rotate(180deg)' : 'rotate(0deg)';"> |
| \${title} |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="transition:0.3s;"><polyline points="6 9 12 15 18 9"></polyline></svg> |
| </button> |
| <div style="display:none; padding: 0 20px 20px 20px; color:var(--txt-m); font-size:0.9rem; border-top:1px solid rgba(255,255,255,0.05);"> |
| <div style="padding-top:20px;">No data available for the selected period.</div> |
| </div> |
| </div> |
| `; |
| }); |
| document.getElementById('accordions-container').innerHTML = accHtml; |
| } |
|
|
| function renderTemplates() { |
| const v = document.getElementById('view-templates'); |
| if(v.innerHTML !== '') return; |
| |
| v.innerHTML = ` |
| <div class="db-hd"> |
| <div><h1>Template Gallery</h1><p>Start with a premium pre-designed template and customize it.</p></div> |
| </div> |
| |
| <div class="db-grid" style="margin-top:20px;"> |
| <div class="dbc" onclick="window.location.href='qr-editor.html?template=business_card'" style="cursor:pointer;"> |
| <div class="dbt" style="background: linear-gradient(135deg, #3b82f6, #2dd4bf);"><div style="font-size: 4rem;">💼</div></div> |
| <div class="dbb"><h3>Modern Business Card</h3><div class="ddt">Professional layout with gradient logo support</div></div> |
| </div> |
| |
| <div class="dbc" onclick="window.location.href='qr-editor.html?template=wifi_neon'" style="cursor:pointer;"> |
| <div class="dbt" style="background: linear-gradient(135deg, #10b981, #f59e0b);"><div style="font-size: 4rem;">📶</div></div> |
| <div class="dbb"><h3>Neon Wi-Fi Access</h3><div class="ddt">Vibrant styling for quick guest access</div></div> |
| </div> |
| |
| <div class="dbc" onclick="window.location.href='qr-editor.html?template=minimal_menu'" style="cursor:pointer;"> |
| <div class="dbt" style="background: linear-gradient(135deg, #ec4899, #8b5cf6);"><div style="font-size: 4rem;">🍽️</div></div> |
| <div class="dbb"><h3>Minimalist Menu</h3><div class="ddt">Clean and scannable design for restaurants</div></div> |
| </div> |
| |
| <div class="dbc" onclick="window.location.href='qr-editor.html?template=social_hub'" style="cursor:pointer;"> |
| <div class="dbt" style="background: linear-gradient(135deg, #6366f1, #a855f7);"><div style="font-size: 4rem;">🔗</div></div> |
| <div class="dbb"><h3>Social Media Hub</h3><div class="ddt">Optimized for Linktree or social profiles</div></div> |
| </div> |
| </div> |
| `; |
| } |
| function renderDomains() { |
| const v = document.getElementById('view-domains'); |
| if(v.innerHTML !== '') return; |
| |
| v.innerHTML = ` |
| <div class="db-hd"> |
| <div><h1>Custom Domains</h1><p>Connect your own domain to brand your dynamic short links.</p></div> |
| </div> |
| |
| <div style="background: var(--panel); border: 1px solid var(--border); border-radius: 20px; padding: 30px; margin-top: 20px;"> |
| <h3 style="margin-bottom: 15px; color: var(--txt);">Add New Domain</h3> |
| <div style="display: flex; gap: 15px; flex-wrap: wrap;"> |
| <input type="text" placeholder="e.g. qr.yourcompany.com" style="flex: 1; min-width: 200px; background: rgba(0,0,0,0.2); border: 1px solid var(--border); border-radius: 12px; padding: 15px; color: var(--txt); font-size: 1rem;"> |
| <button class="btn-nx" onclick="alert('DNS configuration instructions would appear here. Please configure your CNAME record to point to domains.classoflearners.com')" style="margin:0; padding: 15px 30px; border-radius: 12px; font-size: 0.95rem;">Add Domain</button> |
| </div> |
| <p style="color: var(--txt-m); font-size: 0.85rem; margin-top: 10px;">You will need to update your DNS records (CNAME) with your registrar to complete setup.</p> |
| </div> |
| |
| <h3 style="margin: 40px 0 20px; color: var(--txt);">Connected Domains</h3> |
| |
| <div style="background: var(--panel); border: 1px solid var(--border); border-radius: 20px; overflow: hidden;"> |
| <div style="display: flex; justify-content: space-between; align-items: center; padding: 20px 30px; border-bottom: 1px solid var(--border);"> |
| <div style="display: flex; align-items: center; gap: 15px;"> |
| <div style="width: 10px; height: 10px; border-radius: 50%; background: var(--pri); box-shadow: 0 0 10px var(--glow);"></div> |
| <span style="font-weight: 700; color: var(--txt);">qr.classoflearners.com</span> |
| </div> |
| <span style="font-size: 0.8rem; background: rgba(16,185,129,0.1); color: var(--pri); padding: 5px 12px; border-radius: 20px; font-weight: 800;">Active</span> |
| </div> |
| |
| <div style="display: flex; justify-content: space-between; align-items: center; padding: 20px 30px;"> |
| <div style="display: flex; align-items: center; gap: 15px;"> |
| <div style="width: 10px; height: 10px; border-radius: 50%; background: #f59e0b; box-shadow: 0 0 10px rgba(245,158,11,0.5);"></div> |
| <span style="font-weight: 700; color: var(--txt);">link.mybusiness.co</span> |
| </div> |
| <span style="font-size: 0.8rem; background: rgba(245,158,11,0.1); color: #f59e0b; padding: 5px 12px; border-radius: 20px; font-weight: 800;">Pending DNS</span> |
| </div> |
| </div> |
| `; |
| } |
| function editQR(id){ window.location.href = 'qr-editor.html?id=' + id; } |
| function reDL(id){ |
| let q=null; |
| for(let i=0;i<saved.length;i++){ if(saved[i].id===id){q=saved[i];break;} } |
| if(!q)return; |
| if(typeof QRCodeStyling === 'undefined') { toast('Library loading...', ''); return; } |
| const dlOpts = Object.assign({}, q.opts, {width:800,height:800}); |
| new QRCodeStyling(dlOpts).download({name:'qr-'+q.type,extension:'png'}); |
| } |
|
|