function updateMembersList() {
const list = document.getElementById('membersList');
list.innerHTML = familyData.members.map(member => `
${member}
`).join('');
}
// Form submissions
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('incomeForm').addEventListener('submit', function(e) {
e.preventDefault();
const income = {
id: Date.now(),
member: document.getElementById('incomeMember').value,
source: document.getElementById('incomeSource').value,
amount: parseFloat(document.getElementById('incomeAmount').value),
date: document.getElementById('incomeDate').value,
description: document.getElementById('incomeDescription').value,
timestamp: new Date().toISOString()
};
familyData.income.push(income);
this.reset();
setDefaultDate();
updateDashboard();
updateIncomeHistory();
saveData();
showNotification('Income added successfully!', 'success');
});
document.getElementById('expenseForm').addEventListener('submit', function(e) {
e.preventDefault();
const expense = {
id: Date.now(),
member: document.getElementById('expenseMember').value,
category: document.getElementById('expenseCategory').value,
amount: parseFloat(document.getElementById('expenseAmount').value),
date: document.getElementById('expenseDate').value,
description: document.getElementById('expenseDescription').value,
timestamp: new Date().toISOString()
};
familyData.expenses.push(expense);
this.reset();
setDefaultDate();
updateDashboard();
updateExpenseHistory();
saveData();
showNotification('Expense added successfully!', 'success');
});
document.getElementById('savingsForm').addEventListener('submit', function(e) {
e.preventDefault();
const savings = {
id: Date.now(),
member: document.getElementById('savingsMember').value,
type: document.getElementById('savingsType').value,
amount: parseFloat(document.getElementById('savingsAmount').value),
date: document.getElementById('savingsDate').value,
description: document.getElementById('savingsDescription').value,
timestamp: new Date().toISOString()
};
familyData.savings.push(savings);
this.reset();
setDefaultDate();
updateDashboard();
updateSavingsHistory();
saveData();
showNotification('Savings added successfully!', 'success');
});
});
function updateDashboard() {
const totalIncome = familyData.income.reduce((sum, item) => sum + item.amount, 0);
const totalExpenses = familyData.expenses.reduce((sum, item) => sum + item.amount, 0);
const totalSavings = familyData.savings.reduce((sum, item) => sum + item.amount, 0);
const netWorth = totalIncome - totalExpenses + totalSavings;
document.getElementById('totalIncomeDisplay').textContent = formatCurrency(totalIncome);
document.getElementById('totalExpenseDisplay').textContent = formatCurrency(totalExpenses);
document.getElementById('totalSavingsDisplay').textContent = formatCurrency(totalSavings);
document.getElementById('netWorthDisplay').textContent = formatCurrency(netWorth);
updateRecentTransactions();
}
function updateRecentTransactions() {
const allTransactions = [
...familyData.income.map(i => ({...i, type: 'Income', category: i.source})),
...familyData.expenses.map(e => ({...e, type: 'Expense'})),
...familyData.savings.map(s => ({...s, type: 'Savings', category: s.type}))
].sort((a, b) => new Date(b.date) - new Date(a.date)).slice(0, 10);
const tbody = document.querySelector('#recentTransactions tbody');
tbody.innerHTML = allTransactions.map(t => `
| ${formatDate(t.date)} |
${t.type} |
${t.category || t.source || t.type} |
${t.member} |
${formatCurrency(t.amount)} |
${t.description || '-'} |
`).join('');
}
function updateIncomeHistory() {
const tbody = document.querySelector('#incomeHistory tbody');
tbody.innerHTML = familyData.income.sort((a, b) => new Date(b.date) - new Date(a.date)).map(income => `
| ${formatDate(income.date)} |
${income.member} |
${income.source} |
${formatCurrency(income.amount)} |
${income.description || '-'} |
${isAdminMode ? `
` : `
Admin required
`}
|
`).join('');
}
function updateExpenseHistory() {
const tbody = document.querySelector('#expenseHistory tbody');
tbody.innerHTML = familyData.expenses.sort((a, b) => new Date(b.date) - new Date(a.date)).map(expense => `
| ${formatDate(expense.date)} |
${expense.member} |
${expense.category} |
${formatCurrency(expense.amount)} |
${expense.description || '-'} |
${isAdminMode ? `
` : `
Admin required
`}
|
`).join('');
}
function updateSavingsHistory() {
const tbody = document.querySelector('#savingsHistory tbody');
tbody.innerHTML = familyData.savings.sort((a, b) => new Date(b.date) - new Date(a.date)).map(savings => `
| ${formatDate(savings.date)} |
${savings.member} |
${savings.type} |
${formatCurrency(savings.amount)} |
${savings.description || '-'} |
${isAdminMode ? `
` : `
Admin required
`}
|
`).join('');
}
function updateReports() {
const now = new Date();
const currentMonth = now.getMonth();
const currentYear = now.getFullYear();
const monthlyIncome = familyData.income
.filter(i => {
const date = new Date(i.date);
return date.getMonth() === currentMonth && date.getFullYear() === currentYear;
})
.reduce((sum, i) => sum + i.amount, 0);
const monthlyExpenses = familyData.expenses
.filter(e => {
const date = new Date(e.date);
return date.getMonth() === currentMonth && date.getFullYear() === currentYear;
})
.reduce((sum, e) => sum + e.amount, 0);
const monthlySavings = familyData.savings
.filter(s => {
const date = new Date(s.date);
return date.getMonth() === currentMonth && date.getFullYear() === currentYear;
})
.reduce((sum, s) => sum + s.amount, 0);
// Monthly Summary
document.getElementById('monthlyReport').innerHTML = `
Income: ${formatCurrency(monthlyIncome)}
Expenses: ${formatCurrency(monthlyExpenses)}
Savings: ${formatCurrency(monthlySavings)}
Net: ${formatCurrency(monthlyIncome - monthlyExpenses + monthlySavings)}
`;
// Expense Breakdown by Category
const expensesByCategory = {};
const currentMonthExpenses = familyData.expenses.filter(e => {
const date = new Date(e.date);
return date.getMonth() === currentMonth && date.getFullYear() === currentYear;
});
currentMonthExpenses.forEach(expense => {
if (!expensesByCategory[expense.category]) {
expensesByCategory[expense.category] = 0;
}
expensesByCategory[expense.category] += expense.amount;
});
const totalMonthlyExpenses = Object.values(expensesByCategory).reduce((sum, amount) => sum + amount, 0);
let breakdownHTML = '';
if (totalMonthlyExpenses > 0) {
for (const [category, amount] of Object.entries(expensesByCategory)) {
const percentage = ((amount / totalMonthlyExpenses) * 100).toFixed(1);
const displayName = category.charAt(0).toUpperCase() + category.slice(1);
breakdownHTML += `
${displayName}
${formatCurrency(amount)} (${percentage}%)
`;
}
} else {
breakdownHTML = 'No expenses recorded this month
';
}
document.getElementById('expenseBreakdown').innerHTML = breakdownHTML;
// Family Member Spending
const spendingByMember = {};
currentMonthExpenses.forEach(expense => {
if (!spendingByMember[expense.member]) {
spendingByMember[expense.member] = 0;
}
spendingByMember[expense.member] += expense.amount;
});
let memberHTML = '';
if (Object.keys(spendingByMember).length > 0) {
for (const [member, amount] of Object.entries(spendingByMember)) {
const percentage = totalMonthlyExpenses > 0 ? ((amount / totalMonthlyExpenses) * 100).toFixed(1) : 0;
memberHTML += `
${member}
${formatCurrency(amount)} (${percentage}%)
`;
}
} else {
memberHTML = 'No expenses recorded this month
';
}
document.getElementById('memberSpending').innerHTML = memberHTML;
// Create Visual Chart
createExpenseChart(expensesByCategory, totalMonthlyExpenses);
}
function createExpenseChart(expensesByCategory, totalExpenses) {
const chartContainer = document.getElementById('chartContainer');
if (totalExpenses === 0) {
chartContainer.innerHTML = 'No expense data to display
';
return;
}
// Create a simple donut chart using CSS
const categories = Object.entries(expensesByCategory);
let currentAngle = 0;
const colors = ['#667eea', '#764ba2', '#f44336', '#4CAF50', '#FF9800', '#2196F3', '#9C27B0', '#795548', '#607D8B', '#FFC107'];
let chartHTML = `
Total
${formatCurrency(totalExpenses)}
Legend
`;
categories.forEach(([category, amount], index) => {
const percentage = ((amount / totalExpenses) * 100).toFixed(1);
const displayName = category.charAt(0).toUpperCase() + category.slice(1);
chartHTML += `
${displayName}
${formatCurrency(amount)} (${percentage}%)
`;
});
chartHTML += `
`;
chartContainer.innerHTML = chartHTML;
}
function updateSettings() {
const totalRecords = familyData.income.length + familyData.expenses.length + familyData.savings.length;
document.getElementById('totalRecords').textContent = totalRecords;
if (familyData.lastSync) {
document.getElementById('lastSync').textContent = new Date(familyData.lastSync).toLocaleString();
}
document.getElementById('appStatus').textContent = isOnline ? 'Online' : 'Offline';
}
function exportData() {
const dataStr = JSON.stringify(familyData, null, 2);
const dataBlob = new Blob([dataStr], {type: 'application/json'});
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `family-finance-data-${new Date().toISOString().split('T')[0]}.json`;
link.click();
URL.revokeObjectURL(url);
showNotification('Data exported successfully!', 'success');
}
function clearAllData() {
if (confirm('This will delete ALL data permanently. Are you sure?')) {
familyData = {
members: ['Dad', 'Mom', 'Son', 'Daughter'],
income: [],
expenses: [],
savings: [],
expenseCategories: ['housing', 'food', 'transportation', 'healthcare', 'entertainment', 'shopping', 'utilities', 'education', 'insurance', 'other'],
lastSync: null
};
localStorage.removeItem('familyFinanceData');
populateMemberSelects();
updateDashboard();
showNotification('All data cleared!', 'success');
}
}
function formatCurrency(amount) {
return new Intl.NumberFormat('en-PK', {
style: 'currency',
currency: 'PKR'
}).format(amount);
}
function formatDate(dateString) {
return new Date(dateString).toLocaleDateString('en-US');
}
function showNotification(message, type) {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => notification.classList.add('show'), 100);
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => document.body.removeChild(notification), 300);
}, 3000);
}