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=`
🔳
Workspace Empty
No assets found for this filter.
`;
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=`${q.thumb?`

`:'
🔳
'}
${q.typeIcon} ${q.typeName}
${shortC}
📅 ${q.date}
`;
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-1){
saved.splice(idx,1);
async function renderStats() {
const v = document.getElementById('view-stats');
if(v.innerHTML !== '') return;
// Initial loading state
v.innerHTML = `
You can set your time zone.
Current time zone: Asia/Calcutta
Jun 3, 2026 - Jun 10, 2026
Time zone: Asia/Calcutta ✎
Analyzed period: 03-06-2026 to 10-06-2026
Syncing analytics data from cloud...
`;
// Initialize Supabase if needed
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;
// Calculate unique scans (by visitor_hash)
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; // Mock visits
const cardsHtml = `
\${totalQRs}
Total QR Codes
`;
document.getElementById('stats-cards-container').innerHTML = cardsHtml;
// Render Chart.js
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(); }
// Add grid color variable handling for light/dark mode
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 } }
}
}
});
// Render accordions
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 += `
No data available for the selected period.
`;
});
document.getElementById('accordions-container').innerHTML = accHtml;
}
function renderTemplates() {
const v = document.getElementById('view-templates');
if(v.innerHTML !== '') return;
v.innerHTML = `
Template Gallery
Start with a premium pre-designed template and customize it.
Modern Business Card
Professional layout with gradient logo support
Neon Wi-Fi Access
Vibrant styling for quick guest access
Minimalist Menu
Clean and scannable design for restaurants
Social Media Hub
Optimized for Linktree or social profiles
`;
}
function renderDomains() {
const v = document.getElementById('view-domains');
if(v.innerHTML !== '') return;
v.innerHTML = `
Custom Domains
Connect your own domain to brand your dynamic short links.
Connected Domains
`;
}
function editQR(id){ window.location.href = 'qr-editor.html?id=' + id; }
function reDL(id){
let q=null;
for(let i=0;i