From abacfd78c8dd8da97c3714e278a334752b6e5966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elisi=C3=A1rio=20Couto?= Date: Mon, 8 Sep 2025 19:08:30 +0100 Subject: [PATCH] Fix api in lib folder. --- .gitignore | 1 - frontend/src/components/AccountsOverview.tsx | 5 +- frontend/src/components/Dashboard.tsx | 3 +- frontend/src/components/TransactionsList.tsx | 23 +++---- frontend/src/lib/api.ts | 67 ++++++++++++++++++++ frontend/src/lib/utils.ts | 46 ++++++++++++++ frontend/src/types/api.ts | 13 ++-- 7 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 frontend/src/lib/api.ts create mode 100644 frontend/src/lib/utils.ts diff --git a/.gitignore b/.gitignore index c9a966a..ae59837 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ diff --git a/frontend/src/components/AccountsOverview.tsx b/frontend/src/components/AccountsOverview.tsx index 78caff8..50a5950 100644 --- a/frontend/src/components/AccountsOverview.tsx +++ b/frontend/src/components/AccountsOverview.tsx @@ -10,6 +10,7 @@ import { import { apiClient } from '../lib/api'; import { formatCurrency, formatDate } from '../lib/utils'; import LoadingSpinner from './LoadingSpinner'; +import type { Account, Balance } from '../types/api'; export default function AccountsOverview() { const { @@ -17,14 +18,14 @@ export default function AccountsOverview() { isLoading: accountsLoading, error: accountsError, refetch: refetchAccounts - } = useQuery({ + } = useQuery({ queryKey: ['accounts'], queryFn: apiClient.getAccounts, }); const { data: balances - } = useQuery({ + } = useQuery({ queryKey: ['balances'], queryFn: () => apiClient.getBalances(), }); diff --git a/frontend/src/components/Dashboard.tsx b/frontend/src/components/Dashboard.tsx index 461e642..6f77403 100644 --- a/frontend/src/components/Dashboard.tsx +++ b/frontend/src/components/Dashboard.tsx @@ -15,6 +15,7 @@ import AccountsOverview from './AccountsOverview'; import TransactionsList from './TransactionsList'; import ErrorBoundary from './ErrorBoundary'; import { cn } from '../lib/utils'; +import type { Account } from '../types/api'; type TabType = 'overview' | 'transactions' | 'analytics'; @@ -22,7 +23,7 @@ export default function Dashboard() { const [activeTab, setActiveTab] = useState('overview'); const [sidebarOpen, setSidebarOpen] = useState(false); - const { data: accounts } = useQuery({ + const { data: accounts } = useQuery({ queryKey: ['accounts'], queryFn: apiClient.getAccounts, }); diff --git a/frontend/src/components/TransactionsList.tsx b/frontend/src/components/TransactionsList.tsx index f5b3a4d..ef5048e 100644 --- a/frontend/src/components/TransactionsList.tsx +++ b/frontend/src/components/TransactionsList.tsx @@ -13,6 +13,7 @@ import { import { apiClient } from '../lib/api'; import { formatCurrency, formatDate } from '../lib/utils'; import LoadingSpinner from './LoadingSpinner'; +import type { Account, Transaction } from '../types/api'; export default function TransactionsList() { const [searchTerm, setSearchTerm] = useState(''); @@ -23,7 +24,7 @@ export default function TransactionsList() { const { data: accounts - } = useQuery({ + } = useQuery({ queryKey: ['accounts'], queryFn: apiClient.getAccounts, }); @@ -33,18 +34,18 @@ export default function TransactionsList() { isLoading: transactionsLoading, error: transactionsError, refetch: refetchTransactions - } = useQuery({ + } = useQuery({ queryKey: ['transactions', selectedAccount, startDate, endDate], queryFn: () => apiClient.getTransactions({ - account_id: selectedAccount || undefined, - start_date: startDate || undefined, - end_date: endDate || undefined, + accountId: selectedAccount || undefined, + startDate: startDate || undefined, + endDate: endDate || undefined, }), }); const filteredTransactions = (transactions || []).filter(transaction => { // Additional validation (API client should have already filtered out invalid ones) - if (!transaction || !transaction.id) { + if (!transaction || !transaction.account_id) { console.warn('Invalid transaction found after API filtering:', transaction); return false; } @@ -239,7 +240,7 @@ export default function TransactionsList() { const isPositive = transaction.amount > 0; return ( -
+
@@ -274,8 +275,8 @@ export default function TransactionsList() {

Ref: {transaction.reference}

)} - {transaction.internal_id && ( -

ID: {transaction.internal_id}

+ {transaction.internal_transaction_id && ( +

ID: {transaction.internal_transaction_id}

)}
@@ -289,9 +290,9 @@ export default function TransactionsList() { {isPositive ? '+' : ''}{formatCurrency(transaction.amount, transaction.currency)}

- {transaction.transaction_date ? formatDate(transaction.transaction_date) : 'No date'} + {transaction.date ? formatDate(transaction.date) : 'No date'}

- {transaction.booking_date && transaction.booking_date !== transaction.transaction_date && ( + {transaction.booking_date && transaction.booking_date !== transaction.date && (

Booked: {formatDate(transaction.booking_date)}

diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts new file mode 100644 index 0000000..c19ad5f --- /dev/null +++ b/frontend/src/lib/api.ts @@ -0,0 +1,67 @@ +import axios from 'axios'; +import type { Account, Transaction, Balance, ApiResponse } from '../types/api'; + +const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000/api/v1'; + +const api = axios.create({ + baseURL: API_BASE_URL, + headers: { + 'Content-Type': 'application/json', + }, +}); + +export const apiClient = { + // Get all accounts + getAccounts: async (): Promise => { + const response = await api.get>('/accounts'); + return response.data.data; + }, + + // Get account by ID + getAccount: async (id: string): Promise => { + const response = await api.get>(`/accounts/${id}`); + return response.data.data; + }, + + // Get all balances + getBalances: async (): Promise => { + const response = await api.get>('/balances'); + return response.data.data; + }, + + // Get balances for specific account + getAccountBalances: async (accountId: string): Promise => { + const response = await api.get>(`/accounts/${accountId}/balances`); + return response.data.data; + }, + + // Get transactions with optional filters + getTransactions: async (params?: { + accountId?: string; + startDate?: string; + endDate?: string; + page?: number; + perPage?: number; + search?: string; + }): Promise => { + const queryParams = new URLSearchParams(); + + if (params?.accountId) queryParams.append('account_id', params.accountId); + if (params?.startDate) queryParams.append('start_date', params.startDate); + if (params?.endDate) queryParams.append('end_date', params.endDate); + if (params?.page) queryParams.append('page', params.page.toString()); + if (params?.perPage) queryParams.append('per_page', params.perPage.toString()); + if (params?.search) queryParams.append('search', params.search); + + const response = await api.get>(`/transactions?${queryParams.toString()}`); + return response.data.data; + }, + + // Get transaction by ID + getTransaction: async (id: string): Promise => { + const response = await api.get>(`/transactions/${id}`); + return response.data.data; + }, +}; + +export default apiClient; diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts new file mode 100644 index 0000000..5d47532 --- /dev/null +++ b/frontend/src/lib/utils.ts @@ -0,0 +1,46 @@ +import { clsx, type ClassValue } from 'clsx'; + +export function cn(...inputs: ClassValue[]) { + return clsx(inputs); +} + +export function formatCurrency(amount: number, currency: string = 'EUR'): string { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: currency, + }).format(amount); +} + +export function formatDate(date: string): string { + if (!date) return 'No date'; + + const parsedDate = new Date(date); + if (isNaN(parsedDate.getTime())) { + console.warn('Invalid date string:', date); + return 'Invalid date'; + } + + return new Intl.DateTimeFormat('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + }).format(parsedDate); +} + +export function formatDateTime(date: string): string { + if (!date) return 'No date'; + + const parsedDate = new Date(date); + if (isNaN(parsedDate.getTime())) { + console.warn('Invalid date string:', date); + return 'Invalid date'; + } + + return new Intl.DateTimeFormat('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + }).format(parsedDate); +} diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index 0ea1d2b..5be0656 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -11,21 +11,22 @@ export interface Account { } export interface Transaction { - id: string; - internal_id?: string; + internal_transaction_id: string | null; account_id: string; amount: number; currency: string; description: string; - transaction_date: string; + date: string; + status: string; + // Optional fields that may be present in some transactions booking_date?: string; value_date?: string; creditor_name?: string; debtor_name?: string; reference?: string; category?: string; - created_at: string; - updated_at: string; + created_at?: string; + updated_at?: string; } // Type for raw transaction data from API (before sanitization) @@ -68,7 +69,7 @@ export interface Bank { export interface ApiResponse { data: T; message?: string; - status: string; + success: boolean; } export interface PaginatedResponse {