mirror of
https://github.com/elisiariocouto/leggen.git
synced 2025-12-11 17:22:18 +00:00
Fix api in lib folder.
This commit is contained in:
committed by
Elisiário Couto
parent
26487cff89
commit
abacfd78c8
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,7 +14,6 @@ dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
||||
@@ -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<Account[]>({
|
||||
queryKey: ['accounts'],
|
||||
queryFn: apiClient.getAccounts,
|
||||
});
|
||||
|
||||
const {
|
||||
data: balances
|
||||
} = useQuery({
|
||||
} = useQuery<Balance[]>({
|
||||
queryKey: ['balances'],
|
||||
queryFn: () => apiClient.getBalances(),
|
||||
});
|
||||
|
||||
@@ -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<TabType>('overview');
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
|
||||
const { data: accounts } = useQuery({
|
||||
const { data: accounts } = useQuery<Account[]>({
|
||||
queryKey: ['accounts'],
|
||||
queryFn: apiClient.getAccounts,
|
||||
});
|
||||
|
||||
@@ -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<Account[]>({
|
||||
queryKey: ['accounts'],
|
||||
queryFn: apiClient.getAccounts,
|
||||
});
|
||||
@@ -33,18 +34,18 @@ export default function TransactionsList() {
|
||||
isLoading: transactionsLoading,
|
||||
error: transactionsError,
|
||||
refetch: refetchTransactions
|
||||
} = useQuery({
|
||||
} = useQuery<Transaction[]>({
|
||||
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 (
|
||||
<div key={transaction.id} className="p-6 hover:bg-gray-50 transition-colors">
|
||||
<div key={transaction.internal_transaction_id || `${transaction.account_id}-${transaction.date}-${transaction.amount}`} className="p-6 hover:bg-gray-50 transition-colors">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-start space-x-3">
|
||||
@@ -274,8 +275,8 @@ export default function TransactionsList() {
|
||||
<p>Ref: {transaction.reference}</p>
|
||||
)}
|
||||
|
||||
{transaction.internal_id && (
|
||||
<p>ID: {transaction.internal_id}</p>
|
||||
{transaction.internal_transaction_id && (
|
||||
<p>ID: {transaction.internal_transaction_id}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -289,9 +290,9 @@ export default function TransactionsList() {
|
||||
{isPositive ? '+' : ''}{formatCurrency(transaction.amount, transaction.currency)}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
{transaction.transaction_date ? formatDate(transaction.transaction_date) : 'No date'}
|
||||
{transaction.date ? formatDate(transaction.date) : 'No date'}
|
||||
</p>
|
||||
{transaction.booking_date && transaction.booking_date !== transaction.transaction_date && (
|
||||
{transaction.booking_date && transaction.booking_date !== transaction.date && (
|
||||
<p className="text-xs text-gray-400">
|
||||
Booked: {formatDate(transaction.booking_date)}
|
||||
</p>
|
||||
|
||||
67
frontend/src/lib/api.ts
Normal file
67
frontend/src/lib/api.ts
Normal file
@@ -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<Account[]> => {
|
||||
const response = await api.get<ApiResponse<Account[]>>('/accounts');
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
// Get account by ID
|
||||
getAccount: async (id: string): Promise<Account> => {
|
||||
const response = await api.get<ApiResponse<Account>>(`/accounts/${id}`);
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
// Get all balances
|
||||
getBalances: async (): Promise<Balance[]> => {
|
||||
const response = await api.get<ApiResponse<Balance[]>>('/balances');
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
// Get balances for specific account
|
||||
getAccountBalances: async (accountId: string): Promise<Balance[]> => {
|
||||
const response = await api.get<ApiResponse<Balance[]>>(`/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<Transaction[]> => {
|
||||
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<ApiResponse<Transaction[]>>(`/transactions?${queryParams.toString()}`);
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
// Get transaction by ID
|
||||
getTransaction: async (id: string): Promise<Transaction> => {
|
||||
const response = await api.get<ApiResponse<Transaction>>(`/transactions/${id}`);
|
||||
return response.data.data;
|
||||
},
|
||||
};
|
||||
|
||||
export default apiClient;
|
||||
46
frontend/src/lib/utils.ts
Normal file
46
frontend/src/lib/utils.ts
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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<T> {
|
||||
data: T;
|
||||
message?: string;
|
||||
status: string;
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
|
||||
Reference in New Issue
Block a user