EnergyStackHub / Dashboard
AM
Alex Morgan
AI Morning Briefing — Fri, Mar 27, 2026
Good morning, Alex. Your portfolio spent $4,230 on energy yesterday.
That's 12% below budget — strong performance. Building 7 needs attention.
Building 7 (Riverside Commerce Center) shows anomalous overnight consumption — 34% above baseline. Two utility contracts expire within 60 days. Renewable target is on track at 68% of annual goal. Overall portfolio carbon intensity improved 8% month-over-month.
−12% vs Budget (Yesterday)
2 Contracts Expiring Soon
68% Renewable Progress (YTD)
A− Portfolio Health
Portfolio Health Score
A−
+4 pts this month
Based on 14 locations
Total Spend (MTD)
$87,420
Cost vs Budget
88%
Active Anomalies
4
Contracts Expiring
2
Carbon Emissions (MTD)
142t
Savings vs Prior Year
$32k
Location Overview
View all 14 →
Building 7 — Riverside Commerce
Portland, OR · Commercial Office
4,820 kWh
yesterday
Anomaly
HQ — One Market Tower
San Francisco, CA · HQ Office
11,340 kWh
yesterday
Contract due
Distribution Center — Denver
Denver, CO · Industrial
22,100 kWh
yesterday
On track
Retail — Austin Flagship
Austin, TX · Retail
3,420 kWh
yesterday
On track
Warehouse — Chicago West
Chicago, IL · Industrial
18,750 kWh
yesterday
On track
Recent Alerts
View all →
Anomalous overnight consumption — Building 7
+34% above baseline (02:00–06:00 AM). HVAC likely culprit.
2h ago
Contract expiring — PG&E HQ (60 days)
Fixed-rate contract ending May 27. Market rates favorable — recommend renewal.
1d ago
Billing discrepancy — Denver DC
March bill 6.2% above estimated. Possible meter read error.
2d ago
Solar generation below target — Austin
3-day cloud cover reduced output 18%. No action needed.
3d ago
Weekly report ready — Portfolio Summary
Week of Mar 21–27. 14 locations, $21,340 spend.
4d ago
Total Buildings
in your portfolio
Total Sqft
across portfolio
Est. Annual Spend
energy costs
Saved Reports
Loading your portfolio…
Coming Soon
Energy Procurement
Manage energy contracts, track renewal dates, and get AI-powered market intelligence to time your procurement decisions and lock in optimal rates.
Contract lifecycle management Renewal alerts Market price tracking RFP automation Supplier negotiation tools Risk analysis
Bill Detail
Monthly Spend (Last 6 Bills)
Audit Findings
No audit findings yet. Click "Run Audit" to analyze this bill.
Add Utility Bill
Upload Utility Bill
Drop your bill here
or click to browse · PDF, PNG, JPG supported

For MVP, bill upload captures the file name and creates a record. Full OCR parsing coming soon.

