import React, { useEffect, useState, useMemo } from 'react';
import { StorageService } from '../services/storageService';
import { Task, TaskStatus, StudySession, Priority, UserProfile } from '../types';
import { useNavigate } from 'react-router-dom';
import {
BarChart, Bar, XAxis, YAxis, Tooltip as RechartsTooltip, ResponsiveContainer,
Cell, PieChart, Pie
} from 'recharts';
import {
TrendingUp, Zap, Target,
Brain, Info,
ArrowUpRight,
Loader2,
GraduationCap, Activity,
CheckCircle2, Clock,
Sparkles,
PieChart as PieIcon,
Zap as ZapIcon,
Layout,
ArrowUp,
ArrowDown,
Minus,
AlertCircle
} from 'lucide-react';
import { Button } from '../components/ui/Button';
import { useLanguage } from '../contexts/LanguageContext';
const CountUp = ({ end, duration = 1500 }: { end: number, duration?: number }) => {
const [count, setCount] = useState(0);
useEffect(() => {
let startTime: number;
let animationFrame: number;
const update = (currentTime: number) => {
if (!startTime) startTime = currentTime;
const progress = Math.min((currentTime - startTime) / duration, 1);
const ease = 1 - Math.pow(1 - progress, 4);
setCount(Math.floor(ease * end));
if (progress < 1) animationFrame = requestAnimationFrame(update);
};
animationFrame = requestAnimationFrame(update);
return () => cancelAnimationFrame(animationFrame);
}, [end, duration]);
return <>{count}>;
};
const EmptyMetricState = ({
title,
message,
cta,
onCtaClick,
icon: Icon
}: {
title: string,
message: string,
cta: string,
onCtaClick: () => void,
icon: any
}) => (
{title}
"{message}"
);
const Analytics: React.FC = () => {
const navigate = useNavigate();
const { t } = useLanguage();
const [tasks, setTasks] = useState([]);
const [sessions, setSessions] = useState([]);
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
const [tasksData, sessionsData, profileData] = await Promise.all([
StorageService.getTasks(),
StorageService.getStudySessions(),
StorageService.getProfile()
]);
setTasks(tasksData);
setSessions(sessionsData);
setProfile(profileData);
setLoading(false);
};
fetchData();
}, []);
const stats = useMemo(() => {
if (tasks.length === 0 && sessions.length === 0) {
return { score: 0, trend: 'stable', focus: 0, deepWork: 0, curriculum: 0, consistency: 0, difficulty: 0 };
}
// 1. Focus Grade (30%): Completion percentage
const completedTasks = tasks.filter(t => t.status === TaskStatus.COMPLETED).length;
const focusGrade = tasks.length > 0 ? (completedTasks / tasks.length) * 100 : 0;
// 2. Deep Work Hours (25%): Target 15 hours / week as 100%
const totalFocusMinutes = sessions.filter(s => s.isFocusMode).reduce((sum, s) => sum + s.durationMinutes, 0);
const deepWorkHours = totalFocusMinutes / 60;
const deepWorkScore = Math.min(100, (deepWorkHours / 15) * 100);
// 3. Curriculum Load Completion (20%): Percentage of tasks completed
const curriculumScore = tasks.length > 0 ? (completedTasks / tasks.length) * 100 : 0;
// 4. Consistency/Streaks (15%): Days active in the last 7 days
const uniqueDays = new Set(sessions.map(s => new Date(s.timestamp).toLocaleDateString())).size;
const consistencyScore = (uniqueDays / 7) * 100;
// 5. Task Difficulty Handling (10%): Completion of High Priority tasks
const highPriorityTasks = tasks.filter(t => t.priority === Priority.HIGH);
const completedHighPriority = highPriorityTasks.filter(t => t.status === TaskStatus.COMPLETED).length;
const difficultyScore = highPriorityTasks.length > 0 ? (completedHighPriority / highPriorityTasks.length) * 100 : 50;
const compositeScore = Math.round(
(focusGrade * 0.30) +
(deepWorkScore * 0.25) +
(curriculumScore * 0.20) +
(consistencyScore * 0.15) +
(difficultyScore * 0.10)
);
let trend: 'up' | 'down' | 'stable' = 'stable';
if (sessions.length > 2) {
const mid = Math.floor(sessions.length / 2);
const recent = sessions.slice(mid).reduce((a, b) => a + b.durationMinutes, 0);
const older = sessions.slice(0, mid).reduce((a, b) => a + b.durationMinutes, 0);
if (recent > older * 1.1) trend = 'up';
else if (recent < older * 0.9) trend = 'down';
}
return {
score: compositeScore,
trend,
focus: Math.round(focusGrade),
deepWork: Math.round(deepWorkHours * 10) / 10,
curriculum: Math.round(curriculumScore),
consistency: Math.round(consistencyScore),
difficulty: Math.round(difficultyScore)
};
}, [tasks, sessions]);
const intensityMapping = useMemo(() => {
const days = 7;
const result = [];
for (let i = days - 1; i >= 0; i--) {
const d = new Date();
d.setDate(d.getDate() - i);
const dateStr = d.toLocaleDateString();
const mins = sessions
.filter(s => new Date(s.timestamp).toLocaleDateString() === dateStr)
.reduce((sum, s) => sum + s.durationMinutes, 0);
result.push({ name: d.toLocaleDateString(undefined, {weekday: 'short'}), mins });
}
return result;
}, [sessions]);
const velocityByDiscipline = useMemo(() => {
const subs = Array.from(new Set(tasks.map(t => t.subject)));
return subs.map(sub => {
const subTasks = tasks.filter(t => t.subject === sub);
const completed = subTasks.filter(t => t.status === TaskStatus.COMPLETED).length;
const subSessions = sessions.filter(s => s.subject === sub);
const totalMins = subSessions.reduce((sum, s) => sum + s.durationMinutes, 0);
return {
subject: sub,
percent: Math.round((completed / subTasks.length) * 100),
time: totalMins,
tasks: subTasks.length
};
}).sort((a, b) => b.percent - a.percent);
}, [tasks, sessions]);
const curriculumLoad = useMemo(() => {
const subjects = Array.from(new Set(tasks.map(t => t.subject)));
return subjects.map((sub, i) => ({
name: sub,
value: tasks.filter(t => t.subject === sub).length,
fill: ['#ef4444', '#3b82f6', '#10b981', '#f59e0b', '#6366f1', '#ec4899'][i % 6]
}));
}, [tasks]);
const stateAnalysisData = useMemo(() => {
const deepWork = sessions.filter(s => s.isFocusMode).reduce((sum, s) => sum + s.durationMinutes, 0);
const standardWork = sessions.filter(s => !s.isFocusMode).reduce((sum, s) => sum + s.durationMinutes, 0);
return [
{ name: 'Deep Work', value: deepWork, fill: '#4f46e5' },
{ name: 'Standard', value: standardWork, fill: '#94a3b8' }
].filter(v => v.value > 0);
}, [sessions]);
const nextBestAction = useMemo(() => {
const pending = tasks.filter(t => t.status !== TaskStatus.COMPLETED);
if (pending.length === 0) return { title: "Set New Goals", desc: "Your queue is empty. Ready for the next challenge?", subject: "General" };
const highPriority = pending.find(t => t.priority === Priority.HIGH);
if (highPriority) return { title: "High Priority Mission", desc: `Focus on ${highPriority.title}. It's critical for your curriculum progress.`, subject: highPriority.subject };
return { title: "Momentum Builder", desc: `Keep the streak alive by finishing "${pending[0].title}".`, subject: pending[0].subject };
}, [tasks]);
if (loading) return (
Processing Intelligence Command...
);
return (
{/* HEADER & COMPOSITE SCORE */}
{stats.trend === 'up' &&
}
{stats.trend === 'down' &&
}
{stats.trend === 'stable' &&
}
Intelligence Score
Cognitive Profile
This score reflects learning quality, not IQ.
Breakdown: Focus (30%), Deep Work (25%), Completion (20%), Consistency (15%), Difficulty (10%).
Efficiency at {stats.focus}%.
Deep work sessions are {stats.trend === 'up' ? 'increasing' : 'stabilizing'}.
{stats.score > 70 ? 'Scholar Class' : 'Active Growth'}
Level {Math.floor((profile?.xp || 0)/500)+1}
Recommendation
{nextBestAction.title}
"{nextBestAction.desc}"
{/* INTENSITY MAPPING (STUDY PULSE) */}
Intensity Pulse
Concentration Peaks (Last 7 Days)
{sessions.length > 0 && (
{(sessions.reduce((a,b) => a+b.durationMinutes,0)/60).toFixed(1)}h
Total Energy Invested
)}
{sessions.length === 0 ? (
navigate('/study-buddy')}
icon={ZapIcon}
/>
) : (
{
if (active && payload) return {payload[0].value} Minutes
;
return null;
}}
/>
{intensityMapping.map((entry, index) => (
60 ? '#dc2626' : entry.mins > 0 ? '#4f46e5' : '#e2e8f0'} />
))}
|
)}
{/* CURRICULUM LOAD BREAKDOWN */}
Equilibrium Index
{tasks.length === 0 ? (
navigate('/planner')}
icon={Layout}
/>
) : (
{curriculumLoad.map((entry, index) => (
|
))}
{curriculumLoad.map((item, i) => (
))}
)}
{/* STATE ANALYSIS */}
Flow Ratio
{sessions.length === 0 ? (
navigate('/study-buddy')}
icon={ZapIcon}
/>
) : (
{stateAnalysisData.map((item, i) => {
const total = stateAnalysisData.reduce((a,b) => a+b.value, 0);
const percent = Math.round((item.value / total) * 100);
return (
);
})}
Focus sessions yield 2x faster mastery conversion.
)}
{/* VELOCITY BY DISCIPLINE */}
Subject Velocity
{velocityByDiscipline.length === 0 ? (
navigate('/planner')}
icon={TrendingUp}
/>
) : (
{velocityByDiscipline.map((sub, i) => (
{sub.subject}
{sub.time} Minutes Invested • {sub.tasks} Units
{sub.percent}%
))}
)}
);
};
export default Analytics;