mirror of
https://github.com/th30d4y/OpenLearnX.git
synced 2026-05-26 11:25:49 +00:00
update error
This commit is contained in:
+353
-100
@@ -1,121 +1,374 @@
|
||||
"use client"
|
||||
|
||||
import { useAuth } from "@/context/auth-context"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useAuth } from "@/context/auth-context"
|
||||
import { DashboardStatsOverview } from "@/components/dashboard-stats"
|
||||
import { Loader2, AlertCircle } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
User,
|
||||
LogOut,
|
||||
Settings,
|
||||
Trophy,
|
||||
BookOpen,
|
||||
Target,
|
||||
TrendingUp,
|
||||
Wallet,
|
||||
Mail,
|
||||
Calendar,
|
||||
Award,
|
||||
BarChart3,
|
||||
Activity,
|
||||
Edit3,
|
||||
Save,
|
||||
X
|
||||
} from "lucide-react"
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { isLoadingAuth, walletConnected, walletAddress, firebaseUser, authMethod } = useAuth()
|
||||
const { user, firebaseUser, walletConnected, logout, authMethod } = useAuth()
|
||||
const router = useRouter()
|
||||
const [showDashboard, setShowDashboard] = useState(false)
|
||||
const [debugInfo, setDebugInfo] = useState<any>(null)
|
||||
const [isEditingProfile, setIsEditingProfile] = useState(false)
|
||||
const [profileData, setProfileData] = useState({
|
||||
name: user?.name || '',
|
||||
bio: user?.bio || '',
|
||||
avatar: user?.avatar || ''
|
||||
})
|
||||
|
||||
const [stats, setStats] = useState({
|
||||
coursesCompleted: 12,
|
||||
totalXP: 2450,
|
||||
currentStreak: 7,
|
||||
rank: 156,
|
||||
certificatesEarned: 3,
|
||||
hoursLearned: 45
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
// Debug authentication state
|
||||
const authState = {
|
||||
isLoadingAuth,
|
||||
walletConnected,
|
||||
walletAddress: !!walletAddress,
|
||||
firebaseUser: !!firebaseUser,
|
||||
authMethod,
|
||||
localStorage: {
|
||||
token: !!localStorage.getItem('openlearnx_jwt_token'),
|
||||
wallet: !!localStorage.getItem('openlearnx_wallet'),
|
||||
user: !!localStorage.getItem('openlearnx_user')
|
||||
}
|
||||
if (!user && !firebaseUser) {
|
||||
router.replace("/auth/login")
|
||||
}
|
||||
|
||||
setDebugInfo(authState)
|
||||
console.log('📊 Dashboard auth state:', authState)
|
||||
}, [user, firebaseUser, router])
|
||||
|
||||
// Give auth some time to initialize
|
||||
const timer = setTimeout(() => {
|
||||
const isAuthenticated = (walletConnected && walletAddress) || firebaseUser
|
||||
|
||||
if (isAuthenticated) {
|
||||
console.log('✅ User authenticated, showing dashboard')
|
||||
setShowDashboard(true)
|
||||
} else if (!isLoadingAuth) {
|
||||
console.log('❌ User not authenticated, redirecting to login')
|
||||
router.replace("/auth/login")
|
||||
}
|
||||
}, 2000) // Wait 2 seconds for auth to stabilize
|
||||
const handleProfileUpdate = async () => {
|
||||
try {
|
||||
// Here you would call your API to update profile
|
||||
// await updateProfile(profileData)
|
||||
setIsEditingProfile(false)
|
||||
console.log("Profile updated:", profileData)
|
||||
} catch (error) {
|
||||
console.error("Failed to update profile:", error)
|
||||
}
|
||||
}
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [isLoadingAuth, walletConnected, walletAddress, firebaseUser, authMethod, router])
|
||||
|
||||
// Show loading state
|
||||
if (isLoadingAuth || !showDashboard) {
|
||||
if (!user && !firebaseUser) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 dark:from-gray-900 dark:via-blue-900 dark:to-purple-900">
|
||||
<div className="text-center space-y-4 max-w-md mx-auto p-6">
|
||||
<Loader2 className="w-12 h-12 animate-spin mx-auto text-purple-600" />
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100">
|
||||
Loading Dashboard...
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
{walletConnected ? `Connected to ${walletAddress?.slice(0, 6)}...${walletAddress?.slice(-4)}` :
|
||||
firebaseUser ? `Logged in as ${firebaseUser.email}` :
|
||||
'Verifying authentication...'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Debug info in development */}
|
||||
{process.env.NODE_ENV === 'development' && debugInfo && (
|
||||
<details className="text-left text-xs text-gray-500 bg-gray-100 dark:bg-gray-800 p-2 rounded mt-4">
|
||||
<summary>Debug Info</summary>
|
||||
<pre>{JSON.stringify(debugInfo, null, 2)}</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-indigo-600"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Show error state if no auth after loading
|
||||
if (!walletConnected && !firebaseUser && !isLoadingAuth) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 dark:from-gray-900 dark:via-blue-900 dark:to-purple-900">
|
||||
<div className="text-center space-y-4 max-w-md mx-auto p-6">
|
||||
<AlertCircle className="w-16 h-16 text-red-500 mx-auto" />
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100">
|
||||
Authentication Required
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
Please log in to access your dashboard.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<Button onClick={() => router.push("/auth/login")} className="w-full">
|
||||
Go to Login
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
localStorage.clear()
|
||||
window.location.href = "/auth/login"
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
Clear Data & Login
|
||||
</Button>
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50">
|
||||
{/* Professional Header */}
|
||||
<header className="bg-white shadow-lg border-b border-gray-100">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 bg-gradient-to-r from-indigo-600 via-purple-600 to-blue-600 rounded-xl flex items-center justify-center shadow-lg">
|
||||
<BookOpen className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
|
||||
OpenLearnX
|
||||
</h1>
|
||||
<p className="text-xs text-gray-500">Learn • Earn • Grow</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<button className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-xl transition-all duration-200">
|
||||
<Settings className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={logout}
|
||||
className="flex items-center space-x-2 px-4 py-2 text-red-600 hover:text-white hover:bg-red-600 rounded-xl transition-all duration-200 border border-red-200 hover:border-red-600"
|
||||
>
|
||||
<LogOut className="w-4 h-4" />
|
||||
<span className="text-sm font-medium">Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Debug info */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<details className="text-left text-xs text-gray-500 bg-gray-100 dark:bg-gray-800 p-2 rounded mt-4">
|
||||
<summary>Debug Info</summary>
|
||||
<pre>{JSON.stringify(debugInfo, null, 2)}</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</header>
|
||||
|
||||
// Show dashboard if authenticated
|
||||
return <DashboardStatsOverview />
|
||||
{/* Main Dashboard Content */}
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{/* Welcome Section */}
|
||||
<div className="mb-8">
|
||||
<div className="bg-gradient-to-r from-indigo-600 via-purple-600 to-blue-600 rounded-2xl p-8 text-white shadow-xl">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold mb-2">
|
||||
Welcome back! 👋
|
||||
</h2>
|
||||
<p className="text-indigo-100 text-lg">
|
||||
Ready to continue your learning journey?
|
||||
</p>
|
||||
{authMethod === "metamask" && user ? (
|
||||
<div className="mt-3 flex items-center space-x-2">
|
||||
<Wallet className="w-4 h-4 text-orange-300" />
|
||||
<span className="text-sm text-indigo-100">
|
||||
Connected: {user.wallet_address.slice(0, 6)}...{user.wallet_address.slice(-4)}
|
||||
</span>
|
||||
</div>
|
||||
) : firebaseUser && (
|
||||
<div className="mt-3 flex items-center space-x-2">
|
||||
<Mail className="w-4 h-4 text-blue-300" />
|
||||
<span className="text-sm text-indigo-100">
|
||||
{firebaseUser.email}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<div className="w-32 h-32 bg-white/10 rounded-full flex items-center justify-center backdrop-blur-sm">
|
||||
<Trophy className="w-16 h-16 text-yellow-300" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-100 p-6 hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-gray-600 uppercase tracking-wide">Total XP</p>
|
||||
<p className="text-3xl font-bold text-gray-900 mt-1">{stats.totalXP.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="p-4 bg-gradient-to-r from-indigo-500 to-purple-500 rounded-xl shadow-lg">
|
||||
<Trophy className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mt-4">
|
||||
<TrendingUp className="w-4 h-4 text-green-500 mr-2" />
|
||||
<span className="text-sm text-green-600 font-medium">+12% from last week</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-100 p-6 hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-gray-600 uppercase tracking-wide">Courses</p>
|
||||
<p className="text-3xl font-bold text-gray-900 mt-1">{stats.coursesCompleted}</p>
|
||||
</div>
|
||||
<div className="p-4 bg-gradient-to-r from-green-500 to-teal-500 rounded-xl shadow-lg">
|
||||
<BookOpen className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mt-4">
|
||||
<Activity className="w-4 h-4 text-blue-500 mr-2" />
|
||||
<span className="text-sm text-blue-600 font-medium">3 in progress</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-100 p-6 hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-gray-600 uppercase tracking-wide">Streak</p>
|
||||
<p className="text-3xl font-bold text-gray-900 mt-1">{stats.currentStreak} days</p>
|
||||
</div>
|
||||
<div className="p-4 bg-gradient-to-r from-orange-500 to-red-500 rounded-xl shadow-lg">
|
||||
<Target className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mt-4">
|
||||
<span className="text-sm text-orange-600 font-medium">🔥 Keep it up!</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-100 p-6 hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-gray-600 uppercase tracking-wide">Global Rank</p>
|
||||
<p className="text-3xl font-bold text-gray-900 mt-1">#{stats.rank}</p>
|
||||
</div>
|
||||
<div className="p-4 bg-gradient-to-r from-purple-500 to-pink-500 rounded-xl shadow-lg">
|
||||
<BarChart3 className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mt-4">
|
||||
<Award className="w-4 h-4 text-purple-500 mr-2" />
|
||||
<span className="text-sm text-purple-600 font-medium">Top 5% learner</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Profile Card with Edit Functionality */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-100 p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h3 className="text-xl font-bold text-gray-900">Profile</h3>
|
||||
<button
|
||||
onClick={() => setIsEditingProfile(!isEditingProfile)}
|
||||
className="p-2 text-gray-500 hover:text-indigo-600 hover:bg-indigo-50 rounded-lg transition-all duration-200"
|
||||
>
|
||||
{isEditingProfile ? <X className="w-5 h-5" /> : <Edit3 className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="text-center mb-6">
|
||||
<div className="w-20 h-20 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full flex items-center justify-center mx-auto mb-4 shadow-lg">
|
||||
<User className="w-10 h-10 text-white" />
|
||||
</div>
|
||||
{isEditingProfile ? (
|
||||
<div className="space-y-3">
|
||||
<input
|
||||
type="text"
|
||||
value={profileData.name}
|
||||
onChange={(e) => setProfileData({...profileData, name: e.target.value})}
|
||||
placeholder="Your name"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-center"
|
||||
/>
|
||||
<textarea
|
||||
value={profileData.bio}
|
||||
onChange={(e) => setProfileData({...profileData, bio: e.target.value})}
|
||||
placeholder="Your bio"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-center h-20 resize-none"
|
||||
/>
|
||||
<button
|
||||
onClick={handleProfileUpdate}
|
||||
className="flex items-center space-x-2 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors mx-auto"
|
||||
>
|
||||
<Save className="w-4 h-4" />
|
||||
<span>Save</span>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<h4 className="text-lg font-semibold text-gray-900">
|
||||
{profileData.name || "Your Name"}
|
||||
</h4>
|
||||
<p className="text-gray-600 text-sm mt-1">
|
||||
{profileData.bio || "Add a bio to tell others about yourself"}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-4 bg-gradient-to-r from-indigo-50 to-purple-50 rounded-xl border border-indigo-100">
|
||||
<div className="flex items-center space-x-3">
|
||||
{authMethod === "metamask" ? (
|
||||
<Wallet className="w-6 h-6 text-orange-600" />
|
||||
) : (
|
||||
<Mail className="w-6 h-6 text-blue-600" />
|
||||
)}
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-gray-900">Auth Method</p>
|
||||
<p className="text-xs text-gray-600">
|
||||
{authMethod === "metamask" ? "MetaMask Wallet" : "Email Account"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
|
||||
<span className="text-xs text-green-600 font-medium">Connected</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-4 bg-blue-50 rounded-xl">
|
||||
<Calendar className="w-6 h-6 text-blue-600 mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold text-blue-900">{stats.hoursLearned}</p>
|
||||
<p className="text-xs text-blue-600 font-medium">Hours Learned</p>
|
||||
</div>
|
||||
<div className="text-center p-4 bg-green-50 rounded-xl">
|
||||
<Award className="w-6 h-6 text-green-600 mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold text-green-900">{stats.certificatesEarned}</p>
|
||||
<p className="text-xs text-green-600 font-medium">Certificates</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Activity */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-100 p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h3 className="text-xl font-bold text-gray-900">Recent Activity</h3>
|
||||
<button className="text-sm text-indigo-600 hover:text-indigo-800 font-semibold hover:bg-indigo-50 px-3 py-1 rounded-lg transition-all duration-200">
|
||||
View all →
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{[
|
||||
{
|
||||
type: "course",
|
||||
title: "Completed React Fundamentals",
|
||||
time: "2 hours ago",
|
||||
icon: BookOpen,
|
||||
color: "green",
|
||||
bgColor: "bg-green-100",
|
||||
textColor: "text-green-600"
|
||||
},
|
||||
{
|
||||
type: "quiz",
|
||||
title: "Scored 95% on JavaScript Quiz",
|
||||
time: "1 day ago",
|
||||
icon: Award,
|
||||
color: "blue",
|
||||
bgColor: "bg-blue-100",
|
||||
textColor: "text-blue-600"
|
||||
},
|
||||
{
|
||||
type: "streak",
|
||||
title: "7-day learning streak!",
|
||||
time: "Today",
|
||||
icon: Target,
|
||||
color: "orange",
|
||||
bgColor: "bg-orange-100",
|
||||
textColor: "text-orange-600"
|
||||
},
|
||||
{
|
||||
type: "rank",
|
||||
title: "Moved up 5 positions in leaderboard",
|
||||
time: "2 days ago",
|
||||
icon: TrendingUp,
|
||||
color: "purple",
|
||||
bgColor: "bg-purple-100",
|
||||
textColor: "text-purple-600"
|
||||
},
|
||||
].map((activity, index) => (
|
||||
<div key={index} className="flex items-center space-x-4 p-4 hover:bg-gray-50 rounded-xl transition-all duration-200 border border-gray-100 hover:border-gray-200 hover:shadow-md">
|
||||
<div className={`p-3 rounded-xl ${activity.bgColor} shadow-sm`}>
|
||||
<activity.icon className={`w-5 h-5 ${activity.textColor}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-semibold text-gray-900">{activity.title}</p>
|
||||
<p className="text-xs text-gray-500 mt-1">{activity.time}</p>
|
||||
</div>
|
||||
<div className="w-2 h-2 bg-gray-300 rounded-full"></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 p-4 bg-gradient-to-r from-indigo-50 to-purple-50 rounded-xl border border-indigo-100">
|
||||
<h4 className="text-sm font-semibold text-indigo-900 mb-2">🚀 Keep Learning!</h4>
|
||||
<p className="text-xs text-indigo-700">
|
||||
You're doing great! Complete 2 more courses this week to maintain your streak.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user