IT Budget Dashboard
Annual Budget
$0
YTD Actuals
$0
Remaining Budget
$0
Budget Burn Rate
0%
Budget vs. Actual Spend (YTD)
YTD Spending by Cost Center
Budget vs. Actual Variance
Manage Annual Budgets
| Cost Center | Annual Budget | Action |
|---|
Log Monthly Actual Expenses
| Month | Cost Center | Amount | Action |
|---|
Error: A required library is missing.
'; return; } // --- DATA MANAGEMENT --- let budgets = JSON.parse(localStorage.getItem('itb_budgets')) || []; let actuals = JSON.parse(localStorage.getItem('itb_actuals')) || []; const getSampleData = () => { budgets = [ { category: 'Personnel', amount: 1200000 }, { category: 'Software Licensing', amount: 350000 }, { category: 'Cloud Services (AWS, Azure)', amount: 250000 }, { category: 'Hardware Procurement', amount: 200000 }, { category: 'Consulting & Training', amount: 75000 }, ]; actuals = []; const today = new Date('2025-07-10'); const currentYear = today.getFullYear(); for (let i = 0; i < today.getMonth(); i++) { const monthStr = `${currentYear}-${String(i + 1).padStart(2, '0')}`; actuals.push({ month: monthStr, category: 'Personnel', amount: 100000 + (Math.random() * 2000 - 1000) }); actuals.push({ month: monthStr, category: 'Software Licensing', amount: 29000 + (Math.random() * 1000 - 500) }); actuals.push({ month: monthStr, category: 'Cloud Services (AWS, Azure)', amount: 20000 + (Math.random() * 3000) }); actuals.push({ month: monthStr, category: 'Hardware Procurement', amount: 15000 + (Math.random() * 5000 - 2500) }); actuals.push({ month: monthStr, category: 'Consulting & Training', amount: 6000 + (Math.random() * 2000 - 1000) }); } }; if (budgets.length === 0) getSampleData(); const saveState = () => { localStorage.setItem('itb_budgets', JSON.stringify(budgets)); localStorage.setItem('itb_actuals', JSON.stringify(actuals)); }; // --- CHART INSTANCES & UTILITIES --- let trendChart, costCenterChart, varianceChart; const tabButtons = document.querySelectorAll('.itb-tab-button'); const tabContents = document.querySelectorAll('.itb-tab-content'); const nextBtn = document.getElementById('itb-next-btn'); const prevBtn = document.getElementById('itb-prev-btn'); const formatCurrency = (val) => val.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits:0 }); const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // --- RENDER FUNCTIONS --- const renderAll = () => { try { const processedData = processData(); renderKPIs(processedData); renderTrendChart(processedData); renderCostCenterChart(processedData); renderVarianceChart(processedData); renderManageTables(); updateNavButtons(); } catch(error) { console.error("Dashboard rendering failed:", error); } }; const processData = () => { const currentYear = new Date('2025-07-10').getFullYear(); const ytdActuals = actuals.filter(a => new Date(a.month).getFullYear() === currentYear); return { ytdActuals }; }; const renderKPIs = ({ ytdActuals }) => { const totalBudget = budgets.reduce((sum, b) => sum + b.amount, 0); const totalSpent = ytdActuals.reduce((sum, a) => sum + a.amount, 0); const remaining = totalBudget - totalSpent; const burnRate = totalBudget > 0 ? (totalSpent / totalBudget) * 100 : 0; document.getElementById('itb-annual-budget-kpi').textContent = formatCurrency(totalBudget); document.getElementById('itb-ytd-actual-kpi').textContent = formatCurrency(totalSpent); document.getElementById('itb-remaining-budget-kpi').textContent = formatCurrency(remaining); document.getElementById('itb-burn-rate-kpi').textContent = `${burnRate.toFixed(1)}%`; }; const renderTrendChart = ({ ytdActuals }) => { const totalBudget = budgets.reduce((sum, b) => sum + b.amount, 0); const cumulativeBudgetData = Array(12).fill(0).map((_, i) => (totalBudget / 12) * (i + 1)); let cumulativeActuals = 0; const cumulativeActualsData = Array(12).fill(null).map((_, i) => { const monthActual = ytdActuals .filter(a => new Date(a.month).getMonth() === i) .reduce((sum, a) => sum + a.amount, 0); if (monthActual > 0) { cumulativeActuals += monthActual; return cumulativeActuals; } return null; }); const options = { chart: { type: 'area', height: 350, toolbar: { show: false } }, series: [ { name: 'Cumulative Budget', data: cumulativeBudgetData }, { name: 'Cumulative Actuals', data: cumulativeActualsData } ], xaxis: { categories: MONTHS }, yaxis: { labels: { formatter: val => formatCurrency(val/1000) + 'k' } }, colors: ['var(--itb-primary-color)', 'var(--itb-secondary-color)'], dataLabels: { enabled: false }, stroke: { curve: 'smooth' } }; if(trendChart) trendChart.destroy(); document.querySelector("#itb-trend-chart").innerHTML = ''; trendChart = new ApexCharts(document.querySelector("#itb-trend-chart"), options); trendChart.render(); }; const renderCostCenterChart = ({ ytdActuals }) => { const byCategory = ytdActuals.reduce((acc, a) => { acc[a.category] = (acc[a.category] || 0) + a.amount; return acc; }, {}); const options = { chart: { type: 'donut', height: 350 }, series: Object.values(byCategory), labels: Object.keys(byCategory), legend: { position: 'bottom' } }; if(costCenterChart) costCenterChart.destroy(); document.querySelector("#itb-cost-center-chart").innerHTML = ''; costCenterChart = new ApexCharts(document.querySelector("#itb-cost-center-chart"), options); costCenterChart.render(); }; const renderVarianceChart = ({ ytdActuals }) => { const currentMonthIndex = new Date('2025-07-10').getMonth(); // 6 for July const variances = budgets.map(b => { const ytdBudget = (b.amount / 12) * (currentMonthIndex + 1); const ytdActual = ytdActuals.filter(a => a.category === b.category).reduce((s,a) => s+a.amount, 0); return { category: b.category, variance: ytdActual - ytdBudget }; }); const options = { chart: { type: 'bar', height: 350 }, series: [{ name: 'Variance', data: variances.map(v => v.variance.toFixed(0)) }], xaxis: { categories: variances.map(v => v.category) }, plotOptions: { bar: { horizontal: true } }, colors: variances.map(v => v.variance > 0 ? 'var(--itb-over-budget-color)' : 'var(--itb-under-budget-color)'), tooltip: { y: { formatter: (val) => formatCurrency(val) + (val > 0 ? ' Over' : ' Under') } }, dataLabels: { formatter: val => formatCurrency(val) } }; if(varianceChart) varianceChart.destroy(); document.querySelector("#itb-variance-chart").innerHTML = ''; varianceChart = new ApexCharts(document.querySelector("#itb-variance-chart"), options); varianceChart.render(); }; const renderManageTables = () => { const budgetsTbody = document.getElementById('itb-budgets-tbody'); budgetsTbody.innerHTML = ''; budgets.forEach(b => { budgetsTbody.innerHTML += `