mirror of
https://github.com/elisiariocouto/leggen.git
synced 2025-12-13 11:22:21 +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/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
lib/
|
|
||||||
lib64/
|
lib64/
|
||||||
parts/
|
parts/
|
||||||
sdist/
|
sdist/
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import { apiClient } from '../lib/api';
|
import { apiClient } from '../lib/api';
|
||||||
import { formatCurrency, formatDate } from '../lib/utils';
|
import { formatCurrency, formatDate } from '../lib/utils';
|
||||||
import LoadingSpinner from './LoadingSpinner';
|
import LoadingSpinner from './LoadingSpinner';
|
||||||
|
import type { Account, Balance } from '../types/api';
|
||||||
|
|
||||||
export default function AccountsOverview() {
|
export default function AccountsOverview() {
|
||||||
const {
|
const {
|
||||||
@@ -17,14 +18,14 @@ export default function AccountsOverview() {
|
|||||||
isLoading: accountsLoading,
|
isLoading: accountsLoading,
|
||||||
error: accountsError,
|
error: accountsError,
|
||||||
refetch: refetchAccounts
|
refetch: refetchAccounts
|
||||||
} = useQuery({
|
} = useQuery<Account[]>({
|
||||||
queryKey: ['accounts'],
|
queryKey: ['accounts'],
|
||||||
queryFn: apiClient.getAccounts,
|
queryFn: apiClient.getAccounts,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: balances
|
data: balances
|
||||||
} = useQuery({
|
} = useQuery<Balance[]>({
|
||||||
queryKey: ['balances'],
|
queryKey: ['balances'],
|
||||||
queryFn: () => apiClient.getBalances(),
|
queryFn: () => apiClient.getBalances(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import AccountsOverview from './AccountsOverview';
|
|||||||
import TransactionsList from './TransactionsList';
|
import TransactionsList from './TransactionsList';
|
||||||
import ErrorBoundary from './ErrorBoundary';
|
import ErrorBoundary from './ErrorBoundary';
|
||||||
import { cn } from '../lib/utils';
|
import { cn } from '../lib/utils';
|
||||||
|
import type { Account } from '../types/api';
|
||||||
|
|
||||||
type TabType = 'overview' | 'transactions' | 'analytics';
|
type TabType = 'overview' | 'transactions' | 'analytics';
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ export default function Dashboard() {
|
|||||||
const [activeTab, setActiveTab] = useState<TabType>('overview');
|
const [activeTab, setActiveTab] = useState<TabType>('overview');
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
|
|
||||||
const { data: accounts } = useQuery({
|
const { data: accounts } = useQuery<Account[]>({
|
||||||
queryKey: ['accounts'],
|
queryKey: ['accounts'],
|
||||||
queryFn: apiClient.getAccounts,
|
queryFn: apiClient.getAccounts,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { apiClient } from '../lib/api';
|
import { apiClient } from '../lib/api';
|
||||||
import { formatCurrency, formatDate } from '../lib/utils';
|
import { formatCurrency, formatDate } from '../lib/utils';
|
||||||
import LoadingSpinner from './LoadingSpinner';
|
import LoadingSpinner from './LoadingSpinner';
|
||||||
|
import type { Account, Transaction } from '../types/api';
|
||||||
|
|
||||||
export default function TransactionsList() {
|
export default function TransactionsList() {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
@@ -23,7 +24,7 @@ export default function TransactionsList() {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
data: accounts
|
data: accounts
|
||||||
} = useQuery({
|
} = useQuery<Account[]>({
|
||||||
queryKey: ['accounts'],
|
queryKey: ['accounts'],
|
||||||
queryFn: apiClient.getAccounts,
|
queryFn: apiClient.getAccounts,
|
||||||
});
|
});
|
||||||
@@ -33,18 +34,18 @@ export default function TransactionsList() {
|
|||||||
isLoading: transactionsLoading,
|
isLoading: transactionsLoading,
|
||||||
error: transactionsError,
|
error: transactionsError,
|
||||||
refetch: refetchTransactions
|
refetch: refetchTransactions
|
||||||
} = useQuery({
|
} = useQuery<Transaction[]>({
|
||||||
queryKey: ['transactions', selectedAccount, startDate, endDate],
|
queryKey: ['transactions', selectedAccount, startDate, endDate],
|
||||||
queryFn: () => apiClient.getTransactions({
|
queryFn: () => apiClient.getTransactions({
|
||||||
account_id: selectedAccount || undefined,
|
accountId: selectedAccount || undefined,
|
||||||
start_date: startDate || undefined,
|
startDate: startDate || undefined,
|
||||||
end_date: endDate || undefined,
|
endDate: endDate || undefined,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredTransactions = (transactions || []).filter(transaction => {
|
const filteredTransactions = (transactions || []).filter(transaction => {
|
||||||
// Additional validation (API client should have already filtered out invalid ones)
|
// 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);
|
console.warn('Invalid transaction found after API filtering:', transaction);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -239,7 +240,7 @@ export default function TransactionsList() {
|
|||||||
const isPositive = transaction.amount > 0;
|
const isPositive = transaction.amount > 0;
|
||||||
|
|
||||||
return (
|
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 items-start justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
@@ -274,8 +275,8 @@ export default function TransactionsList() {
|
|||||||
<p>Ref: {transaction.reference}</p>
|
<p>Ref: {transaction.reference}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{transaction.internal_id && (
|
{transaction.internal_transaction_id && (
|
||||||
<p>ID: {transaction.internal_id}</p>
|
<p>ID: {transaction.internal_transaction_id}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -289,9 +290,9 @@ export default function TransactionsList() {
|
|||||||
{isPositive ? '+' : ''}{formatCurrency(transaction.amount, transaction.currency)}
|
{isPositive ? '+' : ''}{formatCurrency(transaction.amount, transaction.currency)}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
{transaction.transaction_date ? formatDate(transaction.transaction_date) : 'No date'}
|
{transaction.date ? formatDate(transaction.date) : 'No date'}
|
||||||
</p>
|
</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">
|
<p className="text-xs text-gray-400">
|
||||||
Booked: {formatDate(transaction.booking_date)}
|
Booked: {formatDate(transaction.booking_date)}
|
||||||
</p>
|
</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 {
|
export interface Transaction {
|
||||||
id: string;
|
internal_transaction_id: string | null;
|
||||||
internal_id?: string;
|
|
||||||
account_id: string;
|
account_id: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
description: string;
|
description: string;
|
||||||
transaction_date: string;
|
date: string;
|
||||||
|
status: string;
|
||||||
|
// Optional fields that may be present in some transactions
|
||||||
booking_date?: string;
|
booking_date?: string;
|
||||||
value_date?: string;
|
value_date?: string;
|
||||||
creditor_name?: string;
|
creditor_name?: string;
|
||||||
debtor_name?: string;
|
debtor_name?: string;
|
||||||
reference?: string;
|
reference?: string;
|
||||||
category?: string;
|
category?: string;
|
||||||
created_at: string;
|
created_at?: string;
|
||||||
updated_at: string;
|
updated_at?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type for raw transaction data from API (before sanitization)
|
// Type for raw transaction data from API (before sanitization)
|
||||||
@@ -68,7 +69,7 @@ export interface Bank {
|
|||||||
export interface ApiResponse<T> {
|
export interface ApiResponse<T> {
|
||||||
data: T;
|
data: T;
|
||||||
message?: string;
|
message?: string;
|
||||||
status: string;
|
success: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginatedResponse<T> {
|
export interface PaginatedResponse<T> {
|
||||||
|
|||||||
Reference in New Issue
Block a user