FinMK / frontend /src /pages /AnalyticsPage.jsx
Kumar
Refactor: Exclude PDF and CSV files from Git to fix HF push error
24e6f5b
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 }) => (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '1rem', marginBottom: '1.5rem' }}>
<div className="stat-card" style={{
background: 'rgba(99, 102, 241, 0.1)',
border: '1px solid rgba(99, 102, 241, 0.2)',
borderRadius: '50px',
padding: '0.75rem 1rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
boxShadow: '0 4px 15px rgba(99, 102, 241, 0.1)'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div style={{ background: 'rgba(99, 102, 241, 0.2)', padding: '0.5rem', borderRadius: '50%' }}><Activity size={18} color="#6366f1" /></div>
<div>
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>Financial Health</div>
<div style={{ fontSize: '1.1rem', fontWeight: 'bold', color: '#fff' }}>{summary?.health_score || 0}%</div>
</div>
</div>
</div>
<div className="stat-card" style={{
background: 'rgba(236, 72, 153, 0.1)',
border: '1px solid rgba(236, 72, 153, 0.2)',
borderRadius: '50px',
padding: '0.75rem 1rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
boxShadow: '0 4px 15px rgba(236, 72, 153, 0.1)'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div style={{ background: 'rgba(236, 72, 153, 0.2)', padding: '0.5rem', borderRadius: '50%' }}><TrendingUp size={18} color="#ec4899" /></div>
<div>
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>Savings Rate</div>
<div style={{ fontSize: '1.1rem', fontWeight: 'bold', color: '#fff' }}>{summary?.savings_rate || 0}%</div>
</div>
</div>
</div>
<div className="stat-card" style={{
background: 'rgba(16, 185, 129, 0.1)',
border: '1px solid rgba(16, 185, 129, 0.2)',
borderRadius: '50px',
padding: '0.75rem 1rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
boxShadow: '0 4px 15px rgba(16, 185, 129, 0.1)'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div style={{ background: 'rgba(16, 185, 129, 0.2)', padding: '0.5rem', borderRadius: '50%' }}><Shield size={18} color="#10b981" /></div>
<div>
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>Budget Control</div>
<div style={{ fontSize: '1.1rem', fontWeight: 'bold', color: '#fff' }}>{summary?.expense_control || 0}%</div>
</div>
</div>
</div>
</div>
));
const MonthlyTrendsChart = memo(({ summary, currencySymbol }) => (
<div className="glass-panel" style={{ padding: '1rem', minHeight: '350px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }}>
<h3 style={{ margin: 0, fontSize: '1.1rem' }}>Monthly Trends</h3>
<div style={{ background: 'rgba(255,255,255,0.05)', padding: '0.25rem 0.75rem', borderRadius: '20px', fontSize: '0.8rem', color: 'var(--text-muted)' }}>
All Time
</div>
</div>
<div style={{ height: '350px', width: '100%' }}>
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={(summary?.monthly_trends || []).filter(t => (t.income || 0) !== 0 || (t.expense || 0) !== 0)}>
<defs>
<linearGradient id="colorIncome" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#10b981" stopOpacity={0.3} />
<stop offset="95%" stopColor="#10b981" stopOpacity={0} />
</linearGradient>
<linearGradient id="colorExpense" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#ef4444" stopOpacity={0.3} />
<stop offset="95%" stopColor="#ef4444" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.1)" />
<XAxis dataKey="month" stroke="var(--text-muted)" fontSize={12} />
<YAxis stroke="var(--text-muted)" fontSize={12} />
<Tooltip
contentStyle={{ backgroundColor: 'var(--bg-card)', border: '1px solid var(--glass-border)', borderRadius: '0.5rem' }}
itemStyle={{ color: 'white' }}
formatter={(value, name) => [`${currencySymbol}${value.toLocaleString()}`, name]}
/>
<Legend />
<Area type="monotone" dataKey="income" name="Income" stroke="#10b981" fillOpacity={1} fill="url(#colorIncome)" strokeWidth={2} />
<Area type="monotone" dataKey="expense" name="Expense" stroke="#ef4444" fillOpacity={1} fill="url(#colorExpense)" strokeWidth={2} />
<Line type="monotone" dataKey="net" name="Net Balance" stroke="#6366f1" strokeWidth={2} dot={{ fill: '#6366f1' }} />
</AreaChart>
</ResponsiveContainer>
</div>
</div>
));
const SavingsRateTrendChart = memo(({ summary, currencySymbol }) => (
<div className="glass-panel" style={{ padding: '1rem', minHeight: '350px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }}>
<h3 style={{ margin: 0, fontSize: '1.1rem' }}>Savings Trend</h3>
<div style={{ background: 'rgba(255,255,255,0.05)', padding: '0.25rem 0.75rem', borderRadius: '20px', fontSize: '0.8rem', color: 'var(--text-muted)' }}>
All Time
</div>
</div>
<div style={{ height: '350px', width: '100%' }}>
<ResponsiveContainer width="100%" height="100%">
<LineChart data={(summary?.monthly_trends || []).map(item => ({
month: item.month,
savings: item.income - item.expense,
income: item.income,
expense: item.expense
})).filter(t => (t.income || 0) !== 0 || (t.expense || 0) !== 0)}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.1)" />
<XAxis dataKey="month" stroke="var(--text-muted)" fontSize={12} />
<YAxis stroke="var(--text-muted)" fontSize={12} />
<Tooltip
contentStyle={{ backgroundColor: 'var(--bg-card)', border: '1px solid var(--glass-border)', borderRadius: '0.5rem' }}
itemStyle={{ color: 'white' }}
formatter={(value, name) => [`${currencySymbol}${value.toLocaleString()}`, name]}
/>
<Legend />
<ReferenceLine y={0} stroke="#64748b" strokeDasharray="3 3" />
<Line type="monotone" dataKey="savings" name="Savings" stroke="#10b981" strokeWidth={3} dot={{ fill: '#10b981', r: 4 }} />
</LineChart>
</ResponsiveContainer>
</div>
</div>
));
const ExpenseBreakdownChart = memo(({ summary, currencySymbol, colors }) => (
<div className="glass-panel" style={{ padding: '1.5rem' }}>
<h3 style={{ borderBottom: '1px solid var(--glass-border)', paddingBottom: '0.75rem', marginBottom: '1.5rem', fontSize: '1.2rem' }}>Expense Breakdown</h3>
<div style={{ height: '400px', width: '100%' }}>
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={summary?.expense_breakdown || []}
dataKey="value"
nameKey="name"
cx="50%"
cy="50%"
outerRadius={110}
innerRadius={60}
label={({ name, percent }) => `${name}: ${(percent * 100).toFixed(1)}%`}
>
{(summary?.expense_breakdown || []).map((entry, index) => (
<Cell key={`cell-${index}`} fill={colors[index % colors.length]} />
))}
</Pie>
<Tooltip
contentStyle={{ backgroundColor: 'var(--bg-card)', border: '1px solid var(--glass-border)', borderRadius: '0.5rem' }}
itemStyle={{ color: '#fff' }}
formatter={(value, name, props) => [`${currencySymbol}${value.toLocaleString()}`, props.payload.name]}
/>
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
</div>
));
const IncomeBreakdownChart = memo(({ summary, currencySymbol, colors }) => (
<div className="glass-panel" style={{ padding: '1.5rem' }}>
<h3 style={{ borderBottom: '1px solid var(--glass-border)', paddingBottom: '0.75rem', marginBottom: '1.5rem', fontSize: '1.2rem' }}>Income Breakdown</h3>
<div style={{ height: '400px', width: '100%' }}>
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={summary?.income_sources || []}
dataKey="value"
nameKey="name"
cx="50%"
cy="50%"
outerRadius={110}
innerRadius={60}
label={({ name, percent }) => `${name}: ${(percent * 100).toFixed(1)}%`}
>
{(summary?.income_sources || []).map((entry, index) => (
<Cell key={`income-cell-${index}`} fill={colors[index % colors.length]} />
))}
</Pie>
<Tooltip
contentStyle={{ backgroundColor: 'var(--bg-card)', border: '1px solid var(--glass-border)', borderRadius: '0.5rem' }}
itemStyle={{ color: '#fff' }}
formatter={(value, name, props) => [`${currencySymbol}${value.toLocaleString()}`, props.payload.name]}
/>
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
</div>
));
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 (
<div className="glass-panel" style={{ padding: '1rem', gridColumn: 'span 12' }}>
<h3 style={{ borderBottom: '1px solid var(--glass-border)', paddingBottom: '0.5rem', marginBottom: '1rem' }}>
Daily Income Trend (All-Time History)
</h3>
<div style={{ height: '350px', width: '100%' }}>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={(summary?.daily_income_trend || []).filter(d => (d.total_amount || 0) > 0)}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.1)" />
<XAxis dataKey="date" stroke="var(--text-muted)" fontSize={10} interval={Math.max(6, Math.floor((summary?.daily_income_trend?.length || 0) / 10))} />
<YAxis stroke="var(--text-muted)" fontSize={12} />
<Tooltip
contentStyle={{ backgroundColor: 'var(--bg-card)', border: '1px solid var(--glass-border)', borderRadius: '0.5rem' }}
itemStyle={{ color: 'white' }}
cursor={{ fill: 'rgba(255,255,255,0.05)' }}
formatter={(value, name) => {
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}
/>
<Legend />
{Array.from(allDailyCats).map((cat, idx) => (
<Bar
key={cat}
dataKey={cat}
name={cat}
stackId="day"
fill={colors[idx % colors.length]}
radius={[0, 0, 0, 0]}
/>
))}
<Line
type="monotone"
dataKey="moving_avg_30d"
name="30-Day Avg"
stroke="#6366f1"
strokeWidth={2}
strokeDasharray="5 5"
dot={false}
/>
<ReferenceLine
y={summary?.lifetime_daily_income_average || 0}
stroke="#fbbf24"
strokeWidth={2}
strokeDasharray="3 3"
label={{ value: `Avg: ${currencySymbol}${Math.round(summary?.lifetime_daily_income_average || 0)}`, position: 'insideTopRight', fill: '#fbbf24', fontSize: 12, fontWeight: 'bold' }}
/>
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
});
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 (
<div className="glass-panel" style={{ padding: '1rem', marginBottom: '1.5rem' }}>
<h3 style={{ borderBottom: '1px solid var(--glass-border)', paddingBottom: '0.75rem', marginBottom: '1.5rem', fontSize: '1.2rem' }}>
Income Category Trends (All Time)
</h3>
<div style={{ height: '450px', width: '100%' }}>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={(summary?.income_category_trends || []).filter(item => {
const entries = Object.entries(item);
return entries.some(([key, val]) => key !== 'month' && key !== 'total' && val !== 0);
})}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.1)" />
<XAxis dataKey="month" stroke="var(--text-muted)" fontSize={12} />
<YAxis stroke="var(--text-muted)" fontSize={12} />
<Tooltip
contentStyle={{ backgroundColor: 'var(--bg-card)', border: '1px solid var(--glass-border)', borderRadius: '0.5rem' }}
itemStyle={{ color: 'white' }}
formatter={(value, name) => [`${currencySymbol}${value}`, name]}
/>
<Legend />
{Array.from(allCats).map((cat, idx) => (
<Bar
key={cat}
dataKey={cat}
stackId="a"
fill={colors[idx % colors.length]}
/>
))}
<Line type="monotone" dataKey="total" name="Total Income" stroke="#10b981" strokeWidth={3} dot={{ fill: '#10b981', r: 4 }} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
});
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 (
<div className="glass-panel" style={{ padding: '1rem', marginBottom: '1.5rem' }}>
<h3 style={{ borderBottom: '1px solid var(--glass-border)', paddingBottom: '0.5rem', marginBottom: '1rem' }}>
Category Spending Trends (All Time)
</h3>
<div style={{ height: '450px', width: '100%' }}>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={(summary?.category_trends || []).filter(item => {
const entries = Object.entries(item);
return entries.some(([key, val]) => key !== 'month' && val !== 0);
})}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.1)" />
<XAxis dataKey="month" stroke="var(--text-muted)" fontSize={12} />
<YAxis stroke="var(--text-muted)" fontSize={12} />
<Tooltip
contentStyle={{ backgroundColor: 'var(--bg-card)', border: '1px solid var(--glass-border)', borderRadius: '0.5rem' }}
itemStyle={{ color: 'white' }}
formatter={(value, name) => [`${currencySymbol}${value}`, name]}
/>
<Legend />
{Array.from(allCats).map((cat, idx) => (
<Bar
key={cat}
dataKey={cat}
stackId="a"
fill={colors[idx % colors.length]}
/>
))}
<Line type="monotone" dataKey="total" name="Total Expense" stroke="#10b981" strokeWidth={3} dot={{ fill: '#10b981', r: 4 }} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
});
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 (
<div className="glass-panel" style={{ padding: '1rem', gridColumn: 'span 12' }}>
<h3 style={{ borderBottom: '1px solid var(--glass-border)', paddingBottom: '0.5rem', marginBottom: '1rem' }}>
Daily Spending Trend (All-Time History)
</h3>
<div style={{ height: '350px', width: '100%' }}>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={(summary?.daily_spending_trend || []).filter(d => (d.total_amount || 0) > 0)}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.1)" />
<XAxis dataKey="date" stroke="var(--text-muted)" fontSize={10} interval={Math.max(6, Math.floor((summary?.daily_spending_trend?.length || 0) / 10))} />
<YAxis stroke="var(--text-muted)" fontSize={12} />
<Tooltip
contentStyle={{ backgroundColor: 'var(--bg-card)', border: '1px solid var(--glass-border)', borderRadius: '0.5rem' }}
itemStyle={{ color: 'white' }}
cursor={{ fill: 'rgba(255,255,255,0.05)' }}
formatter={(value, name) => {
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}
/>
<Legend />
{Array.from(allDailyCats).map((cat, idx) => (
<Bar
key={cat}
dataKey={cat}
name={cat}
stackId="day"
fill={colors[idx % colors.length]}
radius={[0, 0, 0, 0]}
/>
))}
<Line
type="monotone"
dataKey="moving_avg_30d"
name="30-Day Avg"
stroke="#ec4899"
strokeWidth={3}
dot={false}
animationDuration={1500}
/>
{summary?.lifetime_daily_average && (
<ReferenceLine
y={summary.lifetime_daily_average}
label={{
value: `Avg: ${currencySymbol}${summary.lifetime_daily_average}`,
position: 'insideTopRight',
fill: '#F59E0B',
fontSize: 13,
fontWeight: 'bold'
}}
stroke="#F59E0B"
strokeWidth={3}
strokeDasharray="3 3"
/>
)}
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
});
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: <Activity size={20} />,
label: 'Burn Rate (Monthly)',
value: `${currencySymbol}${summary?.burn_rate || 0}`,
subValue: 'Last 90d avg',
color: '#ef4444',
bgColor: 'rgba(239, 68, 68, 0.1)'
},
{
icon: <Target size={20} />,
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: <ShoppingCart size={20} />,
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: <TrendingUp size={20} />,
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: <TrendingDown size={20} />,
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: <TrendingUp size={20} />,
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: <Shield size={20} />,
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: <Zap size={20} />,
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: <RefreshCw size={20} />,
label: 'Recurring Subs',
value: summary?.recurring_count || 0,
subValue: 'Detected patterns',
color: '#6366f1',
bgColor: 'rgba(99, 102, 241, 0.1)'
},
{
icon: <Activity size={20} />,
label: 'Current Txns',
value: summary?.monthly_txn_count || 0,
subValue: 'Count this month',
color: '#8b5cf6',
bgColor: 'rgba(139, 92, 246, 0.1)'
}
];
return (
<div className="glass-panel" style={{ padding: '1.5rem', marginBottom: '2rem' }}>
<h3 style={{ margin: '0 0 1.5rem 0', fontSize: '1.2rem', display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<Activity size={24} className="text-gradient" />
Real-Time Financial Insights
</h3>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '1rem' }}>
{insights.map((insight, idx) => (
<div key={idx} style={{
padding: '1.25rem',
background: 'rgba(30, 41, 59, 0.4)',
borderRadius: '1rem',
border: '1px solid rgba(255,255,255,0.05)',
transition: 'all 0.3s'
}} className="hover-lift">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '0.75rem' }}>
<div style={{
width: '40px',
height: '40px',
borderRadius: '10px',
background: insight.bgColor,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: insight.color
}}>
{insight.icon}
</div>
<div style={{ fontSize: '0.85rem', color: 'var(--text-muted)', fontWeight: '500' }}>
{insight.label}
</div>
</div>
<div style={{ fontSize: '1.5rem', fontWeight: '700', color: insight.color, marginBottom: '0.25rem' }}>
{insight.value}
</div>
{insight.subValue && (
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>
{insight.subValue}
</div>
)}
</div>
))}
</div>
</div>
);
});
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 (
<AuroraLayout>
<div className="container" style={{ paddingBottom: '4rem' }}>
<header style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2rem' }}>
<div>
<h1 style={{ marginBottom: '0.5rem', display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<TrendingUp className="text-gradient" size={32} />
<span className="text-gradient">Financial Analytics</span>
</h1>
<p style={{ color: 'var(--text-muted)', margin: 0 }}>Comprehensive visualization of your economic trends</p>
</div>
<button onClick={() => navigate('/dashboard')} className="btn-primary" style={{
background: 'linear-gradient(135deg, #0891b2 0%, #0ea5e9 40%, #38bdf8 100%)',
border: 'none', color: 'white', display: 'flex', alignItems: 'center', gap: '0.5rem',
padding: '0.5rem 1.1rem', borderRadius: '8px', fontWeight: 600,
boxShadow: '0 4px 14px rgba(8,145,178,0.4)'
}}>
<ArrowLeft size={18} /> Dashboard
</button>
</header>
{loading ? (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '60vh' }}>
<div style={{ textAlign: 'center' }}>
<div className="spinner" style={{ margin: '0 auto 1rem' }}></div>
<p style={{ color: 'var(--text-muted)' }}>Analyzing financial data...</p>
</div>
</div>
) : (
<>
<FinancialInsightsWidget summary={summary} currencySymbol={currencySymbol} />
<FinancialHealthMetrics summary={summary} currencySymbol={currencySymbol} />
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(12, 1fr)', gap: '1rem', marginBottom: '1.5rem' }}>
<div style={{ gridColumn: 'span 8', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<ForecastChart />
<MonthlyTrendsChart summary={summary} currencySymbol={currencySymbol} />
</div>
<div style={{ gridColumn: 'span 4', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<AnomalyFeed />
<SavingsRateTrendChart summary={summary} currencySymbol={currencySymbol} />
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '1rem', marginBottom: '1.5rem' }}>
<ExpenseBreakdownChart summary={summary} currencySymbol={currencySymbol} colors={COLORS} />
<IncomeBreakdownChart summary={summary} currencySymbol={currencySymbol} colors={COLORS} />
</div>
<CategoryTrendsChart summary={summary} currencySymbol={currencySymbol} colors={COLORS} />
<IncomeCategoryTrendsChart summary={summary} currencySymbol={currencySymbol} colors={COLORS} />
<div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: '1rem', marginBottom: '1.5rem' }}>
<DailySpendingTrendChart summary={summary} currencySymbol={currencySymbol} colors={COLORS} />
<DailyIncomeTrendChart summary={summary} currencySymbol={currencySymbol} colors={COLORS} />
</div>
</>
)}
</div>
</AuroraLayout>
);
};
export default AnalyticsPage;