// ────────────────────────────── static/analytics.js ────────────────────────────── (function() { // DOM elements const analyticsSection = document.getElementById('analytics-section'); const analyticsPeriod = document.getElementById('analytics-period'); const refreshAnalytics = document.getElementById('refresh-analytics'); const debugAnalytics = document.getElementById('debug-analytics'); const modelUsageChart = document.getElementById('model-usage-chart'); const agentUsageChart = document.getElementById('agent-usage-chart'); const dailyTrendsChart = document.getElementById('daily-trends-chart'); const usageSummary = document.getElementById('usage-summary'); // State let currentAnalyticsData = null; let isAnalyticsVisible = false; // Initialize init(); function init() { setupEventListeners(); // Check if analytics section is already visible on page load checkAnalyticsVisibility(); // Load analytics when section becomes visible document.addEventListener('sectionChanged', (event) => { console.log('[ANALYTICS] Section changed to:', event.detail.section); if (event.detail.section === 'analytics') { isAnalyticsVisible = true; console.log('[ANALYTICS] Analytics section is now visible'); // Small delay to ensure DOM is ready setTimeout(() => { loadAnalytics(); }, 100); } else { isAnalyticsVisible = false; console.log('[ANALYTICS] Analytics section is now hidden'); } }); // Also listen for direct analytics section visibility changes if (analyticsSection) { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'attributes' && mutation.attributeName === 'style') { const isVisible = analyticsSection.style.display !== 'none' && analyticsSection.style.display !== ''; console.log('[ANALYTICS] Direct visibility change detected:', isVisible); if (isVisible && !isAnalyticsVisible) { isAnalyticsVisible = true; setTimeout(() => { loadAnalytics(); }, 100); } else if (!isVisible) { isAnalyticsVisible = false; } } }); }); observer.observe(analyticsSection, { attributes: true, attributeFilter: ['style'] }); } } function checkAnalyticsVisibility() { if (analyticsSection) { const isVisible = analyticsSection.style.display !== 'none' && analyticsSection.style.display !== ''; console.log('[ANALYTICS] Initial visibility check:', isVisible); if (isVisible) { isAnalyticsVisible = true; setTimeout(() => { loadAnalytics(); }, 200); } } } function setupEventListeners() { // Period change if (analyticsPeriod) { analyticsPeriod.addEventListener('change', () => { if (isAnalyticsVisible) { loadAnalytics(); } }); } // Refresh button if (refreshAnalytics) { refreshAnalytics.addEventListener('click', () => { loadAnalytics(); }); } // Debug button if (debugAnalytics) { debugAnalytics.addEventListener('click', () => { console.log('[ANALYTICS] Debug button clicked'); console.log('[ANALYTICS] Current state:', { isAnalyticsVisible, analyticsSection: analyticsSection ? analyticsSection.style.display : 'not found', user: window.__sb_get_user ? window.__sb_get_user() : 'not available' }); forceLoadAnalytics(); }); } } async function loadAnalytics() { console.log('[ANALYTICS] loadAnalytics called, isAnalyticsVisible:', isAnalyticsVisible); if (!isAnalyticsVisible) { console.log('[ANALYTICS] Analytics not visible, skipping load'); return; } const user = window.__sb_get_user(); console.log('[ANALYTICS] User:', user); if (!user) { showAnalyticsError('Please sign in to view analytics'); return; } const period = analyticsPeriod ? analyticsPeriod.value : '30'; console.log('[ANALYTICS] Loading analytics for period:', period); try { showAnalyticsLoading(); // First test if analytics system is working console.log('[ANALYTICS] Testing analytics system...'); const testResponse = await fetch('/analytics/test'); const testData = await testResponse.json(); console.log('[ANALYTICS] Test response:', testData); if (!testData.success) { throw new Error(`Analytics system not working: ${testData.message}`); } const url = `/analytics/user?user_id=${encodeURIComponent(user.user_id)}&days=${period}`; console.log('[ANALYTICS] Fetching from URL:', url); const response = await fetch(url); console.log('[ANALYTICS] Response status:', response.status); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); console.log('[ANALYTICS] Response data:', data); if (data.success) { currentAnalyticsData = data.data; renderAnalytics(data.data); } else { throw new Error(data.message || 'Failed to load analytics'); } } catch (error) { console.error('Analytics loading error:', error); showAnalyticsError(`Failed to load analytics: ${error.message}`); } } function showAnalyticsLoading() { const loadingHtml = `
Loading analytics...
`; if (modelUsageChart) modelUsageChart.innerHTML = loadingHtml; if (agentUsageChart) agentUsageChart.innerHTML = loadingHtml; if (dailyTrendsChart) dailyTrendsChart.innerHTML = loadingHtml; if (usageSummary) usageSummary.innerHTML = loadingHtml; } function showAnalyticsError(message) { const errorHtml = `
${message}
`; if (modelUsageChart) modelUsageChart.innerHTML = errorHtml; if (agentUsageChart) agentUsageChart.innerHTML = errorHtml; if (dailyTrendsChart) dailyTrendsChart.innerHTML = errorHtml; if (usageSummary) usageSummary.innerHTML = errorHtml; } function renderAnalytics(data) { renderModelUsage(data.model_usage); renderAgentUsage(data.agent_usage); renderDailyTrends(data.daily_usage); renderUsageSummary(data); } function renderModelUsage(modelUsage) { if (!modelUsageChart) return; if (!modelUsage || modelUsage.length === 0) { modelUsageChart.innerHTML = '
No model usage data available
'; return; } // Sort by usage count const sortedModels = modelUsage.sort((a, b) => b.count - a.count); const totalUsage = sortedModels.reduce((sum, model) => sum + model.count, 0); let html = '
'; sortedModels.forEach(model => { const percentage = totalUsage > 0 ? Math.round((model.count / totalUsage) * 100) : 0; const lastUsed = new Date(model.last_used * 1000).toLocaleDateString(); const modelName = model.model_name || model._id; const provider = model.provider || 'unknown'; html += `
${modelName}
${provider}
${model.count} requests
${percentage}%
Last used: ${lastUsed}
`; }); html += '
'; modelUsageChart.innerHTML = html; } function renderAgentUsage(agentUsage) { if (!agentUsageChart) return; if (!agentUsage || agentUsage.length === 0) { agentUsageChart.innerHTML = '
No agent usage data available
'; return; } // Sort by usage count const sortedAgents = agentUsage.sort((a, b) => b.count - a.count); const totalUsage = sortedAgents.reduce((sum, agent) => sum + agent.count, 0); let html = '
'; sortedAgents.forEach(agent => { const percentage = totalUsage > 0 ? Math.round((agent.count / totalUsage) * 100) : 0; const lastUsed = new Date(agent.last_used * 1000).toLocaleDateString(); const actions = agent.actions ? agent.actions.join(', ') : 'N/A'; html += `
${agent._id}
Actions: ${actions}
${agent.count} requests
${percentage}%
Last used: ${lastUsed}
`; }); html += '
'; agentUsageChart.innerHTML = html; } function renderDailyTrends(dailyUsage) { if (!dailyTrendsChart) return; if (!dailyUsage || dailyUsage.length === 0) { dailyTrendsChart.innerHTML = '
No daily usage data available
'; return; } // Sort by date const sortedDaily = dailyUsage.sort((a, b) => { const dateA = new Date(a._id.year, a._id.month - 1, a._id.day); const dateB = new Date(b._id.year, b._id.month - 1, b._id.day); return dateA - dateB; }); const maxUsage = Math.max(...sortedDaily.map(d => d.total_requests)); let html = ''; dailyTrendsChart.innerHTML = html; } function renderUsageSummary(data) { if (!usageSummary) return; const totalRequests = data.total_requests || 0; const periodDays = data.period_days || 30; const avgPerDay = Math.round(totalRequests / periodDays * 10) / 10; const modelCount = data.model_usage ? data.model_usage.length : 0; const agentCount = data.agent_usage ? data.agent_usage.length : 0; const mostUsedModel = data.model_usage && data.model_usage.length > 0 ? data.model_usage[0] : null; const mostUsedAgent = data.agent_usage && data.agent_usage.length > 0 ? data.agent_usage[0] : null; let html = `
${totalRequests}
Total Requests
${avgPerDay}
Avg per Day
${modelCount}
Models Used
${agentCount}
Agents Used
`; if (mostUsedModel) { html += `
Most Used Model:
${mostUsedModel._id} (${mostUsedModel.count} times)
`; } if (mostUsedAgent) { html += `
Most Used Agent:
${mostUsedAgent._id} (${mostUsedAgent.count} times)
`; } html += '
'; usageSummary.innerHTML = html; } // Manual trigger function for debugging function forceLoadAnalytics() { console.log('[ANALYTICS] Force loading analytics...'); isAnalyticsVisible = true; loadAnalytics(); } // Expose functions for external use window.__sb_load_analytics = loadAnalytics; window.__sb_force_load_analytics = forceLoadAnalytics; window.__sb_show_analytics_section = () => { console.log('[ANALYTICS] Showing analytics section...'); if (analyticsSection) { analyticsSection.style.display = 'block'; isAnalyticsVisible = true; // Trigger analytics loading setTimeout(() => { loadAnalytics(); }, 100); } }; window.__sb_hide_analytics_section = () => { console.log('[ANALYTICS] Hiding analytics section...'); if (analyticsSection) { analyticsSection.style.display = 'none'; isAnalyticsVisible = false; } }; })();