← back to Watches

analytics/dashboard.html

613 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Analytics Dashboard - Omega Watches</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: #333;
            padding: 20px;
        }

        .dashboard {
            max-width: 1400px;
            margin: 0 auto;
        }

        .header {
            background: white;
            padding: 30px;
            border-radius: 12px;
            margin-bottom: 20px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        .header h1 {
            font-size: 32px;
            margin-bottom: 10px;
            color: #1a202c;
        }

        .header p {
            color: #718096;
            font-size: 14px;
        }

        .controls {
            display: flex;
            gap: 10px;
            margin-top: 20px;
            flex-wrap: wrap;
        }

        .btn {
            padding: 10px 20px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            transition: all 0.3s;
        }

        .btn-primary {
            background: #667eea;
            color: white;
        }

        .btn-primary:hover {
            background: #5568d3;
        }

        .btn-secondary {
            background: #e2e8f0;
            color: #4a5568;
        }

        .btn-secondary:hover {
            background: #cbd5e0;
        }

        .metrics-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 20px;
            margin-bottom: 20px;
        }

        .metric-card {
            background: white;
            padding: 24px;
            border-radius: 12px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        .metric-value {
            font-size: 36px;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 8px;
        }

        .metric-label {
            font-size: 14px;
            color: #718096;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .metric-change {
            font-size: 12px;
            margin-top: 8px;
        }

        .metric-change.positive {
            color: #48bb78;
        }

        .metric-change.negative {
            color: #f56565;
        }

        .chart-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
            gap: 20px;
            margin-bottom: 20px;
        }

        .chart-card {
            background: white;
            padding: 24px;
            border-radius: 12px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        .chart-card h3 {
            font-size: 18px;
            margin-bottom: 20px;
            color: #1a202c;
        }

        .chart-container {
            position: relative;
            height: 300px;
        }

        .table-card {
            background: white;
            padding: 24px;
            border-radius: 12px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }

        .table-card h3 {
            font-size: 18px;
            margin-bottom: 20px;
            color: #1a202c;
        }

        table {
            width: 100%;
            border-collapse: collapse;
        }

        th, td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #e2e8f0;
        }

        th {
            background: #f7fafc;
            font-weight: 600;
            color: #4a5568;
            font-size: 12px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        tr:hover {
            background: #f7fafc;
        }

        .status-badge {
            padding: 4px 12px;
            border-radius: 12px;
            font-size: 12px;
            font-weight: 500;
        }

        .status-active {
            background: #c6f6d5;
            color: #22543d;
        }

        .status-inactive {
            background: #fed7d7;
            color: #742a2a;
        }

        .loading {
            text-align: center;
            padding: 40px;
            color: white;
            font-size: 18px;
        }

        .error {
            background: #fed7d7;
            color: #742a2a;
            padding: 20px;
            border-radius: 8px;
            margin-bottom: 20px;
        }

        .tabs {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            background: white;
            padding: 10px;
            border-radius: 12px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        .tab {
            padding: 10px 20px;
            border: none;
            background: transparent;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            color: #4a5568;
            border-radius: 6px;
            transition: all 0.3s;
        }

        .tab.active {
            background: #667eea;
            color: white;
        }

        .tab-content {
            display: none;
        }

        .tab-content.active {
            display: block;
        }
    </style>
</head>
<body>
    <div class="dashboard">
        <div class="header">
            <h1>📊 Analytics Dashboard</h1>
            <p>Real-time analytics for Omega Watches Price History</p>
            <div class="controls">
                <button class="btn btn-primary" onclick="refreshData()">🔄 Refresh</button>
                <button class="btn btn-secondary" onclick="exportData()">📥 Export Data</button>
                <button class="btn btn-secondary" onclick="window.open('http://45.61.58.125:7600', '_blank')">🌐 Visit Site</button>
                <select class="btn btn-secondary" id="timeRange" onchange="changeTimeRange()">
                    <option value="1">Last Hour</option>
                    <option value="24" selected>Last 24 Hours</option>
                    <option value="168">Last 7 Days</option>
                    <option value="720">Last 30 Days</option>
                </select>
            </div>
        </div>

        <div class="tabs">
            <button class="tab active" onclick="switchTab('overview')">Overview</button>
            <button class="tab" onclick="switchTab('events')">Events</button>
            <button class="tab" onclick="switchTab('funnels')">Funnels</button>
            <button class="tab" onclick="switchTab('cohorts')">Cohorts</button>
            <button class="tab" onclick="switchTab('abtests')">A/B Tests</button>
        </div>

        <div id="overview-tab" class="tab-content active">
            <div class="metrics-grid">
                <div class="metric-card">
                    <div class="metric-value" id="totalEvents">--</div>
                    <div class="metric-label">Total Events</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="uniqueUsers">--</div>
                    <div class="metric-label">Unique Users</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="uniqueSessions">--</div>
                    <div class="metric-label">Active Sessions</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="avgEventsPerUser">--</div>
                    <div class="metric-label">Avg Events/User</div>
                </div>
            </div>

            <div class="chart-grid">
                <div class="chart-card">
                    <h3>Top Events</h3>
                    <div class="chart-container">
                        <canvas id="topEventsChart"></canvas>
                    </div>
                </div>
                <div class="chart-card">
                    <h3>Events Timeline</h3>
                    <div class="chart-container">
                        <canvas id="timelineChart"></canvas>
                    </div>
                </div>
            </div>

            <div class="table-card">
                <h3>Recent Events</h3>
                <table id="recentEventsTable">
                    <thead>
                        <tr>
                            <th>Event</th>
                            <th>User ID</th>
                            <th>Timestamp</th>
                        </tr>
                    </thead>
                    <tbody></tbody>
                </table>
            </div>
        </div>

        <div id="events-tab" class="tab-content">
            <div class="chart-card">
                <h3>Event Breakdown</h3>
                <div class="chart-container" style="height: 400px;">
                    <canvas id="eventBreakdownChart"></canvas>
                </div>
            </div>
        </div>

        <div id="funnels-tab" class="tab-content">
            <div class="table-card">
                <h3>Funnel Performance</h3>
                <div id="funnelData">Loading...</div>
            </div>
        </div>

        <div id="cohorts-tab" class="tab-content">
            <div class="chart-card">
                <h3>Cohort Analysis</h3>
                <div id="cohortData">Loading...</div>
            </div>
        </div>

        <div id="abtests-tab" class="tab-content">
            <div class="table-card">
                <h3>A/B Test Results</h3>
                <div id="abTestData">Loading...</div>
            </div>
        </div>
    </div>

    <script>
        let dashboardData = null;
        let charts = {};

        async function loadDashboard() {
            try {
                const hours = document.getElementById('timeRange').value;
                const response = await fetch(`http://45.61.58.125:7650/api/analytics/dashboard?hours=${hours}`);
                dashboardData = await response.json();
                renderDashboard();
            } catch (error) {
                console.error('Failed to load dashboard:', error);
            }
        }

        function renderDashboard() {
            // Update metrics
            document.getElementById('totalEvents').textContent = dashboardData.summary.totalEvents.toLocaleString();
            document.getElementById('uniqueUsers').textContent = dashboardData.summary.uniqueUsers.toLocaleString();
            document.getElementById('uniqueSessions').textContent = dashboardData.summary.uniqueSessions.toLocaleString();
            document.getElementById('avgEventsPerUser').textContent =
                (dashboardData.summary.totalEvents / Math.max(1, dashboardData.summary.uniqueUsers)).toFixed(1);

            // Render top events chart
            renderTopEventsChart();

            // Render recent events table
            renderRecentEvents();

            // Render event breakdown
            renderEventBreakdown();
        }

        function renderTopEventsChart() {
            const ctx = document.getElementById('topEventsChart');

            if (charts.topEvents) {
                charts.topEvents.destroy();
            }

            charts.topEvents = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: dashboardData.topEvents.map(e => e.event),
                    datasets: [{
                        label: 'Event Count',
                        data: dashboardData.topEvents.map(e => e.count),
                        backgroundColor: 'rgba(102, 126, 234, 0.8)',
                        borderColor: 'rgba(102, 126, 234, 1)',
                        borderWidth: 1
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            display: false
                        }
                    },
                    scales: {
                        y: {
                            beginAtZero: true
                        }
                    }
                }
            });
        }

        function renderEventBreakdown() {
            const ctx = document.getElementById('eventBreakdownChart');

            if (charts.eventBreakdown) {
                charts.eventBreakdown.destroy();
            }

            const eventCounts = Object.entries(dashboardData.summary.eventCounts)
                .sort(([, a], [, b]) => b - a)
                .slice(0, 15);

            charts.eventBreakdown = new Chart(ctx, {
                type: 'doughnut',
                data: {
                    labels: eventCounts.map(([event]) => event),
                    datasets: [{
                        data: eventCounts.map(([, count]) => count),
                        backgroundColor: [
                            '#667eea', '#764ba2', '#f093fb', '#4facfe',
                            '#43e97b', '#fa709a', '#fee140', '#30cfd0',
                            '#a8edea', '#fed6e3', '#c3cfe2', '#ffecd2'
                        ]
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            position: 'right'
                        }
                    }
                }
            });
        }

        function renderRecentEvents() {
            const tbody = document.querySelector('#recentEventsTable tbody');
            tbody.innerHTML = '';

            dashboardData.recentEvents.forEach(event => {
                const row = tbody.insertRow();
                row.insertCell(0).textContent = event.eventName;
                row.insertCell(1).textContent = event.userId.substring(0, 12) + '...';
                row.insertCell(2).textContent = new Date(event.timestamp).toLocaleString();
            });
        }

        function switchTab(tabName) {
            // Update tab buttons
            document.querySelectorAll('.tab').forEach(tab => {
                tab.classList.remove('active');
            });
            event.target.classList.add('active');

            // Update tab content
            document.querySelectorAll('.tab-content').forEach(content => {
                content.classList.remove('active');
            });
            document.getElementById(`${tabName}-tab`).classList.add('active');

            // Load specific tab data
            if (tabName === 'funnels') {
                loadFunnelData();
            } else if (tabName === 'cohorts') {
                loadCohortData();
            } else if (tabName === 'abtests') {
                loadABTestData();
            }
        }

        async function loadFunnelData() {
            const container = document.getElementById('funnelData');
            container.innerHTML = 'Loading funnel data...';

            try {
                const funnels = ['watch_discovery', 'price_research', 'collection_analysis', 'investment_research'];
                let html = '';

                for (const funnelId of funnels) {
                    const response = await fetch(`http://45.61.58.125:7650/api/analytics/funnels/${funnelId}/conversions?days=30`);
                    const data = await response.json();

                    html += `<h4>${funnelId.replace(/_/g, ' ').toUpperCase()}</h4>`;
                    html += '<table><thead><tr><th>Step</th><th>Users</th><th>Conversion Rate</th><th>Drop-off</th></tr></thead><tbody>';

                    data.conversions.forEach(step => {
                        html += `<tr>
                            <td>${step.step}</td>
                            <td>${step.users}</td>
                            <td>${step.conversionRate}%</td>
                            <td>${step.dropoffRate}%</td>
                        </tr>`;
                    });

                    html += '</tbody></table><br>';
                }

                container.innerHTML = html;
            } catch (error) {
                container.innerHTML = 'Error loading funnel data';
            }
        }

        async function loadCohortData() {
            const container = document.getElementById('cohortData');
            container.innerHTML = 'Loading cohort data...';

            try {
                const response = await fetch('http://45.61.58.125:7650/api/analytics/cohorts/summary');
                const data = await response.json();

                let html = '<table><thead><tr><th>Cohort Month</th><th>Users</th><th>Avg Visits</th><th>Avg Actions</th><th>Engagement Score</th></tr></thead><tbody>';

                data.cohorts.forEach(cohort => {
                    html += `<tr>
                        <td>${cohort.month}</td>
                        <td>${cohort.users}</td>
                        <td>${cohort.avgVisitsPerUser}</td>
                        <td>${cohort.avgActionsPerUser}</td>
                        <td>${cohort.avgEngagementScore}</td>
                    </tr>`;
                });

                html += '</tbody></table>';
                container.innerHTML = html;
            } catch (error) {
                container.innerHTML = 'Error loading cohort data';
            }
        }

        async function loadABTestData() {
            const container = document.getElementById('abTestData');
            container.innerHTML = 'Loading A/B test data...';

            try {
                const experiments = ['price_chart_layout', 'watch_card_design', 'cta_button_color', 'search_algorithm'];
                let html = '';

                for (const experimentId of experiments) {
                    const response = await fetch(`http://45.61.58.125:7650/api/analytics/ab-tests/${experimentId}/results`);
                    const data = await response.json();

                    html += `<h4>${experimentId.replace(/_/g, ' ').toUpperCase()}</h4>`;
                    html += '<table><thead><tr><th>Variant</th><th>Users</th><th>Goals</th><th>Metrics</th></tr></thead><tbody>';

                    data.variants.forEach(variant => {
                        html += `<tr>
                            <td>${variant.variantId}</td>
                            <td>${variant.users}</td>
                            <td>${JSON.stringify(variant.goals)}</td>
                            <td>${JSON.stringify(variant.metrics)}</td>
                        </tr>`;
                    });

                    html += '</tbody></table><br>';
                }

                container.innerHTML = html;
            } catch (error) {
                container.innerHTML = 'Error loading A/B test data';
            }
        }

        function refreshData() {
            loadDashboard();
        }

        function changeTimeRange() {
            loadDashboard();
        }

        async function exportData() {
            window.open('http://45.61.58.125:7650/api/analytics/export', '_blank');
        }

        // Load dashboard on page load
        loadDashboard();

        // Auto-refresh every 30 seconds
        setInterval(loadDashboard, 30000);
    </script>
</body>
</html>