import { useState, useEffect, memo } from 'react';
import { useAuth } from '../context/AuthContext';
import { useNavigate } from 'react-router-dom';
import api from '../api/axios';
import {
LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
PieChart, Pie, Cell, Legend, BarChart, Bar, AreaChart, Area, ReferenceLine
} from 'recharts';
import { TrendingUp, TrendingDown, RefreshCw, DollarSign, Activity, Settings, X, Check, FileText, Download, Globe, Target, Mail, ChevronDown, ArrowDown, ArrowUp, Shield, Zap, ShoppingCart, ArrowLeft } from 'lucide-react';
import { useSettings } from '../context/SettingsContext';
import ForecastChart from '../components/ForecastChart';
import AnomalyFeed from '../components/AnomalyFeed';
import AuroraLayout from '../components/AuroraLayout';
const COLORS = ['#6366f1', '#ec4899', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#3b82f6'];
const FinancialHealthMetrics = memo(({ summary, currencySymbol }) => (
Financial Health
{summary?.health_score || 0}%
Savings Rate
{summary?.savings_rate || 0}%
Budget Control
{summary?.expense_control || 0}%
));
const MonthlyTrendsChart = memo(({ summary, currencySymbol }) => (
(t.income || 0) !== 0 || (t.expense || 0) !== 0)}>
[`${currencySymbol}${value.toLocaleString()}`, name]}
/>
));
const SavingsRateTrendChart = memo(({ summary, currencySymbol }) => (
({
month: item.month,
savings: item.income - item.expense,
income: item.income,
expense: item.expense
})).filter(t => (t.income || 0) !== 0 || (t.expense || 0) !== 0)}>
[`${currencySymbol}${value.toLocaleString()}`, name]}
/>
));
const ExpenseBreakdownChart = memo(({ summary, currencySymbol, colors }) => (
Expense Breakdown
`${name}: ${(percent * 100).toFixed(1)}%`}
>
{(summary?.expense_breakdown || []).map((entry, index) => (
|
))}
[`${currencySymbol}${value.toLocaleString()}`, props.payload.name]}
/>
));
const IncomeBreakdownChart = memo(({ summary, currencySymbol, colors }) => (
Income Breakdown
`${name}: ${(percent * 100).toFixed(1)}%`}
>
{(summary?.income_sources || []).map((entry, index) => (
|
))}
[`${currencySymbol}${value.toLocaleString()}`, props.payload.name]}
/>
));
const DailyIncomeTrendChart = memo(({ summary, currencySymbol, colors }) => {
const allDailyCats = new Set();
(summary?.daily_income_trend || []).forEach(day => {
Object.keys(day).forEach(k => {
if (k !== 'date' && k !== 'total_amount' && k !== 'moving_avg_30d') allDailyCats.add(k);
});
});
return (
Daily Income Trend (All-Time History)
(d.total_amount || 0) > 0)}>
{
if (name === 'moving_avg_30d') return [`${currencySymbol}${value}`, "30-Day Moving Avg"];
if (name === 'total_amount') return [null, null];
return [`${currencySymbol}${value}`, name];
}}
labelStyle={{ color: 'var(--primary-color)', fontWeight: 'bold' }}
filterNull={true}
/>
{Array.from(allDailyCats).map((cat, idx) => (
))}
);
});
const IncomeCategoryTrendsChart = memo(({ summary, currencySymbol, colors }) => {
const allCats = new Set();
(summary?.income_category_trends || []).forEach(item => {
Object.keys(item).forEach(k => {
if (k !== 'month' && k !== 'total') allCats.add(k);
});
});
return (
Income Category Trends (All Time)
{
const entries = Object.entries(item);
return entries.some(([key, val]) => key !== 'month' && key !== 'total' && val !== 0);
})}>
[`${currencySymbol}${value}`, name]}
/>
{Array.from(allCats).map((cat, idx) => (
))}
);
});
const CategoryTrendsChart = memo(({ summary, currencySymbol, colors }) => {
const allCats = new Set();
(summary?.category_trends || []).forEach(item => {
Object.keys(item).forEach(k => {
if (k !== 'month') allCats.add(k);
});
});
return (
Category Spending Trends (All Time)
{
const entries = Object.entries(item);
return entries.some(([key, val]) => key !== 'month' && val !== 0);
})}>
[`${currencySymbol}${value}`, name]}
/>
{Array.from(allCats).map((cat, idx) => (
))}
);
});
const DailySpendingTrendChart = memo(({ summary, currencySymbol, colors }) => {
const allDailyCats = new Set();
(summary?.daily_spending_trend || []).forEach(day => {
Object.keys(day).forEach(k => {
if (k !== 'date' && k !== 'total_amount' && k !== 'moving_avg_30d') allDailyCats.add(k);
});
});
return (
Daily Spending Trend (All-Time History)
(d.total_amount || 0) > 0)}>
{
if (name === 'moving_avg_30d') return [`${currencySymbol}${value}`, "30-Day Moving Avg"];
if (name === 'total_amount') return [null, null];
return [`${currencySymbol}${value}`, name];
}}
labelStyle={{ color: 'var(--primary-color)', fontWeight: 'bold' }}
filterNull={true}
/>
{Array.from(allDailyCats).map((cat, idx) => (
))}
{summary?.lifetime_daily_average && (
)}
);
});
const FinancialInsightsWidget = memo(({ summary, currencySymbol }) => {
const totalExpense = summary?.total_expense || 0;
const totalIncome = summary?.total_income || 0;
const recentTransactions = summary?.recent_transactions || [];
const avgTransaction = recentTransactions.length > 0
? (recentTransactions.reduce((sum, t) => sum + parseFloat(t.amount), 0) / recentTransactions.length).toFixed(2)
: 0;
const largestExpense = summary?.expense_breakdown?.[0] || { name: 'N/A', value: 0 };
const largestIncome = summary?.income_sources?.[0] || { name: 'N/A', value: 0 };
const spendingRatio = totalIncome > 0 ? ((totalExpense / totalIncome) * 100).toFixed(1) : 0;
const savingsRate = totalIncome > 0 ? (((totalIncome - totalExpense) / totalIncome) * 100).toFixed(1) : 0;
const insights = [
{
icon: ,
label: 'Burn Rate (Monthly)',
value: `${currencySymbol}${summary?.burn_rate || 0}`,
subValue: 'Last 90d avg',
color: '#ef4444',
bgColor: 'rgba(239, 68, 68, 0.1)'
},
{
icon: ,
label: 'Financial Runway',
value: `${summary?.runway_months || 0} Months`,
subValue: `~${summary?.runway_days || 0} Days remaining`,
color: (summary?.runway_months || 0) > 3 ? '#10b981' : (summary?.runway_months || 0) > 1 ? '#f59e0b' : '#ef4444',
bgColor: (summary?.runway_months || 0) > 3 ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)'
},
{
icon: ,
label: 'Projected Spend',
value: `${currencySymbol}${summary?.momentum?.projected_spending || 0}`,
subValue: 'Estimated end of month',
color: (summary?.momentum?.projected_spending || 0) > (summary?.total_income || 0) ? '#ef4444' : '#6366f1',
bgColor: 'rgba(99, 102, 241, 0.1)'
},
{
icon: ,
label: 'Top Category Surge',
value: summary?.momentum?.spiking_category || 'N/A',
subValue: 'Highest MoM increase',
color: '#f59e0b',
bgColor: 'rgba(245, 158, 11, 0.1)'
},
{
icon: ,
label: 'Spending Momentum',
value: `${summary?.momentum?.spending > 0 ? '+' : ''}${summary?.momentum?.spending || 0}%`,
subValue: 'Change vs last month',
color: (summary?.momentum?.spending || 0) > 0 ? '#ef4444' : '#10b981',
bgColor: (summary?.momentum?.spending || 0) > 0 ? 'rgba(239, 68, 68, 0.1)' : 'rgba(16, 185, 129, 0.1)'
},
{
icon: ,
label: 'Income Momentum',
value: `${summary?.momentum?.income > 0 ? '+' : ''}${summary?.momentum?.income || 0}%`,
subValue: 'Change vs last month',
color: (summary?.momentum?.income || 0) >= 0 ? '#10b981' : '#ef4444',
bgColor: (summary?.momentum?.income || 0) >= 0 ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)'
},
{
icon: ,
label: 'Savings Velocity',
value: `${summary?.momentum?.savings > 0 ? '+' : ''}${summary?.momentum?.savings || 0}%`,
subValue: 'Savings rate shift',
color: (summary?.momentum?.savings || 0) >= 0 ? '#10b981' : '#ef4444',
bgColor: (summary?.momentum?.savings || 0) >= 0 ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)'
},
{
icon: ,
label: 'Volatility Score',
value: `${summary?.volatility_score || 0}%`,
subValue: 'Spending stability',
color: (summary?.volatility_score || 0) < 50 ? '#10b981' : '#f59e0b',
bgColor: 'rgba(16, 185, 129, 0.1)'
},
{
icon: ,
label: 'Recurring Subs',
value: summary?.recurring_count || 0,
subValue: 'Detected patterns',
color: '#6366f1',
bgColor: 'rgba(99, 102, 241, 0.1)'
},
{
icon: ,
label: 'Current Txns',
value: summary?.monthly_txn_count || 0,
subValue: 'Count this month',
color: '#8b5cf6',
bgColor: 'rgba(139, 92, 246, 0.1)'
}
];
return (
Real-Time Financial Insights
{insights.map((insight, idx) => (
{insight.icon}
{insight.label}
{insight.value}
{insight.subValue && (
{insight.subValue}
)}
))}
);
});
const AnalyticsPage = () => {
const { user } = useAuth();
const navigate = useNavigate();
const { currencySymbol } = useSettings();
const [summary, setSummary] = useState(null);
const [loading, setLoading] = useState(true);
const fetchData = async () => {
try {
const summaryRes = await api.get('finance/dashboard-summary/');
setSummary(summaryRes.data);
setLoading(false);
} catch (err) {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return (
{loading ? (
Analyzing financial data...
) : (
<>
>
)}
);
};
export default AnalyticsPage;