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 CenterAnnual BudgetAction

Log Monthly Actual Expenses

MonthCost CenterAmountAction

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 += `${b.category}${formatCurrency(b.amount)}`; }); const actualsTbody = document.getElementById('itb-actuals-tbody'); actualsTbody.innerHTML = ''; actuals.sort((a,b) => new Date(b.month) - new Date(a.month)).forEach(a => { actualsTbody.innerHTML += `${a.month}${a.category}${formatCurrency(a.amount)}`; }); document.getElementById('itb-category-list').innerHTML = budgets.map(b=>``).join(''); }; // --- EVENT HANDLING --- const switchTab = (tabId) => { tabContents.forEach(c => c.style.display = 'none'); tabButtons.forEach(b => b.classList.remove('active')); const activeContent = document.getElementById(tabId); const activeButton = document.querySelector(`.itb-tab-button[data-tab="${tabId}"]`); if (activeContent && activeButton) { activeContent.style.display = 'block'; activeButton.classList.add('active'); } updateNavButtons(); }; const updateNavButtons = () => { const i = [...tabButtons].findIndex(b => b.classList.contains('active')); prevBtn.disabled = i === 0; nextBtn.disabled = i === tabButtons.length - 1; }; tabButtons.forEach(b => b.addEventListener('click', () => switchTab(b.dataset.tab))); nextBtn.addEventListener('click', () => { const i = [...tabButtons].findIndex(b=>b.classList.contains('active')); if (i < tabButtons.length - 1) switchTab(tabButtons[i+1].dataset.tab); }); prevBtn.addEventListener('click', () => { const i = [...tabButtons].findIndex(b=>b.classList.contains('active')); if (i > 0) switchTab(tabButtons[i-1].dataset.tab); }); document.getElementById('itb-budget-form').addEventListener('submit', e => { e.preventDefault(); const category = document.getElementById('itb-budget-category').value; const amount = parseFloat(document.getElementById('itb-budget-amount').value); const existingIndex = budgets.findIndex(b => b.category === category); if (existingIndex > -1) budgets[existingIndex].amount = amount; else budgets.push({ category, amount }); saveState(); renderAll(); e.target.reset(); }); document.getElementById('itb-actuals-form').addEventListener('submit', e => { e.preventDefault(); const month = document.getElementById('itb-actual-month').value; const category = document.getElementById('itb-actual-category').value; const amount = parseFloat(document.getElementById('itb-actual-amount').value); const existingIndex = actuals.findIndex(a => a.month === month && a.category === category); if (existingIndex > -1) actuals[existingIndex].amount = amount; else actuals.push({ month, category, amount }); saveState(); renderAll(); e.target.reset(); }); document.getElementById('itb-manage-tab').addEventListener('click', e => { if(e.target.tagName !== 'BUTTON' || !e.target.classList.contains('itb-btn-danger')) return; if(confirm(`Are you sure you want to delete this item?`)) { if (e.target.dataset.type === 'budget') { budgets = budgets.filter(b => b.category !== e.target.dataset.category); } else if (e.target.dataset.type === 'actual') { const [month, category] = e.target.dataset.id.split('|'); actuals = actuals.filter(a => !(a.month === month && a.category === category)); } saveState(); renderAll(); } }); // PDF EXPORT document.getElementById('itb-download-pdf-btn').addEventListener('click', function() { const btn = this; btn.textContent = 'Generating...'; btn.disabled = true; const content = document.getElementById('itb-pdf-capture-area'); html2canvas(content, { scale: 2 }).then(canvas => { const doc = new jsPDF(); const imgData = canvas.toDataURL('image/png'); doc.setFontSize(18); doc.text('IT Budget Report', 14, 22); const imgWidth = doc.internal.pageSize.getWidth() - 28; const imgHeight = (canvas.height * imgWidth) / canvas.width; doc.addImage(imgData, 'PNG', 14, 30, imgWidth, imgHeight); doc.save('IT_Budget_Report.pdf'); }).finally(() => { btn.textContent = 'Download Report'; btn.disabled = false; }); }); // --- INITIALIZATION --- renderAll(); });
Scroll to Top