From 16ab65d4ecb6f7d1549c40f392cdabc1407a7c26 Mon Sep 17 00:00:00 2001 From: nmemmert <67961292+nmemmert@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:55:41 -0400 Subject: [PATCH] Add Available to Budget envelope view and fix transaction/account delete bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Envelopes view: "Available to Budget" card shows checking+savings balance minus total envelope allocations — turns red if over-allocated - Income transactions in ManualEntry: envelope picker labeled clearly, green highlight, helper text explaining effect on Available to Budget, default option "Leave in Available to Budget" - Fix account delete: now removes the account's transactions and recalculates envelope spending - Fix transaction edit/delete: use functional state updater to avoid stale closure bug on accounts/envelopes recalculation Co-Authored-By: Claude Sonnet 4.6 --- src/app/page.tsx | 42 +++++++++++++++++++++++++++++----- src/components/ManualEntry.tsx | 13 +++++++---- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 7d3178c..9b2e792 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -422,8 +422,8 @@ export default function BudgetDashboard() { const handleTransactionEdit = (updated: Transaction) => { setTransactions(prev => { const next = prev.map(t => t.id === updated.id ? updated : t); - setEnvelopes(calculateEnvelopeSpending(envelopes, next)); - setAccounts(calculateAccountBalances(accounts, next)); + setEnvelopes(e => calculateEnvelopeSpending(e, next)); + setAccounts(a => calculateAccountBalances(a, next)); return next; }); }; @@ -431,8 +431,8 @@ export default function BudgetDashboard() { const handleTransactionDelete = (id: string) => { setTransactions(prev => { const next = prev.filter(t => t.id !== id); - setEnvelopes(calculateEnvelopeSpending(envelopes, next)); - setAccounts(calculateAccountBalances(accounts, next)); + setEnvelopes(e => calculateEnvelopeSpending(e, next)); + setAccounts(a => calculateAccountBalances(a, next)); return next; }); }; @@ -928,9 +928,32 @@ export default function BudgetDashboard() { const incomeAlloc = transactions.filter(t => t.envelopeId === e.id && t.amount > 0).reduce((s, t) => s + t.amount, 0); return e.allocated + incomeAlloc > 0 && e.spent > e.allocated + incomeAlloc; }); + + // Available to budget = liquid assets (checking + savings) minus total envelope allocations + const liquidBalance = accounts + .filter(a => a.type === 'checking' || a.type === 'savings') + .reduce((s, a) => s + a.balance, 0); + const totalAllocated = envelopes.reduce((s, e) => s + e.allocated, 0); + const availableToBudget = liquidBalance - totalAllocated; + return (
-

Envelopes

+

Envelopes

+ + {/* Available to Budget */} +
= 0 ? 'bg-green-50 border-green-300' : 'bg-red-50 border-red-300'}`}> +
+

Available to Budget

+

= 0 ? 'text-green-700' : 'text-red-700'}`}> + ${Math.abs(availableToBudget).toFixed(2)}{availableToBudget < 0 ? ' over' : ''} +

+

+ ${liquidBalance.toFixed(2)} in checking & savings − ${totalAllocated.toFixed(2)} allocated to envelopes +

+
+
{availableToBudget >= 0 ? '💰' : '⚠️'}
+
+ {overspentEnvs.length > 0 && (
⚠️ @@ -1117,7 +1140,14 @@ export default function BudgetDashboard() { accounts={accounts} onAccountAdd={handleAccountAdd} onAccountUpdate={handleAccountUpdate} - onAccountDelete={id => setAccounts(prev => prev.filter(a => a.id !== id))} + onAccountDelete={id => { + setTransactions(prev => { + const next = prev.filter(t => t.accountId !== id); + setEnvelopes(e => calculateEnvelopeSpending(e, next)); + return next; + }); + setAccounts(prev => prev.filter(a => a.id !== id)); + }} /> {sharedAccounts.length > 0 && (
diff --git a/src/components/ManualEntry.tsx b/src/components/ManualEntry.tsx index 1715cec..d371088 100644 --- a/src/components/ManualEntry.tsx +++ b/src/components/ManualEntry.tsx @@ -177,22 +177,25 @@ export default function ManualEntry({ onTransactionAdded, envelopes, accounts, t
+ {!isExpense && ( +

+ Assigning income to an envelope increases its available balance and reduces "Available to Budget". +

+ )}