'use client'; interface Account { id: string; name: string; type: 'checking' | 'savings' | 'credit_card' | 'mortgage' | 'investment' | 'loan'; balance: number; color: string; } interface Envelope { id: string; name: string; allocated: number; spent: number; color: string; } interface Transaction { id: string; amount: number; date: Date; envelopeId?: string; } interface DashboardChartsProps { accounts: Account[]; envelopes: Envelope[]; transactions: Transaction[]; visibleCharts?: { incomeExpenses: boolean; budgetAlerts: boolean; assetsDebt: boolean; envelopeSpending: boolean; accountBalances: boolean; dailySpending: boolean; }; } const assetTypes: Account['type'][] = ['checking', 'savings', 'investment']; const debtTypes: Account['type'][] = ['credit_card', 'mortgage', 'loan']; export default function DashboardCharts({ accounts, envelopes, transactions, visibleCharts = { incomeExpenses: true, budgetAlerts: true, assetsDebt: true, envelopeSpending: true, accountBalances: true, dailySpending: true, } }: DashboardChartsProps) { // Separate accounts into assets and debt const assetAccounts = accounts.filter(acc => assetTypes.includes(acc.type)); const debtAccounts = accounts.filter(acc => debtTypes.includes(acc.type)); const totalAssets = assetAccounts.reduce((sum, acc) => sum + acc.balance, 0); const totalDebt = Math.abs(debtAccounts.reduce((sum, acc) => sum + acc.balance, 0)); // Calculate spending by envelope const envelopeSpending = envelopes.map(env => ({ name: env.name, spent: Math.abs(env.spent), allocated: env.allocated, color: env.color, percentage: env.allocated > 0 ? (Math.abs(env.spent) / env.allocated) * 100 : 0 })).sort((a, b) => b.spent - a.spent); // Find envelopes with alerts (overspent or near limit >80%) const envelopeAlerts = envelopes.filter(env => { const percentage = env.allocated > 0 ? (Math.abs(env.spent) / env.allocated) * 100 : 0; return percentage >= 80; }).sort((a, b) => { const aPerc = a.allocated > 0 ? (Math.abs(a.spent) / a.allocated) * 100 : 0; const bPerc = b.allocated > 0 ? (Math.abs(b.spent) / b.allocated) * 100 : 0; return bPerc - aPerc; }); // Calculate total balance across accounts const totalBalance = accounts.reduce((sum, acc) => sum + acc.balance, 0); const accountBalances = accounts.map(acc => ({ name: acc.name, balance: acc.balance, color: acc.color, percentage: totalBalance > 0 ? (acc.balance / totalBalance) * 100 : 0 })); // Calculate income vs expenses (this month) const now = new Date(); const monthStart = new Date(now.getFullYear(), now.getMonth(), 1); const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0); const monthTransactions = transactions.filter(t => { const txDate = t.date instanceof Date ? t.date : new Date(t.date); return txDate >= monthStart && txDate <= monthEnd; }); const totalIncome = monthTransactions .filter(t => t.amount > 0 && !t.envelopeId) .reduce((sum, t) => sum + t.amount, 0); const totalExpenses = monthTransactions .filter(t => t.amount < 0) .reduce((sum, t) => sum + Math.abs(t.amount), 0); const netIncome = totalIncome - totalExpenses; // Calculate spending over time (last 30 days) const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); const recentTransactions = transactions.filter(t => { const txDate = t.date instanceof Date ? t.date : new Date(t.date); return txDate >= thirtyDaysAgo; }); const dailySpending: { [key: string]: number } = {}; recentTransactions.forEach(t => { const txDate = t.date instanceof Date ? t.date : new Date(t.date); const dateKey = txDate.toISOString().split('T')[0]; if (t.amount < 0) { dailySpending[dateKey] = (dailySpending[dateKey] || 0) + Math.abs(t.amount); } }); const spendingData = Object.entries(dailySpending) .sort((a, b) => a[0].localeCompare(b[0])) .slice(-7); // Last 7 days const maxDailySpending = Math.max(...spendingData.map(([_, amount]) => amount), 1); return (
{/* Income vs Expenses Card */} {visibleCharts.incomeExpenses && (

Income vs Expenses (This Month)

Income ${totalIncome.toFixed(2)}
Expenses ${totalExpenses.toFixed(2)}
Net Income = 0 ? 'text-green-600' : 'text-red-600'}`}> ${netIncome.toFixed(2)}
)} {/* Budget Alerts Card */} {visibleCharts.budgetAlerts && (

Budget Alerts

{envelopeAlerts.length > 0 ? (
{envelopeAlerts.slice(0, 5).map((env) => { const percentage = env.allocated > 0 ? (Math.abs(env.spent) / env.allocated) * 100 : 0; const isOverspent = percentage > 100; return (

{env.name}

{isOverspent ? `Over budget by $${(Math.abs(env.spent) - env.allocated).toFixed(2)}` : `${percentage.toFixed(0)}% of budget used`}

{percentage.toFixed(0)}%
); })}
) : (

✓ All envelopes are within budget

)}
)} {/* Assets & Debt Summary */} {visibleCharts.assetsDebt && (

Assets vs Debt

Total Assets ${totalAssets.toFixed(2)}
{assetAccounts.map(acc => acc.name).join(', ') || 'None'}
Total Debt ${totalDebt.toFixed(2)}
{debtAccounts.map(acc => acc.name).join(', ') || 'None'}
Net Worth = 0 ? 'text-green-600' : 'text-red-600'}`}> ${(totalAssets - totalDebt).toFixed(2)}
)} {/* Envelope Spending Chart */} {visibleCharts.envelopeSpending && (

Envelope Spending

{envelopeSpending.length > 0 ? (
{envelopeSpending.slice(0, 5).map((env) => (
{env.name} ${env.spent.toFixed(2)} / ${env.allocated.toFixed(2)}
100 ? 'bg-red-500' : '' }`} style={{ width: `${Math.min(env.percentage, 100)}%` }} >
{env.percentage > 100 && (

Over budget by ${(env.spent - env.allocated).toFixed(2)}

)}
))}
) : (

No envelope spending data yet.

)}
)} {/* Account Balances Chart */} {visibleCharts.accountBalances && (

Account Balances

{accountBalances.length > 0 ? (
{accountBalances.map((acc) => (
{acc.name} = 0 ? 'text-green-600' : 'text-red-600'}`}> ${acc.balance.toFixed(2)}
))}
Total Balance = 0 ? 'text-green-600' : 'text-red-600'}`}> ${totalBalance.toFixed(2)}
) : (

No account data yet.

)}
)} {/* Monthly Spending History (Last 6 Months) */}

Monthly Spending History (Last 6 Months)

{(() => { const months = Array.from({ length: 6 }, (_, i) => { const d = new Date(now.getFullYear(), now.getMonth() - (5 - i), 1); return { year: d.getFullYear(), month: d.getMonth(), label: d.toLocaleDateString('en-US', { month: 'short', year: '2-digit' }) }; }); const monthlyData = months.map(m => { const start = new Date(m.year, m.month, 1); const end = new Date(m.year, m.month + 1, 0, 23, 59, 59); const income = transactions.filter(t => { const d = t.date instanceof Date ? t.date : new Date(t.date); return t.amount > 0 && !t.envelopeId && d >= start && d <= end; }).reduce((s, t) => s + t.amount, 0); const spent = transactions.filter(t => { const d = t.date instanceof Date ? t.date : new Date(t.date); return t.amount < 0 && d >= start && d <= end; }).reduce((s, t) => s + Math.abs(t.amount), 0); return { ...m, income, spent }; }); const maxVal = Math.max(...monthlyData.map(m => Math.max(m.income, m.spent)), 1); return monthlyData.some(m => m.income > 0 || m.spent > 0) ? (
{monthlyData.map(m => (
0 ? '4px' : '0' }}> {m.income > 0 &&
${m.income.toFixed(0)}
}
0 ? '4px' : '0' }}> {m.spent > 0 &&
${m.spent.toFixed(0)}
}
{m.label}
))}
Income Expenses
) :

No data yet. Add transactions to see history.

; })()}
{/* Daily Spending Chart (Last 7 Days) */} {visibleCharts.dailySpending && (

Daily Spending (Last 7 Days)

{spendingData.length > 0 ? (
{spendingData.map(([date, amount]) => { const height = (amount / maxDailySpending) * 100; const dateObj = new Date(date); const dayName = dateObj.toLocaleDateString('en-US', { weekday: 'short' }); return (
${amount.toFixed(2)}
{dayName}
{dateObj.getDate()}
); })}
) : (

No spending data for the last 7 days.

)}
)}
); }