class BillingDashboard extends HTMLElement { constructor() { super(); this.currentUsage = null; this.subscription = null; this.pricing = null; this.invoices = []; this.boundHandleClick = this.handleClick.bind(this); } async connectedCallback() { this.addEventListener('click', this.boundHandleClick); await this.loadData(); this.render(); this.attachEventListeners(); } disconnectedCallback() { this.removeEventListener('click', this.boundHandleClick); } async loadData() { try { const [usage, subscription, pricing, invoices] = await Promise.all([ this.fetchCurrentUsage(), this.fetchSubscription(), this.fetchPricing(), this.fetchInvoices() ]); this.currentUsage = usage; this.subscription = subscription; this.pricing = pricing; this.invoices = invoices; } catch (error) { console.error('Failed to load billing data:', error); } } async fetchCurrentUsage() { const response = await fetch('/api/billing/usage/current', { headers: {'Authorization': `Bearer ${localStorage.getItem('token')}`} }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } async fetchSubscription() { const response = await fetch('/api/billing/subscription', { headers: {'Authorization': `Bearer ${localStorage.getItem('token')}`} }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } async fetchPricing() { const response = await fetch('/api/billing/pricing'); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } async fetchInvoices() { const response = await fetch('/api/billing/invoices?limit=10', { headers: {'Authorization': `Bearer ${localStorage.getItem('token')}`} }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } formatCurrency(amount) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 4 }).format(amount); } formatGB(gb) { if (gb >= 1024) { return `${(gb / 1024).toFixed(2)} TB`; } return `${gb.toFixed(2)} GB`; } calculateEstimatedCost() { if (!this.currentUsage || !this.pricing) return 0; const storagePrice = parseFloat(this.pricing.storage_per_gb_month?.value || 0); const bandwidthPrice = parseFloat(this.pricing.bandwidth_egress_per_gb?.value || 0); const freeStorage = parseFloat(this.pricing.free_tier_storage_gb?.value || 0); const freeBandwidth = parseFloat(this.pricing.free_tier_bandwidth_gb?.value || 0); const storageGB = this.currentUsage.storage_gb; const bandwidthGB = this.currentUsage.bandwidth_down_gb_today * 30; const billableStorage = Math.max(0, Math.ceil(storageGB - freeStorage)); const billableBandwidth = Math.max(0, Math.ceil(bandwidthGB - freeBandwidth)); return (billableStorage * storagePrice) + (billableBandwidth * bandwidthPrice); } render() { const estimatedCost = this.calculateEstimatedCost(); const storageUsed = this.currentUsage?.storage_gb || 0; const freeStorage = parseFloat(this.pricing?.free_tier_storage_gb?.value || 15); const storagePercentage = Math.min(100, (storageUsed / freeStorage) * 100); this.innerHTML = `
No invoices yet
'; } return `| Invoice # | Period | Amount | Status | Due Date | Actions |
|---|---|---|---|---|---|
| ${invoice.invoice_number} | ${this.formatDate(invoice.period_start)} - ${this.formatDate(invoice.period_end)} | ${this.formatCurrency(invoice.total)} | ${invoice.status} | ${invoice.due_date ? this.formatDate(invoice.due_date) : '-'} |