Budget Tracking Dashboard

Monthly Overview

Total Budget

$0

Total Spent

$0

Remaining

$0

Overall Budget Usage

Spending by Category

Transactions for Selected Month

DateDescriptionCategoryAmount

Manage Monthly Budgets

CategoryMonthly BudgetAction

Log an Expense

DateDescriptionCategoryAmountAction

Error: A required library is missing.

'; return; } // --- DATA MANAGEMENT --- let budgets = JSON.parse(localStorage.getItem('btd_budgets')) || []; let expenses = JSON.parse(localStorage.getItem('btd_expenses')) || []; const getSampleData = () => { budgets = [ { category: 'Rent/Mortgage', amount: 2200 }, { category: 'Utilities', amount: 350 }, { category: 'Groceries', amount: 700 }, { category: 'Transportation', amount: 400 }, { category: 'Entertainment', amount: 250 }, { category: 'Health Insurance', amount: 450 } ]; const today = new Date('2025-07-08'); const year = today.getFullYear(); const month = today.getMonth(); const createDate = (day) => new Date(year, month, day).toISOString().split('T')[0]; expenses = [ { id: 'e1', date: createDate(1), desc: 'Apartment Rent', category: 'Rent/Mortgage', amount: 2200 }, { id: 'e2', date: createDate(2), desc: 'Electricity Bill', category: 'Utilities', amount: 110.45 }, { id: 'e3', date: createDate(3), desc: 'Whole Foods', category: 'Groceries', amount: 175.20 }, { id: 'e4', date: createDate(5), desc: 'Gasoline', category: 'Transportation', amount: 52.80 }, { id: 'e5', date: createDate(6), desc: 'Movie Tickets', category: 'Entertainment', amount: 35.00 }, { id: 'e6', date: createDate(7), desc: 'Health Premium', category: 'Health Insurance', amount: 450.00 } ]; }; if (budgets.length === 0) getSampleData(); const saveState = () => { localStorage.setItem('btd_budgets', JSON.stringify(budgets)); localStorage.setItem('btd_expenses', JSON.stringify(expenses)); }; // --- CHART INSTANCES & UTILITIES --- let gaugeChart, varianceChart; const tabButtons = document.querySelectorAll('.btd-tab-button'); const tabContents = document.querySelectorAll('.btd-tab-content'); const nextBtn = document.getElementById('btd-next-btn'); const prevBtn = document.getElementById('btd-prev-btn'); const monthFilter = document.getElementById('btd-month-filter'); const formatCurrency = (val) => val.toLocaleString('en-US', { style: 'currency', currency: 'USD' }); // --- RENDER FUNCTIONS --- const renderAll = () => { try { const selectedMonth = monthFilter.value; // YYYY-MM format const [year, month] = selectedMonth.split('-').map(Number); const filteredExpenses = expenses.filter(e => { const d = new Date(e.date + 'T00:00:00'); return d.getFullYear() === year && d.getMonth() === month - 1; }); renderKPIs(filteredExpenses); renderGaugeChart(filteredExpenses); renderVarianceChart(filteredExpenses); renderTransactionTables(filteredExpenses); updateNavButtons(); } catch(error) { console.error("Dashboard rendering failed:", error); } }; const renderKPIs = (data) => { const totalBudget = budgets.reduce((sum, b) => sum + b.amount, 0); const totalSpent = data.reduce((sum, e) => sum + e.amount, 0); const remaining = totalBudget - totalSpent; document.getElementById('btd-budget-kpi').textContent = formatCurrency(totalBudget); document.getElementById('btd-spent-kpi').textContent = formatCurrency(totalSpent); const remainingEl = document.getElementById('btd-remaining-kpi'); remainingEl.textContent = formatCurrency(remaining); remainingEl.className = remaining >= 0 ? 'remaining positive' : 'remaining negative'; }; const renderGaugeChart = (data) => { const totalBudget = budgets.reduce((sum, b) => sum + b.amount, 0); const totalSpent = data.reduce((sum, e) => sum + e.amount, 0); const percentageSpent = totalBudget > 0 ? Math.round((totalSpent / totalBudget) * 100) : 0; const options = { chart: { type: 'radialBar', height: 350 }, series: [percentageSpent], plotOptions: { radialBar: { hollow: { size: '70%' }, dataLabels: { name: { show: true, offsetY: -10, fontSize: '16px', color: '#6c757d', formatter: () => 'Budget Used' }, value: { show: true, fontSize: '36px', formatter: (val) => `${val}%` } } } }, stroke: { lineCap: 'round' }, colors: [percentageSpent > 100 ? 'var(--btd-danger-color)' : percentageSpent > 80 ? 'var(--btd-warning-color)' : 'var(--btd-success-color)'] }; if(gaugeChart) gaugeChart.destroy(); document.querySelector("#btd-gauge-chart").innerHTML = ''; gaugeChart = new ApexCharts(document.querySelector("#btd-gauge-chart"), options); gaugeChart.render(); }; const renderVarianceChart = (data) => { const seriesData = budgets.map(b => { const actual = data.filter(e => e.category === b.category).reduce((sum, e) => sum + e.amount, 0); return { category: b.category, budget: b.amount, actual }; }); const options = { chart: { type: 'bar', height: 350, stacked: true }, series: [ { name: 'Spent', data: seriesData.map(d => d.actual) }, { name: 'Remaining', data: seriesData.map(d => Math.max(0, d.budget - d.actual)) } ], xaxis: { categories: seriesData.map(d => d.category) }, plotOptions: { bar: { horizontal: false } }, colors: ['var(--btd-secondary-color)', '#e0e0e0'], dataLabels: { enabled: false } }; if(varianceChart) varianceChart.destroy(); document.querySelector("#btd-variance-chart").innerHTML = ''; varianceChart = new ApexCharts(document.querySelector("#btd-variance-chart"), options); varianceChart.render(); }; const renderTransactionTables = (filteredData) => { const recentTbody = document.getElementById('btd-transactions-tbody'); recentTbody.innerHTML = ''; filteredData.forEach(e => { recentTbody.innerHTML += `${e.date}${e.desc}${e.category}${formatCurrency(e.amount)}`; }); // Manage tab tables const budgetsTbody = document.getElementById('btd-budgets-tbody'); budgetsTbody.innerHTML = ''; budgets.forEach(b => { budgetsTbody.innerHTML += `${b.category}${formatCurrency(b.amount)}`; }); const expensesTbody = document.getElementById('btd-expenses-tbody'); expensesTbody.innerHTML = ''; expenses.sort((a,b) => new Date(b.date) - new Date(a.date)).forEach(e => { expensesTbody.innerHTML += `${e.date}${e.desc}${e.category}${formatCurrency(e.amount)}`; }); document.getElementById('btd-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(`.btd-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); }); monthFilter.addEventListener('change', renderAll); document.getElementById('btd-budget-form').addEventListener('submit', e => { e.preventDefault(); const category = document.getElementById('btd-budget-category').value; const amount = parseFloat(document.getElementById('btd-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('btd-expense-form').addEventListener('submit', e => { e.preventDefault(); expenses.unshift({ id: 'e' + Date.now(), date: document.getElementById('btd-expense-date').value, desc: document.getElementById('btd-expense-desc').value, category: document.getElementById('btd-expense-category').value, amount: parseFloat(document.getElementById('btd-expense-amount').value) }); saveState(); renderAll(); e.target.reset(); }); document.getElementById('btd-manage-tab').addEventListener('click', e => { if(e.target.tagName !== 'BUTTON' || !e.target.classList.contains('btd-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 === 'expense') { expenses = expenses.filter(ex => ex.id !== e.target.dataset.id); } saveState(); renderAll(); } }); // --- PDF EXPORT --- document.getElementById('btd-download-pdf-btn').addEventListener('click', function() { const btn = this; btn.textContent = 'Generating...'; btn.disabled = true; const content = document.getElementById('btd-pdf-capture-area'); html2canvas(content, { scale: 2 }).then(canvas => { const doc = new jsPDF(); const imgData = canvas.toDataURL('image/png'); doc.setFontSize(18); doc.text(`Budget Report - ${monthFilter.value}`, 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(`Budget_Report_${monthFilter.value}.pdf`); }).finally(() => { btn.textContent = 'Download Report'; btn.disabled = false; }); }); // --- INITIALIZATION --- const today = new Date(); monthFilter.value = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}`; renderAll(); });
Scroll to Top