| Short URL | Destination | Clicks | Created | Tags | Status | Actions | |
|---|---|---|---|---|---|---|---|
| Click Refresh to load links | |||||||
| Short URL | Destination | Clicks | Created | Tags | Status | Actions | |
|---|---|---|---|---|---|---|---|
| Click Refresh to load links | |||||||
| Rank | Short URL | Clicks | Last Click |
|---|---|---|---|
| Loading... | |||
'); } async function deleteLink(slug) { if (!confirm('Delete link ' + slug + '?')) return; const {res, data} = await apiFetch('/api/links/' + slug + '/delete', {method:'POST'}); if (res.ok) { showMsg('msg-dash', 'Link deleted successfully', 'ok'); loadDashboard(); } else { showMsg('msg-dash', data.error || 'Delete failed', 'err'); } } // ===== LINK CREATOR (Screen 2) ===== async function createLink() { const url = document.getElementById('create-url').value.trim(); if (!url) { showMsg('msg-create', 'Destination URL is required', 'err'); return; } const domain = document.getElementById('create-domain').value; const slug = document.getElementById('create-slug').value.trim(); const tags = document.getElementById('create-tags').value.trim(); const expiry = document.getElementById('create-expiry').value; const maxClicks = document.getElementById('create-max-clicks').value; // Build UTM string const utmSource = document.getElementById('utm-source').value; const utmMedium = document.getElementById('utm-medium').value; const utmCampaign = document.getElementById('utm-campaign').value; const utmContent = document.getElementById('utm-content').value; let finalUrl = url; const utmParams = []; if (utmSource) utmParams.push(`utm_source=${encodeURIComponent(utmSource)}`); if (utmMedium) utmParams.push(`utm_medium=${encodeURIComponent(utmMedium)}`); if (utmCampaign) utmParams.push(`utm_campaign=${encodeURIComponent(utmCampaign)}`); if (utmContent) utmParams.push(`utm_content=${encodeURIComponent(utmContent)}`); if (utmParams.length) { finalUrl += (url.includes('?') ? '&' : '?') + utmParams.join('&'); } const body = { url: finalUrl, slug: slug || undefined, domain: domain, tags: tags || undefined, expires_at: expiry || undefined, max_clicks: maxClicks ? parseInt(maxClicks) : undefined }; showMsg('msg-create', 'Creating link...', 'ok'); try { const {res, data} = await apiFetch('/api/links/create', {method:'POST', body: JSON.stringify(body)}); if (!res.ok) { showMsg('msg-create', data.error || 'Creation failed', 'err'); return; } showMsg('msg-create', `Link created: ${data.short_url || data.slug}`, 'ok'); // Clear form document.getElementById('create-url').value = ''; document.getElementById('create-slug').value = ''; document.getElementById('create-tags').value = ''; document.getElementById('create-expiry').value = ''; document.getElementById('create-max-clicks').value = ''; document.getElementById('utm-source').value = ''; document.getElementById('utm-medium').value = ''; document.getElementById('utm-campaign').value = ''; document.getElementById('utm-content').value = ''; // Optionally switch back to dashboard setTimeout(() => showScreen('s1'), 2000); } catch(e) { showMsg('msg-create', e.message, 'err'); } } // ===== ANALYTICS (Screen 3) ===== async function loadAnalytics() { showLoading('loading-analytics', true); showMsg('msg-analytics', '', ''); try { const {res, data} = await apiFetch('/api/stats/overview'); showLoading('loading-analytics', false); if (!res.ok) { showMsg('msg-analytics', data.error || 'Error loading analytics', 'err'); return; } // Timeline chart const timelineEl = document.getElementById('timeline-chart'); const timeseries = data.timeseries || []; if (timeseries.length === 0) { timelineEl.innerHTML = '
'; } else { const maxClicks = Math.max(...timeseries.map(t => t.clicks || 0), 1); timelineEl.innerHTML = timeseries.slice(-30).map(t => { const height = Math.max(4, (t.clicks / maxClicks) * 80); const label = new Date(t.date).toLocaleDateString('en-US', {month:'short', day:'numeric'}).replace(',',''); return `
`; }).join(''); } // Device breakdown const deviceEl = document.getElementById('device-chart'); const devices = data.device || []; if (devices.length === 0) { deviceEl.innerHTML = '
'; } else { const totalDevice = devices.reduce((s,d) => s + d.count, 0); deviceEl.innerHTML = devices.map(d => { const pct = totalDevice ? Math.round((d.count / totalDevice) * 100) : 0; return `
`; }).join(''); } // Referrer breakdown const referrerEl = document.getElementById('referrer-chart'); const referrers = data.referrer || []; if (referrers.length === 0) { referrerEl.innerHTML = '
'; } else { const totalRef = referrers.reduce((s,r) => s + r.count, 0); referrerEl.innerHTML = referrers.slice(0,5).map(r => { const pct = totalRef ? Math.round((r.count / totalRef) * 100) : 0; return `
`; }).join(''); } // Geo breakdown const geoEl = document.getElementById('geo-chart'); const geo = data.geo || []; if (geo.length === 0) { geoEl.innerHTML = '
'; } else { const totalGeo = geo.reduce((s,g) => s + g.count, 0); geoEl.innerHTML = geo.slice(0,5).map(g => { const pct = totalGeo ? Math.round((g.count / totalGeo) * 100) : 0; return `
`; }).join(''); } // Top links const topLinksEl = document.getElementById('top-links-tbody'); const topLinks = (data.top_links || []).slice(0,5); if (topLinks.length === 0) { topLinksEl.innerHTML = '
'; } else { topLinksEl.innerHTML = topLinks.map((link, i) => `
`).join(''); } } catch(e) { showLoading('loading-analytics', false); showMsg('msg-analytics', e.message, 'err'); } } // ===== INIT ===== window.addEventListener('DOMContentLoaded', () => { // Load dashboard on first paint loadDashboard(); // Time tab switcher (Analytics screen) document.querySelectorAll('.time-tab').forEach(tab => { tab.addEventListener('click', function() { document.querySelectorAll('.time-tab').forEach(t => t.classList.remove('active')); this.classList.add('active'); // In a full implementation, this would reload analytics with the selected range }); }); });