import React, { useState, useEffect, useRef } from 'react'; import { ArrowLeft, User, Shield, Clock, BookOpen, Calculator, Award, TrendingUp, CheckCircle2, Circle, Lock, Eye, EyeOff, AlertCircle, Check, Sparkles, } from 'lucide-react'; import { useAuth, UserRecord } from './auth/AuthContext'; import { useProgress } from './progress/ProgressContext'; import { useGoldCoins } from './practice/GoldCoinContext'; import { LESSONS, EBRW_LESSONS } from '../constants'; import Mascot from './Mascot'; // Animated count-up function useCountUp(target: number, duration = 900) { const [count, setCount] = useState(0); const started = useRef(false); useEffect(() => { if (started.current) return; started.current = true; const startTime = performance.now(); const animate = (now: number) => { const progress = Math.min((now - startTime) / duration, 1); const eased = 1 - Math.pow(1 - progress, 2.5); setCount(Math.round(eased * target)); if (progress < 1) requestAnimationFrame(animate); }; requestAnimationFrame(animate); }, [target, duration]); return count; } interface UserDashboardProps { onExit: () => void; } export default function UserDashboard({ onExit }: UserDashboardProps) { const { username, role, getUserRecord, changePassword, updateDisplayName } = useAuth(); const { getSubjectStats, getLessonStatus } = useProgress(); const { totalCoins, state: coinState } = useGoldCoins(); const user = getUserRecord(username || ''); const mathStats = getSubjectStats('math'); const ebrwStats = getSubjectStats('ebrw'); // Account settings const [currentPassword, setCurrentPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [showCurrentPw, setShowCurrentPw] = useState(false); const [showNewPw, setShowNewPw] = useState(false); const [pwMsg, setPwMsg] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const [pwLoading, setPwLoading] = useState(false); const [editName, setEditName] = useState(false); const [nameInput, setNameInput] = useState(user?.displayName || ''); const [nameSaved, setNameSaved] = useState(false); const animCoins = useCountUp(totalCoins, 1200); // Count completed topics across all practice const topicsAttempted = Object.keys(coinState.topicProgress).length; // Calculate total accuracy let totalAttempted = 0; let totalCorrect = 0; Object.values(coinState.topicProgress).forEach((tp: any) => { (['easy', 'medium', 'hard'] as const).forEach(d => { totalAttempted += tp[d]?.attempted || 0; totalCorrect += tp[d]?.correct || 0; }); }); const accuracy = totalAttempted > 0 ? Math.round((totalCorrect / totalAttempted) * 100) : 0; const handleChangePassword = async (e: React.FormEvent) => { e.preventDefault(); setPwMsg(null); if (newPassword !== confirmPassword) { setPwMsg({ type: 'error', text: 'New passwords do not match.' }); return; } setPwLoading(true); const result = await changePassword(username || '', currentPassword, newPassword); setPwLoading(false); if (result.success) { setPwMsg({ type: 'success', text: 'Password changed successfully!' }); setCurrentPassword(''); setNewPassword(''); setConfirmPassword(''); } else { setPwMsg({ type: 'error', text: result.error || 'Failed to change password.' }); } }; const handleSaveName = () => { if (username && nameInput.trim()) { updateDisplayName(username, nameInput.trim()); setEditName(false); setNameSaved(true); setTimeout(() => setNameSaved(false), 2000); } }; // Progress ring function ProgressRing({ percent, size = 72, stroke = 6, color }: { percent: number; size?: number; stroke?: number; color: string }) { const r = (size - stroke) / 2; const circ = 2 * Math.PI * r; const offset = circ - (percent / 100) * circ; return ( {percent}% ); } function StatusIcon({ status }: { status: string }) { if (status === 'completed') return ; if (status === 'in_progress') return ; return ; } return (
{/* Header */}

My Dashboard

{/* ── Welcome Hero ── */}
{editName ? (
setNameInput(e.target.value)} className="text-xl font-bold text-slate-900 bg-white border border-slate-200 rounded-lg px-2 py-0.5 focus:outline-none focus:ring-2 focus:ring-cyan-400 w-48" autoFocus onKeyDown={e => e.key === 'Enter' && handleSaveName()} />
) : ( <>

{user?.displayName || username}

{nameSaved && Saved} )}
{role === 'admin' && } {role} @{username}
{user?.lastLoginAt && (

Last login: {new Date(user.lastLoginAt).toLocaleString()} {user.lastLoginIp && user.lastLoginIp !== 'unknown' && from {user.lastLoginIp}}

)}
{/* ── Stats Overview ── */}

{mathStats.completed + ebrwStats.completed}

Lessons Done

{animCoins}

Gold Coins

{accuracy}%

Accuracy

{topicsAttempted}

Topics Practiced

{/* ── Lesson Progress ── */}
{/* Math */}

Mathematics

{mathStats.completed}/{mathStats.total} lessons completed

{LESSONS.map(l => (
{l.title}
))}
{/* EBRW */}

Reading & Writing

{ebrwStats.completed}/{ebrwStats.total} lessons completed

{EBRW_LESSONS.map(l => (
{l.title}
))}
{/* ── Practice Performance ── */}

Practice Performance

{totalAttempted} questions attempted across {topicsAttempted} topics

{topicsAttempted === 0 ? (
No practice sessions yet. Start practicing to see your performance!
) : (
{Object.entries(coinState.topicProgress).map(([topicId, tp]: [string, any]) => { const easy = tp.easy || { attempted: 0, correct: 0 }; const medium = tp.medium || { attempted: 0, correct: 0 }; const hard = tp.hard || { attempted: 0, correct: 0 }; const total = easy.attempted + medium.attempted + hard.attempted; const correct = easy.correct + medium.correct + hard.correct; const acc = total > 0 ? Math.round((correct / total) * 100) : 0; return (

{topicId}

{correct}/{total} correct = 70 ? 'text-emerald-500' : acc >= 40 ? 'text-amber-500' : 'text-rose-500'}`}>{acc}%
= 70 ? 'bg-emerald-400' : acc >= 40 ? 'bg-amber-400' : 'bg-rose-400'}`} style={{ width: `${acc}%` }} />
E: {easy.correct}/{easy.attempted} M: {medium.correct}/{medium.attempted} H: {hard.correct}/{hard.attempted}
); })}
)}
{/* ── Account Settings ── */}

Change Password

Update your account password

{pwMsg && (
{pwMsg.type === 'success' ? : } {pwMsg.text}
)}
setCurrentPassword(e.target.value)} className="w-full px-3 py-2 pr-9 text-sm border border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-cyan-400" required />
setNewPassword(e.target.value)} className="w-full px-3 py-2 pr-9 text-sm border border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-cyan-400" required minLength={4} />
setConfirmPassword(e.target.value)} className="w-full px-3 py-2 text-sm border border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-cyan-400" required minLength={4} />
); }