Personal Finance Tracker — HTML+JS+CSS

Personal Finance Tracker

Dropdown-first entry • Local storage • Excel & PDF export

Add Transaction
Hold Ctrl/Cmd to select multiple
Transactions
Total Income
$0.00
Total Expenses
$0.00
Net
$0.00
DateTypeCategorySubcat AccountMethod/ToCounterparty AmountCurrencyTagsNotes
`); win.document.close(); } } function tableFromRows(rows){ const headers = Object.keys(rows[0]); const head = `${headers.map(h=>`${h}`).join("")}`; const body = rows.map(r=> `${headers.map(h=>`${String(r[h])}`).join("")}`).join(""); return `${head}${body}`; } function summarize(rows){ let income=0, expense=0; const byCategory = {}; rows.forEach(r=>{ const amt = +r.Amount || 0; if(r.Type === "Income") income += amt; else if(r.Type === "Expense") expense += amt; const key = r.Category || "Uncategorized"; byCategory[key] = (byCategory[key]||0) + (r.Type==="Expense" ? -Math.abs(amt) : Math.abs(amt)); }); return { income, expense, net: income - expense, byCategory }; } /* ---------- Utilities & events ---------- */ function seedMonthSelectToCurrent(){ const today = new Date(); $("#date").value = today.toISOString().slice(0,10); } function attachOtherHandlers(){ const pairs = [ ["#account","#accountOther"], ["#toAccount","#toAccountOther"], ["#method","#methodOther"], ["#counterparty","#counterpartyOther"], ]; pairs.forEach(([sel,other])=>{ const s = $(sel), i = $(other); s.addEventListener('change', ()=> showIfOther(s,i)); }); $("#category").addEventListener('change', ()=>{ populateSubcategory($("#type").value, $("#category").value); }); $("#type").addEventListener('change', handleTypeUI); } function addListeners(){ $("#txForm").addEventListener('submit', addTx); $("#resetForm").addEventListener('click', ()=> { $("#txForm").reset(); handleTypeUI(); }); $("#filterMonth").addEventListener('change', render); $("#filterType").addEventListener('change', render); $("#filterAccount").addEventListener('change', render); $("#exportExcel").addEventListener('click', exportExcel); $("#exportPDF").addEventListener('click', exportPDF); $("#clearAll").addEventListener('click', ()=>{ if(confirm("Delete all transactions from this device?")){ Transactions=[]; save(); render(); } }); $("#loadSample").addEventListener('click', loadSample); } function loadSample(){ const today = new Date(); const y = today.getFullYear(), m = today.getMonth(); const date = (d)=> new Date(y, m, d).toISOString().slice(0,10); const sample = [ {date:date(2), type:"Income", amount:3200, currency:"USD", account:"Main Checking", toAccount:"", method:"ACH", category:"Salary", subcategory:"Base Pay", counterparty:"Employer", recurrence:"Monthly", tags:["Work"], notes:""}, {date:date(3), type:"Expense", amount:145.73, currency:"USD", account:"Credit Card — Primary", toAccount:"", method:"Credit Card (Visa)", category:"Food & Dining", subcategory:"Groceries", counterparty:"Walmart", recurrence:"One-time", tags:["Home"], notes:"Stock-up"}, {date:date(5), type:"Expense", amount:69.99, currency:"USD", account:"Credit Card — Primary", toAccount:"", method:"Credit Card (Visa)", category:"Subscriptions", subcategory:"Software", counterparty:"Software", recurrence:"Monthly", tags:["Subscription","Business"], notes:""}, {date:date(6), type:"Expense", amount:52.10, currency:"USD", account:"Main Checking", toAccount:"", method:"Debit Card", category:"Transportation", subcategory:"Fuel", counterparty:"Shell", recurrence:"One-time", tags:["Car"], notes:""}, {date:date(7), type:"Transfer", amount:500, currency:"USD", account:"Main Checking", toAccount:"Savings", method:"", category:"Account Move", subcategory:"Internal Transfer", counterparty:"", recurrence:"Monthly", tags:["Savings"], notes:""}, ]; sample.forEach(s => { s.id = String(Date.now())+Math.random(); }); Transactions = Transactions.concat(sample); save(); render(); } function buildMonthFilter(){ initFilters(); } function init(){ buildMonthFilter(); seedMonthSelectToCurrent(); handleTypeUI(); attachOtherHandlers(); addListeners(); load(); render(); } /* ---------- start ---------- */ document.addEventListener('DOMContentLoaded', init);
Scroll to Top