Personal Finance Tracker — HTML+JS+CSS
Add Transaction
Transactions
| Date | Type | Category | Subcat |
Account | Method/To | Counterparty |
Amount | Currency | Tags | Notes | |
`);
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);