Analytics Dashboard

Real-time website analytics with UTM tracking

Real-time Visitors

0
LIVE

Visits Today

0
Today's traffic

Visits (30 Days)

0
Last 30 days

Unique Visitors

0
Unique sessions

Top UTM Source

-
Leading source

Page Views (Last 24 Hours)

UTM Sources

UTM Mediums

Devices

Traffic Sources (Source / Medium)

Loading...

Top Pages

Loading...

Real-time Visitors

Loading...
'; const code = '\n' + scriptOpen + '\n' + 'window.analyticsConfig={trackingUrl:\'' + trackUrl + '\'};\n' + scriptClose + '\n' + scriptOpen + ' async src="' + scriptUrl + '">' + scriptClose; codeBlock.textContent = code; modal.classList.add('active'); } catch (err) { console.error('Error:', err); } }; window.closeCodeModal = function() { const modal = document.getElementById('codeModal'); if (modal) modal.classList.remove('active'); }; window.copyCode = function(id) { try { const codeElement = document.getElementById(id); if (!codeElement) { console.error('Code element not found:', id); return; } const code = codeElement.textContent; if (!code) { console.error('No code to copy'); return; } navigator.clipboard.writeText(code).then(() => { const btn = event.target; if (btn) { const original = btn.textContent; btn.textContent = 'Copied!'; setTimeout(() => { if (btn) btn.textContent = original; }, 2000); } }).catch(err => { console.error('Failed to copy:', err); alert('Failed to copy. Please select and copy manually.'); }); } catch (err) { console.error('copyCode error:', err); } }; window.checkTracking = function() { const modal = document.getElementById('checkModal'); if (modal) modal.classList.add('active'); }; window.closeCheckModal = function() { const modal = document.getElementById('checkModal'); const statusDiv = document.getElementById('checkStatus'); if (modal) modal.classList.remove('active'); if (statusDiv) statusDiv.style.display = 'none'; }; window.testTracking = async function() { const testUrlInput = document.getElementById('testUrl'); const statusDiv = document.getElementById('checkStatus'); if (!testUrlInput || !statusDiv) { console.error('Test elements not found'); return; } const testUrl = testUrlInput.value; if (!testUrl) { statusDiv.className = 'check-status error'; statusDiv.style.display = 'block'; statusDiv.innerHTML = ' Please enter a test URL'; return; } statusDiv.className = 'check-status'; statusDiv.style.display = 'block'; statusDiv.innerHTML = ' Checking...'; try { const scriptUrl = testUrl.replace(/\/$/, '') + '/analytics.js'; const response = await fetch(scriptUrl, { method: 'HEAD' }); if (response.ok) { statusDiv.className = 'check-status success'; statusDiv.innerHTML = ' Tracking script is accessible!
Visit your website and check the dashboard for real-time data.'; } else { statusDiv.className = 'check-status error'; statusDiv.innerHTML = ' Script not found. Make sure analytics.js is uploaded to your server.'; } } catch (error) { statusDiv.className = 'check-status error'; statusDiv.innerHTML = ' Could not check. Make sure the URL is correct and CORS is enabled.'; }; // Charts let pageViewsChart, utmSourcesChart, utmMediumsChart, devicesChart; function initCharts() { // Page Views Chart const ctx1 = document.getElementById('pageViewsChart').getContext('2d'); pageViewsChart = new Chart(ctx1, { type: 'line', data: { labels: [], datasets: [{ label: 'Page Views', data: [], borderColor: '#4285f4', backgroundColor: 'rgba(66, 133, 244, 0.1)', borderWidth: 3, fill: true, tension: 0.4, pointRadius: 4, pointHoverRadius: 6, pointBackgroundColor: '#4285f4', pointBorderColor: '#fff', pointBorderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0,0,0,0.8)', padding: 12, titleFont: { size: 14, weight: 'bold' }, bodyFont: { size: 13 }, cornerRadius: 8 } }, scales: { y: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#5f6368' } }, x: { grid: { display: false }, ticks: { color: '#5f6368' } } } } }); // UTM Sources Chart const ctx2 = document.getElementById('utmSourcesChart').getContext('2d'); utmSourcesChart = new Chart(ctx2, { type: 'doughnut', data: { labels: [], datasets: [{ data: [], backgroundColor: [ '#4285f4', '#34a853', '#ea4335', '#fbbc04', '#667eea', '#764ba2', '#f093fb', '#4facfe' ], borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { position: 'bottom', labels: { padding: 15, font: { size: 12 }, usePointStyle: true } }, tooltip: { backgroundColor: 'rgba(0,0,0,0.8)', padding: 12, cornerRadius: 8 } } } }); // UTM Mediums Chart const ctx3 = document.getElementById('utmMediumsChart').getContext('2d'); utmMediumsChart = new Chart(ctx3, { type: 'bar', data: { labels: [], datasets: [{ label: 'Visits', data: [], backgroundColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', backgroundColor: '#4285f4', borderRadius: 8, borderSkipped: false }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0,0,0,0.8)', padding: 12, cornerRadius: 8 } }, scales: { y: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#5f6368' } }, x: { grid: { display: false }, ticks: { color: '#5f6368' } } } } }); // Devices Chart const ctx4 = document.getElementById('devicesChart').getContext('2d'); devicesChart = new Chart(ctx4, { type: 'pie', data: { labels: [], datasets: [{ data: [], backgroundColor: ['#4285f4', '#34a853', '#ea4335'], borderWidth: 3, borderColor: '#fff' }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { position: 'bottom', labels: { padding: 15, font: { size: 12 }, usePointStyle: true } }, tooltip: { backgroundColor: 'rgba(0,0,0,0.8)', padding: 12, cornerRadius: 8 } } } }); } // Fetch and update data async function fetchData() { try { // Overall stats const statsRes = await fetch('api.php?action=stats'); if (!statsRes.ok) { throw new Error('Failed to fetch stats'); } const statsData = await statsRes.json(); // Check for error in response if (statsData.error) { console.error('API Error:', statsData.error); showError('Database connection error. Please check your config.php settings.'); return; } const stats = statsData.stats; // Update with animation const realtimeEl = document.getElementById('realtime-count'); const visitsTodayEl = document.getElementById('visits-today'); const visits30dEl = document.getElementById('visits-30d'); const uniqueEl = document.getElementById('unique-visitors'); const topSourceEl = document.getElementById('top-source'); if (realtimeEl) { if (previousStats['realtime-count'] !== undefined) { animateValue(realtimeEl, previousStats['realtime-count'], stats.realtime_visitors, 500); } else { realtimeEl.textContent = stats.realtime_visitors.toLocaleString(); } } if (visitsTodayEl) { if (previousStats['visits-today'] !== undefined) { animateValue(visitsTodayEl, previousStats['visits-today'], stats.visits_today, 500); } else { visitsTodayEl.textContent = stats.visits_today.toLocaleString(); } } if (visits30dEl) { if (previousStats['visits-30d'] !== undefined) { animateValue(visits30dEl, previousStats['visits-30d'], stats.visits_30d, 500); } else { visits30dEl.textContent = stats.visits_30d.toLocaleString(); } } if (uniqueEl) { if (previousStats['unique-visitors'] !== undefined) { animateValue(uniqueEl, previousStats['unique-visitors'], stats.unique_visitors_30d, 500); } else { uniqueEl.textContent = stats.unique_visitors_30d.toLocaleString(); } } if (topSourceEl) { topSourceEl.textContent = stats.top_utm_source; } previousStats['realtime-count'] = stats.realtime_visitors; previousStats['visits-today'] = stats.visits_today; previousStats['visits-30d'] = stats.visits_30d; previousStats['unique-visitors'] = stats.unique_visitors_30d; // Real-time visitors try { const realtimeRes = await fetch('api.php?action=realtime_detail'); if (realtimeRes.ok) { const realtimeData = await realtimeRes.json(); if (realtimeData.visitors) updateRealtimeVisitors(realtimeData.visitors); } } catch (e) { console.error('Realtime error:', e); } // Page views chart try { const viewsRes = await fetch('api.php?action=page_views&period=24h'); if (viewsRes.ok) { const viewsData = await viewsRes.json(); if (viewsData.views) updatePageViewsChart(viewsData.views); } } catch (e) { console.error('Page views error:', e); } // UTM Sources try { const sourcesRes = await fetch('api.php?action=utm_sources'); if (sourcesRes.ok) { const sourcesData = await sourcesRes.json(); if (sourcesData.sources) updateUTMSourcesChart(sourcesData.sources); } } catch (e) { console.error('UTM sources error:', e); } // UTM Mediums try { const mediumsRes = await fetch('api.php?action=utm_mediums'); if (mediumsRes.ok) { const mediumsData = await mediumsRes.json(); if (mediumsData.mediums) updateUTMMediumsChart(mediumsData.mediums); } } catch (e) { console.error('UTM mediums error:', e); } // UTM Combined try { const combinedRes = await fetch('api.php?action=utm_combined'); if (combinedRes.ok) { const combinedData = await combinedRes.json(); if (combinedData.combined) updateUTMCombinedTable(combinedData.combined); } } catch (e) { console.error('UTM combined error:', e); } // Top Pages try { const pagesRes = await fetch('api.php?action=top_pages'); if (pagesRes.ok) { const pagesData = await pagesRes.json(); if (pagesData.pages) updateTopPagesTable(pagesData.pages); } } catch (e) { console.error('Top pages error:', e); } // Devices try { const devicesRes = await fetch('api.php?action=devices'); if (devicesRes.ok) { const devicesData = await devicesRes.json(); if (devicesData.devices) updateDevicesChart(devicesData.devices); } } catch (e) { console.error('Devices error:', e); } } catch (error) { console.error('Error fetching data:', error); // Don't show error for JSON parse errors on first load if (error.message && !error.message.includes('JSON')) { showError('Failed to load analytics data. Please check your database connection.'); } } } function showError(message) { // Show error in a user-friendly way const errorDiv = document.createElement('div'); errorDiv.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #ea4335; color: white; padding: 1rem 1.5rem; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); z-index: 10000; max-width: 400px;'; errorDiv.innerHTML = `⚠️ Error: ${message}`; document.body.appendChild(errorDiv); setTimeout(() => errorDiv.remove(), 5000); } function updatePageViewsChart(data) { pageViewsChart.data.labels = data.map(d => d.time); pageViewsChart.data.datasets[0].data = data.map(d => d.count); pageViewsChart.update(); } function updateUTMSourcesChart(data) { utmSourcesChart.data.labels = data.map(d => d.utm_source || 'Unknown'); utmSourcesChart.data.datasets[0].data = data.map(d => d.count); utmSourcesChart.update(); } function updateUTMMediumsChart(data) { utmMediumsChart.data.labels = data.map(d => d.utm_medium || 'Unknown'); utmMediumsChart.data.datasets[0].data = data.map(d => d.count); utmMediumsChart.update(); } function updateDevicesChart(data) { devicesChart.data.labels = data.map(d => d.device_type); devicesChart.data.datasets[0].data = data.map(d => d.count); devicesChart.update(); } function updateUTMCombinedTable(data) { const container = document.getElementById('utm-combined-table'); if (data.length === 0) { container.innerHTML = '

No traffic source data available

'; return; } let html = ''; data.forEach((item, index) => { html += ``; }); html += '
Source / MediumVisits
${item.source_medium} ${item.count.toLocaleString()}
'; container.innerHTML = html; } function updateTopPagesTable(data) { const container = document.getElementById('top-pages-table'); if (data.length === 0) { container.innerHTML = '

No page data available

'; return; } let html = ''; data.forEach((item, index) => { html += ``; }); html += '
Page URLTitleViews
${item.page_url} ${item.page_title || '-'} ${item.views.toLocaleString()}
'; container.innerHTML = html; } function updateRealtimeVisitors(visitors) { const container = document.getElementById('realtime-visitors'); if (visitors.length === 0) { container.innerHTML = '

No active visitors at the moment

'; return; } let html = ''; visitors.forEach((visitor, index) => { const utm = visitor.utm_source && visitor.utm_medium ? ` ${visitor.utm_source} / ${visitor.utm_medium}` : ''; const timeAgo = getTimeAgo(new Date(visitor.last_activity)); html += `
${visitor.page_title || visitor.page_url} ${utm}
${timeAgo}
`; }); container.innerHTML = html; } function getTimeAgo(date) { const seconds = Math.floor((new Date() - date) / 1000); if (seconds < 60) return 'Just now'; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h ago`; return date.toLocaleTimeString(); } // Animate numbers function animateValue(element, start, end, duration) { if (!element) return; let startTimestamp = null; const step = (timestamp) => { if (!startTimestamp) startTimestamp = timestamp; const progress = Math.min((timestamp - startTimestamp) / duration, 1); const current = Math.floor(progress * (end - start) + start); element.textContent = current.toLocaleString(); if (progress < 1) { window.requestAnimationFrame(step); } }; window.requestAnimationFrame(step); } let previousStats = {}; // Initialize when DOM is ready function initializeDashboard() { // Attach event listeners to buttons const getCodeBtn = document.getElementById('getCodeBtn'); if (getCodeBtn) { getCodeBtn.addEventListener('click', function(e) { e.preventDefault(); window.showCodeModal(); }); } const checkBtn = document.getElementById('checkBtn'); if (checkBtn) { checkBtn.addEventListener('click', function(e) { e.preventDefault(); window.checkTracking(); }); } const refreshBtn = document.getElementById('refreshBtn'); if (refreshBtn) { refreshBtn.addEventListener('click', function(e) { e.preventDefault(); fetchData(); }); } const closeCodeBtn = document.getElementById('closeCodeBtn'); if (closeCodeBtn) { closeCodeBtn.addEventListener('click', function(e) { e.preventDefault(); window.closeCodeModal(); }); } const closeCheckBtn = document.getElementById('closeCheckBtn'); if (closeCheckBtn) { closeCheckBtn.addEventListener('click', function(e) { e.preventDefault(); window.closeCheckModal(); }); } const copyCodeBtn = document.getElementById('copyCodeBtn'); if (copyCodeBtn) { copyCodeBtn.addEventListener('click', function(e) { e.preventDefault(); window.copyCode('trackingCode'); }); } const testTrackingBtn = document.getElementById('testTrackingBtn'); if (testTrackingBtn) { testTrackingBtn.addEventListener('click', function(e) { e.preventDefault(); window.testTracking(); }); } // Close modal on outside click const codeModal = document.getElementById('codeModal'); if (codeModal) { codeModal.addEventListener('click', function(e) { if (e.target === this) window.closeCodeModal(); }); } const checkModal = document.getElementById('checkModal'); if (checkModal) { checkModal.addEventListener('click', function(e) { if (e.target === this) window.closeCheckModal(); }); } // Update code when URL changes const serverUrlInput = document.getElementById('serverUrl'); if (serverUrlInput) { serverUrlInput.addEventListener('input', function() { const serverUrl = this.value.replace(/\/$/, ''); const codeBlock = document.getElementById('trackingCode'); if (codeBlock) { const trackUrl = serverUrl + '/track.php'; const scriptUrl = serverUrl + '/analytics.js'; const scriptTag1 = ''; const code = '\n' + scriptTag1 + '\n' + 'window.analyticsConfig={trackingUrl:\'' + trackUrl + '\'};\n' + scriptTag2 + '\n' + '