import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { financialHealthService } from '../services/api'; import useFormatters from '../hooks/useFormatters'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, BarElement, ArcElement, Title, Tooltip, Legend, Filler, } from 'chart.js'; import { Line, Doughnut, Bar } from 'react-chartjs-2'; ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, BarElement, ArcElement, Title, Tooltip, Legend, Filler ); const FinancialHealth = () => { const { t } = useTranslation(); const { currency: formatCurrency, percent, number } = useFormatters(); const [loading, setLoading] = useState(true); const [data, setData] = useState(null); const [history, setHistory] = useState([]); const [activeTab, setActiveTab] = useState('overview'); // Helper para formatear moneda usando la moneda del API const currency = (value, currencyCode = null) => { const code = currencyCode || data?.currency || 'EUR'; return formatCurrency(value, code); }; useEffect(() => { loadData(); }, []); const loadData = async () => { setLoading(true); try { const [healthData, historyData] = await Promise.all([ financialHealthService.get(), financialHealthService.getHistory({ months: 6 }) ]); setData(healthData); // Asegurar que history sea siempre un array setHistory(Array.isArray(historyData) ? historyData : []); } catch (error) { console.error('Error loading financial health:', error); } finally { setLoading(false); } }; const getScoreColor = (score) => { if (score >= 80) return '#10b981'; if (score >= 60) return '#84cc16'; if (score >= 40) return '#f59e0b'; if (score >= 20) return '#f97316'; return '#ef4444'; }; const getStatusColor = (status) => { const colors = { excellent: '#10b981', good: '#84cc16', adequate: '#22c55e', moderate: '#f59e0b', needs_improvement: '#f97316', needs_attention: '#f97316', needs_work: '#f97316', negative: '#ef4444', critical: '#ef4444', insufficient: '#ef4444', debt_free: '#10b981', healthy: '#22c55e', manageable: '#f59e0b', concerning: '#ef4444', on_track: '#10b981', exceeded: '#ef4444', not_configured: '#6b7280', very_stable: '#10b981', stable: '#22c55e', volatile: '#ef4444', optimized: '#10b981', acceptable: '#f59e0b', high_discretionary: '#f97316', }; return colors[status] || '#6b7280'; }; const metricConfigs = { savings_capacity: { icon: 'bi-piggy-bank-fill', gradient: ['#059669', '#10b981'], }, debt_control: { icon: 'bi-credit-card-2-front-fill', gradient: ['#2563eb', '#3b82f6'], }, budget_management: { icon: 'bi-wallet2', gradient: ['#7c3aed', '#8b5cf6'], }, expense_efficiency: { icon: 'bi-pie-chart-fill', gradient: ['#0891b2', '#06b6d4'], }, emergency_fund: { icon: 'bi-shield-fill-check', gradient: ['#d97706', '#f59e0b'], }, financial_stability: { icon: 'bi-graph-up-arrow', gradient: ['#db2777', '#ec4899'], }, }; const tabs = [ { id: 'overview', icon: 'bi-speedometer2', label: t('financialHealth.tabs.overview') }, { id: 'metrics', icon: 'bi-bar-chart-fill', label: t('financialHealth.tabs.metrics') }, { id: 'categories', icon: 'bi-pie-chart-fill', label: t('financialHealth.tabs.categories') }, { id: 'trends', icon: 'bi-graph-up', label: t('financialHealth.tabs.trends') }, { id: 'insights', icon: 'bi-lightbulb-fill', label: t('financialHealth.tabs.insights') }, ]; if (loading) { return (
{t('common.loading')}
); } if (!data) { return (
{t('financialHealth.errorLoading')}
); } const score = data.overall_score; const scoreColor = getScoreColor(score); // Asegurar arrays válidos para Chart.js const safeHistory = Array.isArray(history) ? history : []; const safeMonthlyData = Array.isArray(data.trends?.monthly_data) ? data.trends.monthly_data : []; const safeTopExpenses = Array.isArray(data.category_analysis?.top_expenses) ? data.category_analysis.top_expenses : []; const safeCategoryTrends = Array.isArray(data.category_analysis?.category_trends) ? data.category_analysis.category_trends : []; // Chart data for history const historyChartData = safeHistory.length > 0 ? { labels: safeHistory.map(h => h.month_label), datasets: [{ label: t('financialHealth.score'), data: safeHistory.map(h => h.score), borderColor: scoreColor, backgroundColor: `${scoreColor}20`, fill: true, tension: 0.4, pointRadius: 4, pointBackgroundColor: scoreColor, }], } : null; // Chart data for expense distribution const distributionChartData = data.category_analysis?.expense_distribution ? { labels: [ t('financialHealth.distribution.fixed'), t('financialHealth.distribution.variable'), t('financialHealth.distribution.discretionary'), ], datasets: [{ data: [ data.category_analysis.expense_distribution.fixed.percentage, data.category_analysis.expense_distribution.variable.percentage, data.category_analysis.expense_distribution.discretionary.percentage, ], backgroundColor: ['#3b82f6', '#22c55e', '#f59e0b'], borderWidth: 0, }], } : null; // Chart data for monthly comparison const monthlyChartData = safeMonthlyData.length > 0 ? { labels: safeMonthlyData.map(m => m.month), datasets: [ { label: t('financialHealth.income'), data: safeMonthlyData.map(m => m.income), backgroundColor: '#22c55e', borderRadius: 4, }, { label: t('financialHealth.expenses'), data: safeMonthlyData.map(m => m.expenses), backgroundColor: '#ef4444', borderRadius: 4, }, ], } : null; // Category expenses chart const categoryChartData = safeTopExpenses.length > 0 ? { labels: safeTopExpenses.slice(0, 8).map(c => c.name), datasets: [{ data: safeTopExpenses.slice(0, 8).map(c => c.total), backgroundColor: safeTopExpenses.slice(0, 8).map(c => c.color || '#6b7280'), borderWidth: 0, }], } : null; return (
{/* Header */}

