import { useState, useEffect } from 'react'; import { DollarSign, TrendingUp, TrendingDown, PieChart, Plus, ArrowUpRight, ArrowDownRight, Loader2 } from 'lucide-react'; import api from '../lib/api'; import { PageHeader, MetricCard, EmptyState, CardSkeleton } from '../components/ui/LinearPrimitives'; interface Transaction { id: string; type: 'EXPENSE' | 'REVENUE' | 'ADJUSTMENT'; category?: string; amount: number; description: string; date: string; } interface ProfitLossData { periods: { period: string; revenue: number; expenses: number; net: number }[]; totals: { revenue: number; expenses: number; net: number }; } interface CategoryBreakdown { breakdown: { category: string; amount: number; percentage: number }[]; total: number; } export default function FinancialDashboard() { const [transactions, setTransactions] = useState([]); const [totals, setTotals] = useState>({}); const [profitLoss, setProfitLoss] = useState(null); const [categories, setCategories] = useState(null); const [loading, setLoading] = useState(true); const [dateRange, setDateRange] = useState({ start: '', end: '' }); const [showAddModal, setShowAddModal] = useState(false); useEffect(() => { loadData(); }, [dateRange]); const loadData = async () => { try { const params = { startDate: dateRange.start || undefined, endDate: dateRange.end || undefined }; const [txRes, plRes, catRes] = await Promise.all([ api.get('/api/financial/transactions', { params: { ...params, limit: 20 } }), api.get('/api/financial/reports/profit-loss', { params }), api.get('/api/financial/reports/category-breakdown', { params }) ]); setTransactions(txRes.data.transactions); setTotals(txRes.data.totals); setProfitLoss(plRes.data); setCategories(catRes.data); } catch (error) { console.error('Failed to load financial data:', error); } finally { setLoading(false); } }; const formatCurrency = (amount: number): string => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount); }; if (loading) { return (
{Array.from({ length: 3 }).map((_, i) => )}
); } const netAmount = (totals.REVENUE || 0) - (totals.EXPENSE || 0); return (
setShowAddModal(true)} className="btn btn-primary" > Add Transaction } /> {/* Summary Cards */}
= 0 ? 'border-success/30 bg-success-muted' : 'border-destructive/30 bg-destructive-muted'} `}>
Net Profit/Loss {netAmount >= 0 ? ( ) : ( )}
= 0 ? 'text-success' : 'text-destructive'}`}> {formatCurrency(netAmount)}
{/* Charts Grid */}
{/* Expense Breakdown */}

Expense Breakdown

{categories?.breakdown.map(cat => (
{cat.category.toLowerCase()} {formatCurrency(cat.amount)}
{cat.percentage.toFixed(1)}% of total
))} {(!categories || categories.breakdown.length === 0) && (

No expense data

)}
{/* Monthly Trend */}

Monthly Trend

{profitLoss?.periods.slice(-6).map(period => (
{period.period}
= 0 ? 'text-success' : 'text-destructive'}`}> {formatCurrency(period.net)}
))} {(!profitLoss || profitLoss.periods.length === 0) && (

No trend data

)}
Revenue
Expenses
{/* Recent Transactions */}

Recent Transactions

{transactions.map(tx => (
{tx.description}
{tx.category && {tx.category.toLowerCase()}} ยท {new Date(tx.date).toLocaleDateString()}
{tx.type === 'EXPENSE' ? '-' : '+'}{formatCurrency(tx.amount)}
))} {transactions.length === 0 && ( )}
); }