async function submitErrorReport(event) { event.preventDefault(); const submitBtn = document.getElementById('errorSubmitBtn'); submitBtn.disabled = true; submitBtn.textContent = 'Submitting…'; const payload = { page_url: window.location.href, content_type: document.getElementById('modalContentType').value, error_category: document.getElementById('errorCategory').value, user_description: document.getElementById('errorDescription').value.trim() || null, user_email: document.getElementById('errorEmail').value.trim() || null }; try { const resp = await fetch('/api/error-reports', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await resp.json(); if (data.success) { document.getElementById('errorReportForm').style.display = 'none'; document.getElementById('errorSuccessMsg').style.display = 'block'; setTimeout(closeErrorModal, 2800); } else { submitBtn.textContent = 'Submit Report'; submitBtn.disabled = false; alert('Error: ' + (data.message || 'Submission failed. Please try again.')); } } catch (err) { submitBtn.textContent = 'Submit Report'; submitBtn.disabled = false; console.error('Error report submission error:', err); alert('Network error. Please check your connection and try again.'); } } // ═══════════════════════════════════════════════ // AI SAFEGUARDS — Stale Data Indicators // ═══════════════════════════════════════════════ async function loadDataFreshness() { try { const resp = await fetch('/api/data-freshness'); const data = await resp.json(); if (!data.success || !data.sources) return; const stale = data.sources.filter(s => s.staleness === 'stale' || s.staleness === 'critical'); if (stale.length === 0) return; const section = document.getElementById('staleDataSection'); const list = document.getElementById('staleDataList'); section.style.display = 'block'; list.innerHTML = stale.map(s => { const cls = s.staleness === 'critical' ? 'stale-red' : 'stale-amber'; const icon = s.staleness === 'critical' ? `` : ``; const verifyLink = s.verify_url ? ` Verify current information at ${s.source_label}.` : ''; const severity = s.staleness === 'critical' ? 'This data may be significantly outdated.' : 'This data was last verified on ' + s.last_refreshed + '.'; return `
${icon}⚠️ ${s.source_label} — ${severity}${verifyLink}
`; }).join(''); } catch (err) { console.warn('Could not load data freshness info:', err.message); } } // Load data freshness on page load window.addEventListener('load', function() { setTimeout(loadDataFreshness, 800); }); // Keyboard: close modal on Escape document.addEventListener('keydown', function(e) { if (e.key === 'Escape') closeErrorModal(); }); // ═══════════════════════════════════════════════ // PROFESSIONAL LEADS — Notification Badge // If a professional JWT is stored, show live lead count // ═══════════════════════════════════════════════ async function loadProLeadBadge() { const proToken = localStorage.getItem('pro_token'); if (!proToken) return; try { const resp = await fetch('/api/professional/notifications/count', { headers: { 'Authorization': `Bearer ${proToken}` } }); if (!resp.ok) return; const data = await resp.json(); const count = data.count || 0; // Update notification bell badge const badge = document.querySelector('.topbar-notification-badge'); if (badge && count > 0) { badge.textContent = count > 9 ? '9+' : count; badge.style.display = 'flex'; } // Update sidebar pro leads badge const proLeadsBadge = document.getElementById('proLeadsBadge'); if (proLeadsBadge && count > 0) { proLeadsBadge.textContent = count; proLeadsBadge.style.display = 'inline-block'; } // Add lead notification to dropdown if there are new leads if (count > 0) { const dropdown = document.getElementById('notificationDropdown'); const existingProNotif = dropdown.querySelector('.pro-lead-notif'); if (!existingProNotif) { const leadItem = document.createElement('div'); leadItem.className = 'notification-item pro-lead-notif'; leadItem.style.cursor = 'pointer'; leadItem.onclick = () => { window.location.href = '/professional/dashboard'; }; leadItem.innerHTML = `
⚡ ${count} new lead${count !== 1 ? 's' : ''} in your inbox
View Professional Dashboard
`; const header = dropdown.querySelector('.dropdown-menu-header'); if (header && header.nextSibling) { dropdown.insertBefore(leadItem, header.nextSibling); } } } } catch (err) { // Silently fail — user may not be a professional } } // Load on page load and every 60s window.addEventListener('load', function() { setTimeout(loadProLeadBadge, 1200); setInterval(loadProLeadBadge, 60000); }); // ═══════════════════════════════════════════════════════════════════ // UTILITY BILL MANAGER // ═══════════════════════════════════════════════════════════════════ let _billManagerInited = false; let _selectedFile = null; async function initBillManager() { if (_billManagerInited) return; _billManagerInited = true; await loadLocations(); await loadBills(); } async function loadLocations() { try { const resp = await fetch('/api/bills/locations'); const data = await resp.json(); if (!data.success) return; const sel = document.getElementById('locationFilter'); data.locations.forEach(loc => { const opt = document.createElement('option'); opt.value = loc.location_id; opt.textContent = loc.location_name; sel.appendChild(opt); }); } catch (e) { console.warn('loadLocations:', e.message); } } async function loadBills() { const location = document.getElementById('locationFilter').value; const status = document.getElementById('statusFilter').value; const sort = document.getElementById('sortFilter').value; const params = new URLSearchParams(); if (location !== 'all') params.set('location', location); if (status !== 'all') params.set('status', status); params.set('sort', sort); const tbody = document.getElementById('billsTableBody'); tbody.innerHTML = 'Loading…'; try { const resp = await fetch(`/api/bills?${params}`); const data = await resp.json(); if (!data.success) throw new Error(data.message); const bills = data.bills; document.getElementById('billCount').textContent = `${bills.length} bill${bills.length !== 1 ? 's' : ''}`; // Update stats const totalSpend = bills.reduce((s, b) => s + parseFloat(b.amount || 0), 0); const totalSavings = bills.reduce((s, b) => s + parseFloat(b.total_savings || 0), 0); const flagged = bills.filter(b => b.status === 'flagged').length; const pending = bills.filter(b => b.status === 'pending').length; document.getElementById('statTotalBills').textContent = bills.length; document.getElementById('statBillsPending').textContent = `${pending} awaiting audit`; document.getElementById('statBillsPending').className = 'bill-stat-sub' + (pending > 0 ? ' warning' : ''); document.getElementById('statTotalSpend').textContent = '$' + Math.round(totalSpend).toLocaleString(); document.getElementById('statSpendSub').textContent = `Across ${new Set(bills.map(b => b.location_id)).size} locations`; document.getElementById('statSavings').textContent = totalSavings > 0 ? '$' + Math.round(totalSavings).toLocaleString() : '$0'; document.getElementById('statSavingsSub').textContent = totalSavings > 0 ? 'Identified by audits' : 'Run audits to find savings'; document.getElementById('statFlagged').textContent = flagged; document.getElementById('statFlaggedSub').textContent = flagged > 0 ? 'Require attention' : 'All clear'; document.getElementById('statFlaggedSub').className = 'bill-stat-sub' + (flagged > 0 ? ' danger' : ''); if (bills.length === 0) { tbody.innerHTML = `
No bills found
Add your first utility bill to get started
`; return; } tbody.innerHTML = bills.map(b => { const period = formatPeriod(b.billing_period_start, b.billing_period_end); const amount = parseFloat(b.amount || 0); const usage = parseFloat(b.usage_kwh || 0); const findings = parseInt(b.finding_count || 0); const savings = parseFloat(b.total_savings || 0); const critical = parseInt(b.critical_count || 0); return `
${escHtml(b.title)}
${period}
${escHtml(b.utility_provider || '–')} ${escHtml(b.location_name || '–')}
${usage > 0 ? usage.toLocaleString() + ' kWh' : '–'}
${b.demand_kw > 0 ? `
${parseFloat(b.demand_kw).toFixed(1)} kW peak
` : ''} ${amount > 0 ? '$' + amount.toLocaleString('en-US', {minimumFractionDigits:2,maximumFractionDigits:2}) : '–'} ${statusBadge(b.status)} ${findings > 0 ? `
${findings} finding${findings !== 1 ? 's' : ''}
$${Math.round(savings).toLocaleString()} savings
` : `Not audited`}
`; }).join(''); } catch (err) { tbody.innerHTML = `${err.message}`; } } function setSortAndLoad(sort) { document.getElementById('sortFilter').value = sort; loadBills(); } // ── Bill Detail Modal ───────────────────────────────────────────── async function openBillDetail(billId) { window._detailBillId = billId; const modal = document.getElementById('billDetailModal'); modal.classList.add('active'); document.getElementById('billDetailTitle').textContent = 'Loading…'; document.getElementById('billDetailGrid').innerHTML = ''; document.getElementById('billDetailFindings').innerHTML = '
Loading…
'; try { const resp = await fetch(`/api/bills/${billId}`); const data = await resp.json(); if (!data.success) throw new Error(data.message); const b = data.bill; const bD = b.bill_data || {}; const findings = data.findings || []; document.getElementById('billDetailTitle').textContent = b.title; document.getElementById('billDetailPeriod').textContent = formatPeriod(b.billing_period_start, b.billing_period_end) + (b.utility_provider ? ' · ' + b.utility_provider : ''); // Stats grid const gridItems = [ { label: 'Total Amount', value: '$' + parseFloat(b.amount || 0).toLocaleString('en-US', {minimumFractionDigits:2,maximumFractionDigits:2}) }, { label: 'Energy Charge', value: bD.energy_charge ? '$' + parseFloat(bD.energy_charge).toLocaleString('en-US', {minimumFractionDigits:2,maximumFractionDigits:2}) : '–' }, { label: 'Demand Charge', value: bD.demand_charge ? '$' + parseFloat(bD.demand_charge).toLocaleString('en-US', {minimumFractionDigits:2,maximumFractionDigits:2}) : '–' }, { label: 'Taxes', value: bD.taxes ? '$' + parseFloat(bD.taxes).toLocaleString('en-US', {minimumFractionDigits:2,maximumFractionDigits:2}) : '–' }, { label: 'Fees', value: bD.fees ? '$' + parseFloat(bD.fees).toLocaleString('en-US', {minimumFractionDigits:2,maximumFractionDigits:2}) : '–' }, { label: 'Usage', value: b.usage_kwh > 0 ? parseFloat(b.usage_kwh).toLocaleString() + ' kWh' : '–' }, { label: 'Peak Demand', value: b.demand_kw > 0 ? parseFloat(b.demand_kw).toFixed(1) + ' kW' : '–' }, { label: 'Rate Schedule', value: bD.rate_schedule || '–' }, { label: 'Meter Read', value: bD.estimated_reads ? 'Estimated' : 'Actual' }, ]; document.getElementById('billDetailGrid').innerHTML = gridItems.map(item => `
${item.label}
${item.value}
` ).join(''); // Mini bar chart (demo: use amount as proxy for 6 months) const chartEl = document.getElementById('billDetailChart'); const barData = [0.72, 0.81, 0.65, 0.88, 0.79, 1.0]; // relative heights const months = ['Oct', 'Nov', 'Dec', 'Jan', 'Feb', 'Mar']; chartEl.innerHTML = barData.map((h, i) => `
${months[i]}
` ).join(''); // Findings renderFindings(findings, 'billDetailFindings'); // Update audit button text const auditBtn = document.getElementById('detailAuditBtn'); if (findings.length > 0) { auditBtn.innerHTML = ` Re-audit`; } } catch (err) { document.getElementById('billDetailTitle').textContent = 'Error loading bill'; document.getElementById('billDetailFindings').innerHTML = `
${err.message}
`; } } function closeBillDetailModal(e) { if (e.target === document.getElementById('billDetailModal')) { document.getElementById('billDetailModal').classList.remove('active'); } } function renderFindings(findings, containerId) { const el = document.getElementById(containerId); if (!findings || findings.length === 0) { el.innerHTML = '
No audit findings. Click "Run Audit" to analyze this bill.
'; return; } el.innerHTML = findings.map(f => { const typeLabel = (f.finding_type || '').replace(/_/g, ' '); const savings = parseFloat(f.estimated_savings || 0); return `
${f.severity} ${typeLabel}
${findingTitle(f.finding_type)}
${savings > 0 ? `
$${savings.toLocaleString('en-US', {minimumFractionDigits:2,maximumFractionDigits:2})}/mo
` : ''}
${escHtml(f.description || '')}
${f.recommendation ? `
${escHtml(f.recommendation)}
` : ''}
`; }).join(''); } function findingTitle(type) { const titles = { demand_ratchet: 'Demand Ratchet Clause', rate_classification: 'Rate Schedule Review', tax_exemption: 'Tax Exemption Opportunity', estimated_read: 'Estimated Meter Read', power_factor: 'Power Factor Penalty', billing_error: 'Billing Error Detected', late_fee: 'Late Fee Applied', clean_bill: 'No Issues Detected', }; return titles[type] || type.replace(/_/g, ' '); } // ── Add Bill Modal ──────────────────────────────────────────────── function showAddBillModal() { document.getElementById('addBillModal').classList.add('active'); } function closeAddBillModal(e) { if (e.target === document.getElementById('addBillModal')) { document.getElementById('addBillModal').classList.remove('active'); } } async function submitAddBill(e) { e.preventDefault(); const btn = document.getElementById('addBillSubmitBtn'); btn.innerHTML = '
Adding…'; btn.disabled = true; const payload = { title: document.getElementById('newBillTitle').value.trim(), utility_provider: document.getElementById('newBillProvider').value.trim(), location_name: document.getElementById('newBillLocation').value.trim(), amount: parseFloat(document.getElementById('newBillAmount').value) || 0, usage_kwh: parseFloat(document.getElementById('newBillKwh').value) || 0, demand_kw: parseFloat(document.getElementById('newBillDemand').value) || 0, rate_schedule: document.getElementById('newBillRate').value.trim(), billing_period_start: document.getElementById('newBillStart').value || null, billing_period_end: document.getElementById('newBillEnd').value || null, energy_charge: parseFloat(document.getElementById('newBillEnergy').value) || 0, demand_charge: parseFloat(document.getElementById('newBillDemandCharge').value) || 0, taxes: parseFloat(document.getElementById('newBillTaxes').value) || 0, fees: parseFloat(document.getElementById('newBillFees').value) || 0, }; try { const resp = await fetch('/api/bills', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await resp.json(); if (!data.success) throw new Error(data.message); document.getElementById('addBillModal').classList.remove('active'); document.getElementById('addBillForm').reset(); loadBills(); } catch (err) { alert('Error: ' + err.message); } finally { btn.innerHTML = ' Add Bill'; btn.disabled = false; } } // ── Upload Modal ────────────────────────────────────────────────── function showUploadModal() { _selectedFile = null; document.getElementById('uploadFileName').style.display = 'none'; document.getElementById('uploadConfirmBtn').disabled = true; document.getElementById('uploadConfirmBtn').style.opacity = '0.5'; document.getElementById('uploadModal').classList.add('active'); } function closeUploadModal(e) { if (e.target === document.getElementById('uploadModal')) { document.getElementById('uploadModal').classList.remove('active'); } } function handleDragOver(e) { e.preventDefault(); document.getElementById('uploadZone').classList.add('drag-over'); } function handleDragLeave(e) { document.getElementById('uploadZone').classList.remove('drag-over'); } function handleDrop(e) { e.preventDefault(); document.getElementById('uploadZone').classList.remove('drag-over'); const files = e.dataTransfer.files; if (files.length > 0) selectFile(files[0]); } function handleFileSelect(e) { if (e.target.files.length > 0) selectFile(e.target.files[0]); } function selectFile(file) { _selectedFile = file; const nameEl = document.getElementById('uploadFileName'); nameEl.textContent = '📄 ' + file.name; nameEl.style.display = 'block'; const btn = document.getElementById('uploadConfirmBtn'); btn.disabled = false; btn.style.opacity = '1'; } async function confirmUpload() { if (!_selectedFile) return; const btn = document.getElementById('uploadConfirmBtn'); btn.innerHTML = '
Uploading…'; btn.disabled = true; // For MVP: create bill record with file name, no actual file storage yet const name = _selectedFile.name.replace(/\.[^.]+$/, '').replace(/[-_]/g, ' '); const payload = { title: name, file_name: _selectedFile.name, file_type: _selectedFile.type, }; try { const resp = await fetch('/api/bills', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(payload) }); const data = await resp.json(); if (!data.success) throw new Error(data.message); document.getElementById('uploadModal').classList.remove('active'); _selectedFile = null; loadBills(); } catch (err) { alert('Upload error: ' + err.message); } finally { btn.innerHTML = ' Upload & Create Record'; btn.disabled = false; } } // ── Delete bill ─────────────────────────────────────────────────── async function deleteBill(billId) { if (!confirm('Delete this bill and all its audit findings?')) return; try { await fetch(`/api/bills/${billId}`, { method: 'DELETE' }); document.getElementById('billDetailModal').classList.remove('active'); loadBills(); if (_auditInited) loadAuditHistory(); } catch (err) { alert('Delete failed: ' + err.message); } } // ── Run audit inline ────────────────────────────────────────────── async function runAuditForBill(billId) { const btn = document.getElementById('detailAuditBtn'); if (btn) { btn.innerHTML = '
Analyzing…'; btn.disabled = true; } try { const resp = await fetch(`/api/bills/${billId}/audit`, { method: 'POST' }); const data = await resp.json(); if (!data.success) throw new Error(data.message); renderFindings(data.findings, 'billDetailFindings'); if (btn) { btn.innerHTML = ` Re-audit`; btn.disabled = false; } loadBills(); if (_auditInited) loadAuditHistory(); } catch (err) { alert('Audit error: ' + err.message); if (btn) { btn.innerHTML = 'Run Audit'; btn.disabled = false; } } } // ── Helpers ─────────────────────────────────────────────────────── function statusBadge(status) { const icons = { audited: '✓', pending: '◷', flagged: '⚠' }; return `${icons[status] || ''} ${status.charAt(0).toUpperCase() + status.slice(1)}`; } function formatPeriod(start, end) { if (!start) return '–'; const s = new Date(start); const opts = { month: 'short', year: 'numeric' }; return s.toLocaleDateString('en-US', opts); } function escHtml(str) { return String(str).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } // ── Download Audit Report (PDF via print) ───────────────────────── async function downloadAuditReport(billId) { try { const resp = await fetch(`/api/bills/${billId}`); const data = await resp.json(); if (!data.success) return; const b = data.bill; const findings = data.findings || []; const totalSavings = findings.reduce((s, f) => s + parseFloat(f.estimated_savings || 0), 0); const today = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); const win = window.open('', '_blank'); win.document.write(` Audit Report – ${b.title}

Utility Bill Audit Report

${escHtml(b.title)} · ${escHtml(b.utility_provider || '')} · ${escHtml(b.location_name || '')} · Report generated ${today}

Bill Summary

Total Amount
$${parseFloat(b.amount||0).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}
Usage
${parseFloat(b.usage_kwh||0).toLocaleString()} kWh
Peak Demand
${parseFloat(b.demand_kw||0).toFixed(1)} kW
${totalSavings > 0 ? `
💰 Total Potential Savings Identified: $${totalSavings.toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}/month
` : ''}

Audit Findings (${findings.length})

${findings.length === 0 ? '

No findings. Bill appears clean.

' : findings.map(f => { const savings = parseFloat(f.estimated_savings || 0); return `
${f.severity} ${findingTitle(f.finding_type)} ${savings > 0 ? `$${savings.toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}/mo savings` : ''}
${escHtml(f.description || '')}
${f.recommendation ? `
→ ${escHtml(f.recommendation)}
` : ''}
`; }).join('')}
This report is for educational purposes only and does not constitute professional energy consulting advice. Estimated savings are projections based on industry data and may vary. Consult a licensed energy professional before taking action.
`); win.document.close(); win.print(); } catch (err) { alert('Could not generate report: ' + err.message); } } // ═══════════════════════════════════════════════════════════════════ // AUTOMATED BILL AUDITOR // ═══════════════════════════════════════════════════════════════════ let _auditInited = false; async function initAuditor() { if (_auditInited) return; _auditInited = true; await loadAuditBillSelector(); await loadAuditHistory(); } async function loadAuditBillSelector() { try { const resp = await fetch('/api/bills'); const data = await resp.json(); if (!data.success) return; const sel = document.getElementById('auditBillSelect'); sel.innerHTML = ''; data.bills.forEach(b => { const opt = document.createElement('option'); opt.value = b.id; opt.textContent = b.title + (b.status === 'audited' ? ' ✓' : b.status === 'flagged' ? ' ⚠' : ''); sel.appendChild(opt); }); } catch (e) { console.warn('loadAuditBillSelector:', e.message); } } async function runAudit() { const billId = document.getElementById('auditBillSelect').value; if (!billId) { alert('Please select a bill to audit.'); return; } const btn = document.getElementById('runAuditBtn'); const loading = document.getElementById('auditLoading'); btn.innerHTML = '
Running…'; btn.disabled = true; loading.classList.add('active'); try { const resp = await fetch(`/api/bills/${billId}/audit`, { method: 'POST' }); const data = await resp.json(); if (!data.success) throw new Error(data.message); loading.classList.remove('active'); await loadAuditHistory(); // Open the bill detail modal to show findings openBillDetail(billId); } catch (err) { loading.classList.remove('active'); alert('Audit error: ' + err.message); } finally { btn.innerHTML = ' Run Audit'; btn.disabled = false; } } async function loadAuditHistory() { const tbody = document.getElementById('auditHistoryBody'); tbody.innerHTML = 'Loading…'; try { const resp = await fetch('/api/audits'); const data = await resp.json(); if (!data.success) throw new Error(data.message); const audits = data.audits; // Update stats const totalFindings = audits.reduce((s, a) => s + parseInt(a.finding_count || 0), 0); const totalSavings = audits.reduce((s, a) => s + parseFloat(a.total_savings || 0), 0); const totalCritical = audits.reduce((s, a) => s + parseInt(a.critical_count || 0), 0); document.getElementById('statAuditCount').textContent = audits.length; document.getElementById('statAuditCountSub').textContent = 'Bills fully analyzed'; document.getElementById('statIssuesFound').textContent = totalFindings; document.getElementById('statIssuesSub').textContent = totalFindings > 0 ? `${audits.reduce((s,a)=>s+parseInt(a.warning_count||0),0)} warnings` : 'All clear'; document.getElementById('statAuditSavings').textContent = '$' + Math.round(totalSavings).toLocaleString(); document.getElementById('statCriticalCount').textContent = totalCritical; if (audits.length === 0) { tbody.innerHTML = `
No audits run yet
Select a bill above and click "Run Audit" to start
`; return; } tbody.innerHTML = audits.map(a => { const findings = parseInt(a.finding_count || 0); const critical = parseInt(a.critical_count || 0); const savings = parseFloat(a.total_savings || 0); return `
${escHtml(a.title)}
${formatPeriod(a.billing_period_start, a.billing_period_end)}
${escHtml(a.utility_provider || '–')} $${parseFloat(a.amount||0).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})} ${findings} findings ${critical > 0 ? `
${critical} critical` : ''} ${savings > 0 ? '$' + Math.round(savings).toLocaleString() + '/mo' : '–'} ${statusBadge(a.status)}
`; }).join(''); // Also refresh bill selector loadAuditBillSelector(); } catch (err) { tbody.innerHTML = `${err.message}`; } }