diff --git a/frontend/src/components/AccountsOverview.tsx b/frontend/src/components/AccountsOverview.tsx index 50a5950..719ed52 100644 --- a/frontend/src/components/AccountsOverview.tsx +++ b/frontend/src/components/AccountsOverview.tsx @@ -61,9 +61,13 @@ export default function AccountsOverview() { ); } - const totalBalance = accounts?.reduce((sum, account) => sum + (account.balance || 0), 0) || 0; + const totalBalance = accounts?.reduce((sum, account) => { + // Get the first available balance from the balances array + const primaryBalance = account.balances?.[0]?.amount || 0; + return sum + primaryBalance; + }, 0) || 0; const totalAccounts = accounts?.length || 0; - const uniqueBanks = new Set(accounts?.map(acc => acc.bank_name) || []).size; + const uniqueBanks = new Set(accounts?.map(acc => acc.institution_id) || []).size; return (
@@ -125,54 +129,57 @@ export default function AccountsOverview() {
) : (
- {accounts.map((account) => { - const accountBalance = balances?.find(b => b.account_id === account.id); - const balance = account.balance || accountBalance?.balance_amount || 0; - const isPositive = balance >= 0; + {accounts.map((account) => { + // Get balance from account's balances array or fallback to balances query + const accountBalance = account.balances?.[0]; + const fallbackBalance = balances?.find(b => b.account_id === account.id); + const balance = accountBalance?.amount || fallbackBalance?.balance_amount || 0; + const currency = accountBalance?.currency || fallbackBalance?.currency || account.currency || 'EUR'; + const isPositive = balance >= 0; - return ( -
-
-
-
- -
-
-

- {account.name} -

-

- {account.bank_name} • {account.account_type} -

- {account.iban && ( -

- IBAN: {account.iban} -

- )} -
-
+ return ( +
+
+
+
+ +
+
+

+ {account.name || 'Unnamed Account'} +

+

+ {account.institution_id} • {account.status} +

+ {account.iban && ( +

+ IBAN: {account.iban} +

+ )} +
+
-
-
- {isPositive ? ( - - ) : ( - - )} -

- {formatCurrency(balance, account.currency)} -

-
-

- Updated {formatDate(account.updated_at)} -

-
-
-
- ); - })} +
+
+ {isPositive ? ( + + ) : ( + + )} +

+ {formatCurrency(balance, currency)} +

+
+

+ Updated {formatDate(account.last_accessed || account.created)} +

+
+
+
+ ); + })}
)} diff --git a/frontend/src/components/Dashboard.tsx b/frontend/src/components/Dashboard.tsx index 6f77403..ee2730e 100644 --- a/frontend/src/components/Dashboard.tsx +++ b/frontend/src/components/Dashboard.tsx @@ -34,7 +34,11 @@ export default function Dashboard() { { name: 'Analytics', icon: BarChart3, id: 'analytics' as TabType }, ]; - const totalBalance = accounts?.reduce((sum, account) => sum + (account.balance || 0), 0) || 0; + const totalBalance = accounts?.reduce((sum, account) => { + // Get the first available balance from the balances array + const primaryBalance = account.balances?.[0]?.amount || 0; + return sum + primaryBalance; + }, 0) || 0; return (
diff --git a/frontend/src/components/TransactionsList.tsx b/frontend/src/components/TransactionsList.tsx index ef5048e..e717259 100644 --- a/frontend/src/components/TransactionsList.tsx +++ b/frontend/src/components/TransactionsList.tsx @@ -163,11 +163,11 @@ export default function TransactionsList() { className="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" > - {accounts?.map((account) => ( - - ))} + {accounts?.map((account) => ( + + ))}
@@ -259,10 +259,10 @@ export default function TransactionsList() { {transaction.description} -
- {account && ( -

{account.name} • {account.bank_name}

- )} +
+ {account && ( +

{account.name || 'Unnamed Account'} • {account.institution_id}

+ )} {(transaction.creditor_name || transaction.debtor_name) && (

diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 5d47532..b9d7638 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -5,10 +5,22 @@ export function cn(...inputs: ClassValue[]) { } export function formatCurrency(amount: number, currency: string = 'EUR'): string { - return new Intl.NumberFormat('en-US', { - style: 'currency', - currency: currency, - }).format(amount); + // Validate currency code - must be 3 letters and a valid ISO 4217 code + const validCurrency = currency && /^[A-Z]{3}$/.test(currency) ? currency : 'EUR'; + + try { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: validCurrency, + }).format(amount); + } catch (error) { + // Fallback if currency is still invalid + console.warn(`Invalid currency code: ${currency}, falling back to EUR`); + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'EUR', + }).format(amount); + } } export function formatDate(date: string): string { diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index 5be0656..3791d58 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -1,13 +1,20 @@ +export interface AccountBalance { + amount: number; + currency: string; + balance_type: string; + last_change_date?: string; +} + export interface Account { id: string; - name: string; - bank_name: string; - account_type: string; - currency: string; - balance?: number; + institution_id: string; + status: string; iban?: string; - created_at: string; - updated_at: string; + name?: string; + currency?: string; + created: string; + last_accessed?: string; + balances: AccountBalance[]; } export interface Transaction { diff --git a/leggend/api/routes/accounts.py b/leggend/api/routes/accounts.py index 593ef63..e6f4362 100644 --- a/leggend/api/routes/accounts.py +++ b/leggend/api/routes/accounts.py @@ -164,6 +164,56 @@ async def get_account_balances(account_id: str) -> APIResponse: ) from e +@router.get("/balances", response_model=APIResponse) +async def get_all_balances() -> APIResponse: + """Get all balances from all accounts in database""" + try: + # Get all accounts first to iterate through them + db_accounts = await database_service.get_accounts_from_db() + + all_balances = [] + for db_account in db_accounts: + try: + # Get balances for this account + db_balances = await database_service.get_balances_from_db( + account_id=db_account["id"] + ) + + # Process balances and add account info + for balance in db_balances: + balance_data = { + "id": f"{db_account['id']}_{balance['type']}", # Create unique ID + "account_id": db_account["id"], + "balance_amount": balance["amount"], + "balance_type": balance["type"], + "currency": balance["currency"], + "reference_date": balance.get( + "timestamp", db_account.get("last_accessed") + ), + "created_at": db_account.get("created"), + "updated_at": db_account.get("last_accessed"), + } + all_balances.append(balance_data) + + except Exception as e: + logger.error( + f"Failed to get balances for account {db_account['id']}: {e}" + ) + continue + + return APIResponse( + success=True, + data=all_balances, + message=f"Retrieved {len(all_balances)} balances from {len(db_accounts)} accounts", + ) + + except Exception as e: + logger.error(f"Failed to get all balances: {e}") + raise HTTPException( + status_code=500, detail=f"Failed to get balances: {str(e)}" + ) from e + + @router.get("/accounts/{account_id}/transactions", response_model=APIResponse) async def get_account_transactions( account_id: str,