// Analytics Page Module class AnalyticsPage { constructor() { this.filters = { dateRange: '7d', client: 'all', provider: 'all' }; this.init(); } async init() { // Load initial data await this.loadFilters(); await this.loadCharts(); await this.loadUsageData(); // Setup event listeners this.setupEventListeners(); } async loadFilters() { try { // Load clients for filter dropdown // In a real app, this would fetch from /api/clients const clients = [ { id: 'client-1', name: 'Web Application' }, { id: 'client-2', name: 'Mobile App' }, { id: 'client-3', name: 'API Integration' }, { id: 'client-4', name: 'Internal Tools' }, { id: 'client-5', name: 'Testing Suite' } ]; this.renderClientFilter(clients); } catch (error) { console.error('Error loading filters:', error); } } renderClientFilter(clients) { const select = document.getElementById('client-filter'); if (!select) return; // Clear existing options except "All Clients" while (select.options.length > 1) { select.remove(1); } // Add client options clients.forEach(client => { const option = document.createElement('option'); option.value = client.id; option.textContent = client.name; select.appendChild(option); }); } async loadCharts() { await this.loadAnalyticsChart(); await this.loadClientsChart(); await this.loadModelsChart(); } async loadAnalyticsChart() { try { // Generate demo data const labels = window.chartManager.generateDateLabels(7); const data = { labels: labels, datasets: [ { label: 'Requests', data: labels.map(() => Math.floor(Math.random() * 1000) + 500), color: '#3b82f6', fill: true }, { label: 'Tokens', data: labels.map(() => Math.floor(Math.random() * 100000) + 50000), color: '#10b981', fill: true }, { label: 'Cost ($)', data: labels.map(() => Math.random() * 50 + 10), color: '#f59e0b', fill: true } ] }; // Create chart window.chartManager.createLineChart('analytics-chart', data, { scales: { y: { ticks: { callback: function(value) { return value.toLocaleString(); } } } } }); } catch (error) { console.error('Error loading analytics chart:', error); } } async loadClientsChart() { try { const data = { labels: ['Web App', 'Mobile App', 'API Integration', 'Internal Tools', 'Testing'], datasets: [{ label: 'Requests', data: [45, 25, 15, 10, 5], color: '#3b82f6' }] }; window.chartManager.createHorizontalBarChart('clients-chart', data); } catch (error) { console.error('Error loading clients chart:', error); } } async loadModelsChart() { try { const data = { labels: ['gpt-4', 'gpt-3.5-turbo', 'gemini-pro', 'deepseek-chat', 'grok-beta'], data: [35, 30, 20, 10, 5], colors: ['#3b82f6', '#60a5fa', '#10b981', '#f59e0b', '#8b5cf6'] }; window.chartManager.createDoughnutChart('models-chart', data); } catch (error) { console.error('Error loading models chart:', error); } } async loadUsageData() { try { // In a real app, this would fetch from /api/usage/detailed const usageData = [ { date: '2024-01-15', client: 'Web App', provider: 'OpenAI', model: 'gpt-4', requests: 245, tokens: 125000, cost: 12.50 }, { date: '2024-01-15', client: 'Mobile App', provider: 'Gemini', model: 'gemini-pro', requests: 180, tokens: 89000, cost: 8.90 }, { date: '2024-01-15', client: 'API Integration', provider: 'OpenAI', model: 'gpt-3.5-turbo', requests: 320, tokens: 156000, cost: 15.60 }, { date: '2024-01-14', client: 'Web App', provider: 'OpenAI', model: 'gpt-4', requests: 210, tokens: 110000, cost: 11.00 }, { date: '2024-01-14', client: 'Internal Tools', provider: 'DeepSeek', model: 'deepseek-chat', requests: 95, tokens: 48000, cost: 4.80 }, { date: '2024-01-14', client: 'Testing Suite', provider: 'Grok', model: 'grok-beta', requests: 45, tokens: 22000, cost: 2.20 }, { date: '2024-01-13', client: 'Web App', provider: 'OpenAI', model: 'gpt-4', requests: 195, tokens: 98000, cost: 9.80 }, { date: '2024-01-13', client: 'Mobile App', provider: 'Gemini', model: 'gemini-pro', requests: 165, tokens: 82000, cost: 8.20 }, { date: '2024-01-13', client: 'API Integration', provider: 'OpenAI', model: 'gpt-3.5-turbo', requests: 285, tokens: 142000, cost: 14.20 }, { date: '2024-01-12', client: 'Web App', provider: 'OpenAI', model: 'gpt-4', requests: 230, tokens: 118000, cost: 11.80 } ]; this.renderUsageTable(usageData); } catch (error) { console.error('Error loading usage data:', error); } } renderUsageTable(data) { const tableBody = document.querySelector('#usage-table tbody'); if (!tableBody) return; tableBody.innerHTML = data.map(row => ` ${row.date} ${row.client} ${row.provider} ${row.model} ${row.requests.toLocaleString()} ${row.tokens.toLocaleString()} $${row.cost.toFixed(2)} `).join(''); } setupEventListeners() { // Filter controls const dateRangeSelect = document.getElementById('date-range'); const clientSelect = document.getElementById('client-filter'); const providerSelect = document.getElementById('provider-filter'); if (dateRangeSelect) { dateRangeSelect.addEventListener('change', (e) => { this.filters.dateRange = e.target.value; this.applyFilters(); }); } if (clientSelect) { clientSelect.addEventListener('change', (e) => { this.filters.client = e.target.value; this.applyFilters(); }); } if (providerSelect) { providerSelect.addEventListener('change', (e) => { this.filters.provider = e.target.value; this.applyFilters(); }); } // Chart metric buttons const metricButtons = document.querySelectorAll('.chart-control-btn[data-metric]'); metricButtons.forEach(button => { button.addEventListener('click', () => { // Update active state metricButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); // Update chart based on metric this.updateAnalyticsChart(button.dataset.metric); }); }); // Export button const exportBtn = document.querySelector('#analytics .btn-secondary'); if (exportBtn) { exportBtn.addEventListener('click', () => { this.exportData(); }); } } applyFilters() { console.log('Applying filters:', this.filters); // In a real app, this would fetch filtered data from the API // For now, just show a toast if (window.authManager) { window.authManager.showToast('Filters applied', 'success'); } // Refresh data this.loadCharts(); this.loadUsageData(); } updateAnalyticsChart(metric) { // Update the main analytics chart to show the selected metric const labels = window.chartManager.generateDateLabels(7); let data; if (metric === 'requests') { data = { labels: labels, datasets: [{ label: 'Requests', data: labels.map(() => Math.floor(Math.random() * 1000) + 500), color: '#3b82f6', fill: true }] }; } else if (metric === 'tokens') { data = { labels: labels, datasets: [{ label: 'Tokens', data: labels.map(() => Math.floor(Math.random() * 100000) + 50000), color: '#10b981', fill: true }] }; } else if (metric === 'cost') { data = { labels: labels, datasets: [{ label: 'Cost ($)', data: labels.map(() => Math.random() * 50 + 10), color: '#f59e0b', fill: true }] }; } window.chartManager.updateChartData('analytics-chart', data); } exportData() { // Create CSV data const table = document.getElementById('usage-table'); if (!table) return; const rows = table.querySelectorAll('tr'); const csv = []; rows.forEach(row => { const rowData = []; row.querySelectorAll('th, td').forEach(cell => { rowData.push(`"${cell.textContent.replace(/"/g, '""')}"`); }); csv.push(rowData.join(',')); }); // Create download link const blob = new Blob([csv.join('\n')], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `llm-proxy-analytics-${new Date().toISOString().split('T')[0]}.csv`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); // Show success message if (window.authManager) { window.authManager.showToast('Data exported successfully', 'success'); } } refresh() { this.loadCharts(); this.loadUsageData(); } } // Initialize analytics page when needed window.initAnalytics = async () => { window.analyticsPage = new AnalyticsPage(); }; // Export for use in other modules if (typeof module !== 'undefined' && module.exports) { module.exports = AnalyticsPage; }