184 lines
5.5 KiB
JavaScript
184 lines
5.5 KiB
JavaScript
// Analytics Page Module
|
|
|
|
class AnalyticsPage {
|
|
constructor() {
|
|
this.filters = {
|
|
dateRange: '7d',
|
|
client: 'all',
|
|
provider: 'all'
|
|
};
|
|
this.init();
|
|
}
|
|
|
|
async init() {
|
|
// Load data
|
|
await Promise.all([
|
|
this.loadClients(),
|
|
this.loadCharts(),
|
|
this.loadUsageData()
|
|
]);
|
|
|
|
// Setup event listeners
|
|
this.setupEventListeners();
|
|
}
|
|
|
|
async loadClients() {
|
|
try {
|
|
const clients = await window.api.get('/clients');
|
|
this.renderClientFilter(clients);
|
|
} catch (error) {
|
|
console.error('Error loading clients for filter:', 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 || client.id;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
async loadCharts() {
|
|
try {
|
|
const breakdown = await window.api.get('/analytics/breakdown');
|
|
const timeSeries = await window.api.get('/usage/time-series');
|
|
|
|
this.renderAnalyticsChart(timeSeries.series);
|
|
this.renderClientsChart(breakdown.clients);
|
|
this.renderModelsChart(breakdown.models);
|
|
} catch (error) {
|
|
console.error('Error loading analytics charts:', error);
|
|
}
|
|
}
|
|
|
|
renderAnalyticsChart(series) {
|
|
const data = {
|
|
labels: series.map(s => s.time),
|
|
datasets: [
|
|
{
|
|
label: 'Requests',
|
|
data: series.map(s => s.requests),
|
|
color: '#fe8019', // orange
|
|
fill: true
|
|
},
|
|
{
|
|
label: 'Tokens',
|
|
data: series.map(s => s.tokens),
|
|
color: '#b8bb26', // green
|
|
fill: true,
|
|
hidden: true
|
|
}
|
|
]
|
|
};
|
|
|
|
window.chartManager.createLineChart('analytics-chart', data);
|
|
}
|
|
|
|
renderClientsChart(clients) {
|
|
const data = {
|
|
labels: clients.map(c => c.label),
|
|
datasets: [{
|
|
label: 'Requests',
|
|
data: clients.map(c => c.value),
|
|
color: '#83a598' // blue
|
|
}]
|
|
};
|
|
|
|
window.chartManager.createHorizontalBarChart('clients-chart', data);
|
|
}
|
|
|
|
renderModelsChart(models) {
|
|
const data = {
|
|
labels: models.map(m => m.label),
|
|
data: models.map(m => m.value),
|
|
colors: window.chartManager.defaultColors
|
|
};
|
|
|
|
window.chartManager.createDoughnutChart('models-chart', data);
|
|
}
|
|
|
|
async loadUsageData() {
|
|
try {
|
|
const usageData = await window.api.get('/usage/detailed');
|
|
this.renderUsageTable(usageData);
|
|
} catch (error) {
|
|
console.error('Error loading usage data:', error);
|
|
}
|
|
}
|
|
|
|
renderUsageTable(data) {
|
|
const tableBody = document.querySelector('#usage-table tbody');
|
|
if (!tableBody) return;
|
|
|
|
if (data.length === 0) {
|
|
tableBody.innerHTML = '<tr><td colspan="7" class="text-center">No historical data found</td></tr>';
|
|
return;
|
|
}
|
|
|
|
tableBody.innerHTML = data.map(row => `
|
|
<tr>
|
|
<td>${row.date}</td>
|
|
<td><span class="badge-client">${row.client}</span></td>
|
|
<td>${row.provider}</td>
|
|
<td><code class="code-sm">${row.model}</code></td>
|
|
<td>${row.requests.toLocaleString()}</td>
|
|
<td>${window.api.formatNumber(row.tokens)}</td>
|
|
<td>${window.api.formatCurrency(row.cost)}</td>
|
|
</tr>
|
|
`).join('');
|
|
}
|
|
|
|
setupEventListeners() {
|
|
const refreshBtn = document.getElementById('refresh-analytics');
|
|
if (refreshBtn) {
|
|
refreshBtn.onclick = () => this.refresh();
|
|
}
|
|
|
|
// Export button
|
|
const exportBtn = document.getElementById('export-data');
|
|
if (exportBtn) {
|
|
exportBtn.onclick = () => this.exportData();
|
|
}
|
|
}
|
|
|
|
async exportData() {
|
|
// Simple CSV export
|
|
const data = await window.api.get('/usage/detailed');
|
|
if (!data || data.length === 0) return;
|
|
|
|
const headers = Object.keys(data[0]).join(',');
|
|
const rows = data.map(obj => Object.values(obj).join(',')).join('\n');
|
|
const csv = `${headers}\n${rows}`;
|
|
|
|
const blob = new Blob([csv], { type: 'text/csv' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `llm-proxy-usage-${new Date().toISOString().split('T')[0]}.csv`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
}
|
|
|
|
refresh() {
|
|
this.loadCharts();
|
|
this.loadUsageData();
|
|
window.authManager.showToast('Analytics data refreshed', 'success');
|
|
}
|
|
}
|
|
|
|
window.initAnalytics = async () => {
|
|
window.analyticsPage = new AnalyticsPage();
|
|
};
|