// Costs Page Module class CostsPage { constructor() { this.costData = null; this.init(); } async init() { // Load data await this.loadCostStats(); await this.loadCostsChart(); await this.loadBudgetTracking(); await this.loadCostProjections(); await this.loadPricingTable(); // Setup event listeners this.setupEventListeners(); } async loadCostStats() { try { // In a real app, this would fetch from /api/costs/summary this.costData = { totalCost: 125.43, todayCost: 12.45, weekCost: 45.67, monthCost: 125.43, avgDailyCost: 8.36, costTrend: 5.2, // percentage budgetUsed: 62, // percentage projectedMonthEnd: 189.75 }; this.renderCostStats(); } catch (error) { console.error('Error loading cost stats:', error); } } renderCostStats() { const container = document.getElementById('cost-stats'); if (!container) return; container.innerHTML = `
$${this.costData.totalCost.toFixed(2)}
Total Cost
$${this.costData.todayCost.toFixed(2)} today
$${this.costData.weekCost.toFixed(2)}
This Week
${Math.abs(this.costData.costTrend)}% from last week
$${this.costData.monthCost.toFixed(2)}
This Month
$${this.costData.avgDailyCost.toFixed(2)}/day avg
${this.costData.budgetUsed}%
Budget Used
$${this.costData.projectedMonthEnd.toFixed(2)} projected
`; } async loadCostsChart() { try { // Generate demo data const data = { labels: ['OpenAI', 'Gemini', 'DeepSeek', 'Grok'], datasets: [{ label: 'Cost by Provider', data: [65, 25, 8, 2], color: '#3b82f6' }] }; window.chartManager.createBarChart('costs-chart', data, { plugins: { tooltip: { callbacks: { label: function(context) { return `$${context.parsed.y.toFixed(2)} (${context.parsed.y}%)`; } } } } }); } catch (error) { console.error('Error loading costs chart:', error); } } async loadBudgetTracking() { const container = document.getElementById('budget-progress'); if (!container) return; const budgets = [ { name: 'Monthly Budget', used: 62, total: 200, color: 'primary' }, { name: 'OpenAI Budget', used: 75, total: 150, color: 'info' }, { name: 'Gemini Budget', used: 45, total: 100, color: 'success' }, { name: 'Team Budget', used: 30, total: 50, color: 'warning' } ]; container.innerHTML = budgets.map(budget => `
${budget.name} $${budget.used} / $${budget.total}
`).join(''); // Add CSS for budget items this.addBudgetStyles(); } addBudgetStyles() { const style = document.createElement('style'); style.textContent = ` .budget-item { margin-bottom: 1.5rem; } .budget-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; } .budget-name { font-size: 0.875rem; font-weight: 500; color: var(--text-primary); } .budget-amount { font-size: 0.875rem; color: var(--text-secondary); } .budget-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 0.5rem; font-size: 0.75rem; } .budget-percentage { color: var(--text-secondary); } .budget-remaining { color: var(--success); font-weight: 500; } .progress-fill.primary { background-color: var(--primary); } .progress-fill.info { background-color: var(--info); } .progress-fill.success { background-color: var(--success); } .progress-fill.warning { background-color: var(--warning); } `; document.head.appendChild(style); } async loadCostProjections() { const container = document.getElementById('cost-projections'); if (!container) return; const projections = [ { period: 'Today', amount: 12.45, trend: 'up' }, { period: 'This Week', amount: 45.67, trend: 'up' }, { period: 'This Month', amount: 189.75, trend: 'up' }, { period: 'Next Month', amount: 210.50, trend: 'up' } ]; container.innerHTML = projections.map(proj => `
${proj.period}
$${proj.amount.toFixed(2)}
`).join(''); // Add CSS for projections this.addProjectionStyles(); } addProjectionStyles() { const style = document.createElement('style'); style.textContent = ` .projection-item { display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 0; border-bottom: 1px solid var(--border-color); } .projection-item:last-child { border-bottom: none; } .projection-period { font-size: 0.875rem; color: var(--text-primary); } .projection-amount { font-size: 1rem; font-weight: 600; color: var(--text-primary); } .projection-trend { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; } .projection-trend.up { background-color: rgba(239, 68, 68, 0.1); color: var(--danger); } .projection-trend.down { background-color: rgba(16, 185, 129, 0.1); color: var(--success); } `; document.head.appendChild(style); } async loadPricingTable() { try { // In a real app, this would fetch from /api/pricing const pricingData = [ { provider: 'OpenAI', model: 'gpt-4', input: 0.03, output: 0.06, updated: '2024-01-15' }, { provider: 'OpenAI', model: 'gpt-3.5-turbo', input: 0.0015, output: 0.002, updated: '2024-01-15' }, { provider: 'Gemini', model: 'gemini-pro', input: 0.0005, output: 0.0015, updated: '2024-01-14' }, { provider: 'Gemini', model: 'gemini-pro-vision', input: 0.0025, output: 0.0075, updated: '2024-01-14' }, { provider: 'DeepSeek', model: 'deepseek-chat', input: 0.00014, output: 0.00028, updated: '2024-01-13' }, { provider: 'DeepSeek', model: 'deepseek-coder', input: 0.00014, output: 0.00028, updated: '2024-01-13' }, { provider: 'Grok', model: 'grok-beta', input: 0.01, output: 0.03, updated: '2024-01-12' } ]; this.renderPricingTable(pricingData); } catch (error) { console.error('Error loading pricing data:', error); } } renderPricingTable(data) { const tableBody = document.querySelector('#pricing-table tbody'); if (!tableBody) return; tableBody.innerHTML = data.map(row => ` ${row.provider} ${row.model} $${row.input.toFixed(5)}/1K tokens $${row.output.toFixed(5)}/1K tokens ${row.updated} `).join(''); } setupEventListeners() { // Breakdown buttons const breakdownButtons = document.querySelectorAll('.chart-control-btn[data-breakdown]'); breakdownButtons.forEach(button => { button.addEventListener('click', () => { // Update active state breakdownButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); // Update chart based on breakdown this.updateCostsChart(button.dataset.breakdown); }); }); // Edit pricing button const editBtn = document.getElementById('edit-pricing'); if (editBtn) { editBtn.addEventListener('click', () => { this.editPricing(); }); } } updateCostsChart(breakdown) { let data; if (breakdown === 'provider') { data = { labels: ['OpenAI', 'Gemini', 'DeepSeek', 'Grok'], datasets: [{ label: 'Cost by Provider', data: [65, 25, 8, 2], color: '#3b82f6' }] }; } else if (breakdown === 'client') { data = { labels: ['Web App', 'Mobile App', 'API Integration', 'Internal Tools', 'Testing'], datasets: [{ label: 'Cost by Client', data: [40, 25, 20, 10, 5], color: '#10b981' }] }; } else if (breakdown === 'model') { data = { labels: ['gpt-4', 'gpt-3.5-turbo', 'gemini-pro', 'deepseek-chat', 'grok-beta'], datasets: [{ label: 'Cost by Model', data: [35, 30, 20, 10, 5], color: '#f59e0b' }] }; } window.chartManager.updateChartData('costs-chart', data); } editPricing() { // Show pricing edit modal this.showPricingModal(); } showPricingModal() { // Create modal for editing pricing const modal = document.createElement('div'); modal.className = 'modal active'; modal.innerHTML = ` `; document.body.appendChild(modal); // Setup event listeners const closeBtn = modal.querySelector('.modal-close'); const closeModalBtn = modal.querySelector('.close-modal'); const saveBtn = modal.querySelector('.save-pricing'); const closeModal = () => { modal.classList.remove('active'); setTimeout(() => modal.remove(), 300); }; closeBtn.addEventListener('click', closeModal); closeModalBtn.addEventListener('click', closeModal); saveBtn.addEventListener('click', () => { // In a real app, this would save pricing changes if (window.authManager) { window.authManager.showToast('Pricing updated successfully', 'success'); } closeModal(); }); // Close on background click modal.addEventListener('click', (e) => { if (e.target === modal) { closeModal(); } }); } refresh() { this.loadCostStats(); this.loadCostsChart(); this.loadBudgetTracking(); this.loadCostProjections(); this.loadPricingTable(); } } // Initialize costs page when needed window.initCosts = async () => { window.costsPage = new CostsPage(); }; // Export for use in other modules if (typeof module !== 'undefined' && module.exports) { module.exports = CostsPage; }