{t('financialHealth.title')}

{t('financialHealth.subtitle')} • {t('financialHealth.lastUpdate')}: {new Date(data.last_updated).toLocaleDateString()}

{data.currency}
{/* Tabs */} {/* Overview Tab */} {activeTab === 'overview' && (
{/* Score Circle */}
{/* Score Ring */}
{score} {t('financialHealth.outOf100')}
{t(`financialHealth.levels.${data.health_level?.level}`)}

{t('financialHealth.scoreDescription')}

{/* Mini History Chart */} {historyChartData && (
)}
{/* Summary Cards */}
{/* Net Worth */}
{t('financialHealth.summary.netWorth')}

= 0 ? 'text-success' : 'text-danger'}`}> {currency(data.summary?.net_worth || 0)}

{t('financialHealth.summary.assets')}: {currency(data.summary?.total_assets || 0)} {t('financialHealth.summary.liabilities')}: {currency(data.summary?.total_liabilities || 0)}
{/* Monthly Savings */}
{t('financialHealth.summary.monthlySavings')}

= 0 ? 'text-success' : 'text-danger'}`}> {currency(data.summary?.monthly_savings || 0)}

{t('financialHealth.summary.savingsRate')}: = 20 ? 'text-success' : 'text-warning'}> {' '}{data.summary?.savings_rate || 0}%
{/* Monthly Income */}
{t('financialHealth.summary.monthlyIncome')}
{currency(data.summary?.monthly_income || 0)}
{data.trends?.monthly_comparison?.income?.change !== 0 && ( 0 ? 'text-success' : 'text-danger'}> 0 ? 'up' : 'down'} me-1`}> {Math.abs(data.trends.monthly_comparison.income.change)}% {t('financialHealth.vsLastMonth')} )}
{/* Monthly Expenses */}
{t('financialHealth.summary.monthlyExpenses')}
{currency(data.summary?.monthly_expenses || 0)}
{data.trends?.monthly_comparison?.expenses?.change !== 0 && ( 0 ? 'up' : 'down'} me-1`}> {Math.abs(data.trends.monthly_comparison.expenses.change)}% {t('financialHealth.vsLastMonth')} )}
{/* Projection */}
{t('financialHealth.summary.projectedSavings')}
= 0 ? 'text-success' : 'text-danger'}`}> {currency(data.projection?.projected?.savings || 0)}
{data.projection?.days_remaining} {t('financialHealth.daysRemaining')}
{/* Accounts by Currency */} {data.summary?.accounts_by_currency?.length > 1 && (
{t('financialHealth.summary.byCurrency')}
{data.summary.accounts_by_currency.map((curr, idx) => (
{curr.currency} = 0 ? 'text-success' : 'text-danger'}> {currency(curr.balance, curr.currency)}
))}
)}
)} {/* Metrics Tab */} {activeTab === 'metrics' && (
{Object.entries(data.metrics || {}).map(([key, metric]) => { const config = metricConfigs[key]; if (!config) return null; return (
{t(`financialHealth.metrics.${key}`)}
{t(`financialHealth.status.${metric.status}`)}

{metric.score}

/100
{/* Progress bar */}
{/* Metric-specific details */}
{key === 'savings_capacity' && ( <>
{t('financialHealth.details.savingsRate')}: {metric.savings_rate}%
{t('financialHealth.details.monthlySavings')}: {currency(metric.monthly_savings)}
)} {key === 'debt_control' && ( <>
{t('financialHealth.details.totalDebt')}: {currency(metric.total_debt)}
{/* Multi-currency breakdown */} {metric.debt_by_currency && Object.keys(metric.debt_by_currency).length > 1 && (
{Object.entries(metric.debt_by_currency).map(([curr, amt]) => ( {curr}: {Number(amt).toLocaleString(undefined, {minimumFractionDigits: 2})} ))}
)}
{t('financialHealth.details.debtToIncome')}: {metric.debt_to_income_ratio}%
{metric.active_debts > 0 && (
{t('financialHealth.details.activeDebts')}: {metric.active_debts}
)} )} {key === 'budget_management' && ( <> {metric.has_budgets ? ( <>
{t('financialHealth.details.budgetsConfigured')}: {metric.total_budgets}
{t('financialHealth.details.compliance')}: {metric.compliance_rate}%
{metric.exceeded_count > 0 && (
{t('financialHealth.details.exceeded')}: {metric.exceeded_count}
)} ) : ( {t('financialHealth.details.noBudgets')} )} )} {key === 'expense_efficiency' && ( <>
{t('financialHealth.distribution.fixed')}: {metric.distribution?.fixed?.percentage}%
{t('financialHealth.distribution.variable')}: {metric.distribution?.variable?.percentage}%
{t('financialHealth.distribution.discretionary')}: {metric.distribution?.discretionary?.percentage}%
)} {key === 'emergency_fund' && ( <>
{t('financialHealth.details.liquidAssets')}: {currency(metric.liquid_assets)}
{/* Multi-currency breakdown */} {metric.liquid_assets_by_currency && Object.keys(metric.liquid_assets_by_currency).length > 1 && (
{Object.entries(metric.liquid_assets_by_currency).map(([curr, amt]) => ( {curr}: {Number(amt).toLocaleString(undefined, {minimumFractionDigits: 2})} ))}
)}
{t('financialHealth.details.monthsCovered')}: {metric.months_covered} {t('common.months')}
{metric.gap > 0 && (
{t('financialHealth.details.gap')}: {currency(metric.gap)}
)} )} {key === 'financial_stability' && ( <>
{t('financialHealth.details.incomeVolatility')}: {metric.income_volatility}%
{t('financialHealth.details.expenseVolatility')}: {metric.expense_volatility}%
{t('financialHealth.details.savingsTrend')}: {' '}{t(`financialHealth.trend.${metric.savings_trend}`)}
)}
); })}
)} {/* Categories Tab */} {activeTab === 'categories' && (
{/* Expense Distribution */}
{t('financialHealth.categories.distribution')}
{distributionChartData && (
)}
{data.category_analysis?.expense_distribution && Object.entries(data.category_analysis.expense_distribution).map(([key, val]) => (
{t(`financialHealth.distribution.${key}`)} {currency(val.amount)} ({val.percentage}%)
{/* Multi-currency breakdown */} {val.by_currency && Object.keys(val.by_currency).length > 1 && (
{Object.entries(val.by_currency).map(([curr, amt]) => ( {curr}: {Number(amt).toLocaleString()} ))}
)}
))}
{/* Top Expenses */}
{t('financialHealth.categories.topExpenses')}
{data.category_analysis?.top_expenses?.slice(0, 10).map((cat, idx) => (
{cat.name}
{currency(cat.total)} ({cat.percentage}%) {/* Multi-currency breakdown */} {cat.by_currency && Object.keys(cat.by_currency).length > 1 && (
{Object.entries(cat.by_currency).map(([curr, amt]) => ( {curr}: {Number(amt).toLocaleString(undefined, { minimumFractionDigits: 2 })} ))}
)}
))}
{/* Category Trends */} {data.category_analysis?.category_trends?.length > 0 && (
{t('financialHealth.categories.trends')}
{safeCategoryTrends.map((trend, idx) => (
{trend.category}
{Math.abs(trend.change_percent)}%
{currency(trend.previous)} → {currency(trend.current)}
))}
)}
)} {/* Trends Tab */} {activeTab === 'trends' && (
{/* Monthly Evolution Chart */}
{t('financialHealth.trends.monthlyEvolution')}
{monthlyChartData && (
)}
{/* Trend Indicators */}
{/* Income Trend */}
{t('financialHealth.trends.incomeTrend')}
{t(`financialHealth.trend.${data.trends?.income_trend?.direction}`)}
{data.trends?.income_trend?.strength}%
{/* Expense Trend */}
{t('financialHealth.trends.expenseTrend')}
{t(`financialHealth.trend.${data.trends?.expense_trend?.direction}`)}
{data.trends?.expense_trend?.strength}%
{/* Savings Trend */}
{t('financialHealth.trends.savingsTrend')}
{t(`financialHealth.trend.${data.trends?.savings_trend?.direction}`)}
{data.trends?.savings_trend?.strength}%
{/* Monthly Comparison */}
{t('financialHealth.trends.monthlyComparison')}
{t('financialHealth.income')} 0 ? 'text-success' : 'text-danger'}> {data.trends?.monthly_comparison?.income?.change > 0 ? '+' : ''}{data.trends?.monthly_comparison?.income?.change}%
{t('financialHealth.expenses')} {data.trends?.monthly_comparison?.expenses?.change > 0 ? '+' : ''}{data.trends?.monthly_comparison?.expenses?.change}%
{/* Score History */}
{t('financialHealth.trends.scoreHistory')}
{safeHistory.length > 0 && (
h.month_label), datasets: [ { label: t('financialHealth.score'), data: safeHistory.map(h => h.score), borderColor: '#3b82f6', backgroundColor: '#3b82f620', fill: true, tension: 0.4, }, { label: t('financialHealth.savingsRate'), data: safeHistory.map(h => h.savings_rate), borderColor: '#22c55e', backgroundColor: 'transparent', borderDash: [5, 5], tension: 0.4, }, ], }} options={{ responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', labels: { color: '#94a3b8' }, }, }, scales: { x: { grid: { color: '#1e293b' }, ticks: { color: '#94a3b8' }, }, y: { grid: { color: '#1e293b' }, ticks: { color: '#94a3b8' }, min: 0, max: 100, }, }, }} />
)}
)} {/* Insights Tab */} {activeTab === 'insights' && (
{/* Insights */}
{t('financialHealth.insightsTitle')}
{data.insights?.length > 0 ? (
{data.insights.map((insight, idx) => (
{t(insight.title_key, insight.data)}

{t(insight.message_key, insight.data)}

))}
) : (

{t('financialHealth.noInsights')}

)}
{/* Recommendations */}
{t('financialHealth.recommendationsTitle')}
{data.recommendations?.length > 0 ? (
{data.recommendations.map((rec, idx) => (
{rec.priority === 'high' ? t('financialHealth.priority.high') : t('financialHealth.priority.medium')}

{t(rec.action_key, rec)}

{rec.target_amount && ( {t('financialHealth.target')}: {currency(rec.target_amount)} )} {rec.monthly_suggestion && ( {t('financialHealth.monthlyTarget')}: {currency(rec.monthly_suggestion)} )}
))}
) : (

{t('financialHealth.noRecommendations')}

)}
{/* Projection */} {data.projection && (
{t('financialHealth.projection.title')}
{t('financialHealth.projection.currentExpenses')} {currency(data.projection.current_month?.expenses || 0)}
{t('financialHealth.projection.projected')} {currency(data.projection.projected?.expenses || 0)}
{data.projection.days_remaining} {t('financialHealth.daysRemaining')} {data.projection.vs_average?.expenses !== 0 && ( 0 ? 'text-danger' : 'text-success'}> {' '}({data.projection.vs_average.expenses > 0 ? '+' : ''}{data.projection.vs_average.expenses}% {t('financialHealth.vsAverage')}) )}
)}
)}
); }; export default FinancialHealth;