Fix: Update button remaining disabled in transaction edit mode

This commit resolves an issue where the Update button in the transaction form
would remain disabled when attempting to edit a transaction. The problem was
in how the transactionStore was managing state updates during transaction editing.

Key changes:
- Enhanced startEditingTransaction function in transactionStore.ts to ensure proper reactivity
- Added clean copy creation of transaction objects to avoid reference issues
- Implemented a state update cycle with null value first to force reactivity
- Added a small timeout to ensure state changes are properly detected by components

The Transaction form now correctly enables the Update button when in edit mode,
regardless of account selection state.
This commit is contained in:
GitHub Copilot
2025-05-05 21:29:36 +00:00
parent d3855aa7e4
commit 07fbb82385
27 changed files with 2961 additions and 952 deletions

View File

@@ -4,43 +4,179 @@ import type { Transaction } from '../types';
// Atom to hold the ID of the currently selected account
export const currentAccountId = atom<string | null>(null);
// Atom to hold the current transactions
export const currentTransactions = atom<Transaction[]>([]);
// Atom to hold the transaction object when editing, or null otherwise
export const transactionToEdit = atom<Transaction | null>(null);
// Atom to trigger refreshes in components that depend on external changes
export const refreshKey = atom<number>(0);
// Action to set the current transactions
export function setTransactions(transactions: Transaction[]) {
console.log('Setting transactions in store:', transactions.length, transactions);
currentTransactions.set(transactions);
}
// Action to increment the refresh key, forcing dependent effects to re-run
export function triggerRefresh() {
console.log('Triggering transaction refresh');
refreshKey.set(refreshKey.get() + 1);
}
// Action to set the transaction to be edited
export function startEditingTransaction(transaction: Transaction) {
transactionToEdit.set(transaction);
// Optionally, trigger UI changes like expanding the form here
// document.getElementById('add-transaction-section')?.classList.replace('collapsed', 'expanded');
// document.getElementById('toggle-add-txn-btn')?.setAttribute('aria-expanded', 'true');
console.log('Setting transaction to edit:', transaction);
// Create a clean copy of the transaction to avoid reference issues
const transactionCopy = { ...transaction };
// Force update to ensure subscribers get notified
transactionToEdit.set(null);
// Set after a small delay to ensure state change is detected
setTimeout(() => {
transactionToEdit.set(transactionCopy);
console.log('Transaction edit state updated:', transactionToEdit.get());
}, 0);
}
// Action to clear the edit state
export function cancelEditingTransaction() {
console.log('Canceling transaction edit');
transactionToEdit.set(null);
// Optionally, collapse the form
// document.getElementById('add-transaction-section')?.classList.replace('expanded', 'collapsed');
// document.getElementById('toggle-add-txn-btn')?.setAttribute('aria-expanded', 'false');
}
// Action triggered after a transaction is saved (created or updated)
export function transactionSaved(transaction: Transaction) {
console.log('Transaction saved:', transaction);
// Clear edit state if the saved transaction was the one being edited
if (transactionToEdit.get()?.id === transaction.id) {
transactionToEdit.set(null);
}
// Potentially trigger UI updates or refreshes here
// This might involve dispatching a custom event or calling a refresh function
document.dispatchEvent(new CustomEvent('transactionSaved', { detail: { transaction } }));
// Trigger a general refresh after saving too, to update balance
// Add/update the transaction in the current list
const currentList = currentTransactions.get();
const existingIndex = currentList.findIndex((t) => t.id === transaction.id);
if (existingIndex >= 0) {
// Update existing transaction
const updatedList = [...currentList];
updatedList[existingIndex] = transaction;
currentTransactions.set(updatedList);
} else {
// Add new transaction
currentTransactions.set([transaction, ...currentList]);
}
// Trigger a general refresh after saving
triggerRefresh();
}
// Helper function to load transactions for an account
export async function loadTransactionsForAccount(accountId: string) {
console.log('loadTransactionsForAccount called with ID:', accountId);
try {
if (!accountId) {
console.warn('No account ID provided, clearing transactions');
currentTransactions.set([]);
return [];
}
console.log(`Fetching transactions from API for account: ${accountId}`);
const response = await fetch(`/api/accounts/${accountId}/transactions`);
if (!response.ok) {
console.error('API error:', response.status, response.statusText);
const errorText = await response.text();
console.error('Error response:', errorText);
throw new Error(`Failed to fetch transactions: ${response.statusText}`);
}
const transactions: Transaction[] = await response.json();
console.log(
`Loaded ${transactions.length} transactions for account ${accountId}:`,
transactions,
);
// Set transactions in the store
currentTransactions.set(transactions);
return transactions;
} catch (error) {
console.error('Error loading transactions:', error);
// Don't clear transactions on error, to avoid flickering UI
throw error;
}
}
// Helper to create a new transaction
export async function createTransaction(transaction: Omit<Transaction, 'id'>) {
try {
console.log('Creating new transaction:', transaction);
const response = await fetch('/api/transactions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(transaction),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
throw new Error(errorData.error || `Failed to create transaction: ${response.statusText}`);
}
const newTransaction = await response.json();
console.log('Transaction created successfully:', newTransaction);
// Add the new transaction to the existing list
const currentList = currentTransactions.get();
currentTransactions.set([newTransaction, ...currentList]);
// Trigger refresh to update other components
triggerRefresh();
return newTransaction;
} catch (error) {
console.error('Error creating transaction:', error);
throw error;
}
}
// Helper to update an existing transaction
export async function updateTransaction(id: string, transaction: Partial<Transaction>) {
try {
console.log(`Updating transaction ${id}:`, transaction);
const response = await fetch(`/api/transactions/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(transaction),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
throw new Error(errorData.error || `Failed to update transaction: ${response.statusText}`);
}
const updatedTransaction = await response.json();
console.log('Transaction updated successfully:', updatedTransaction);
// Update the transaction in the existing list
const currentList = currentTransactions.get();
const updatedList = currentList.map((t) =>
t.id === updatedTransaction.id ? updatedTransaction : t,
);
currentTransactions.set(updatedList);
// Trigger refresh to update other components
triggerRefresh();
return updatedTransaction;
} catch (error) {
console.error('Error updating transaction:', error);
throw error;
}
}