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 = `
`; categories.forEach(([category, amount], index) => { const percentage = (amount / totalExpenses) * 100; const angle = (percentage / 100) * 360; const radius = 80; const circumference = 2 * Math.PI * radius; const strokeLength = (angle / 360) * circumference; chartHTML += ` `; currentAngle += angle; }); 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); } 0; padding-left: 20px;">
  • Access code protection
  • Admin authorization for edits
  • Auto-lockout after failed attempts
  • Hidden from search engines
  • Session timeout after inactivity
  • Data Management

    Data syncs automatically with Google Sheets when connected

    Application Info

    Version: 2.0.0 PWA

    Last Sync: Never

    Total Records: 0

    Status: Ready