import { useState, useEffect, useMemo } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import Layout from "@/components/Layout"; import { Button } from "@/components/ui/button"; import { useAuth } from "@/contexts/AuthContext"; import { aethexToast } from "@/lib/aethex-toast"; import { supabase } from "@/lib/supabase"; import { aethexProjectService, aethexAchievementService, } from "@/lib/aethex-database-adapter"; import { communityService } from "@/lib/supabase-service"; import PostComposer from "@/components/social/PostComposer"; import OAuthConnections, { ProviderDescriptor, ProviderKey, } from "@/components/settings/OAuthConnections"; import RealmSwitcher, { RealmKey } from "@/components/settings/RealmSwitcher"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Separator } from "@/components/ui/separator"; import LoadingScreen from "@/components/LoadingScreen"; import { Link } from "react-router-dom"; import { User, Settings, Activity, TrendingUp, Zap, Target, Users, Calendar, Bell, Star, Trophy, Rocket, Code, Database, Shield, ChevronRight, MoreHorizontal, Play, Pause, BarChart3, Github, Globe, } from "lucide-react"; export default function Dashboard() { const navigate = useNavigate(); const { user, profile, loading: authLoading, updateProfile, profileComplete, linkedProviders, linkProvider, unlinkProvider, } = useAuth(); const [displayName, setDisplayName] = useState(""); const [locationInput, setLocationInput] = useState(""); const [bio, setBio] = useState(""); const [website, setWebsite] = useState(""); const [linkedin, setLinkedin] = useState(""); const [github, setGithub] = useState(""); const [twitter, setTwitter] = useState(""); const [savingProfile, setSavingProfile] = useState(false); const [isLoading, setIsLoading] = useState(true); const [projects, setProjects] = useState([]); const [achievements, setAchievements] = useState([]); const [profileCompletion, setProfileCompletion] = useState(0); const [stats, setStats] = useState({ activeProjects: 0, completedTasks: 0, teamMembers: 0, performanceScore: "0%", }); const [userPosts, setUserPosts] = useState([]); const [applications, setApplications] = useState([]); const [connectionAction, setConnectionAction] = useState(null); const [userRealm, setUserRealm] = useState(null); const [experienceLevel, setExperienceLevel] = useState("beginner"); const [savingRealm, setSavingRealm] = useState(false); const [searchParams, setSearchParams] = useSearchParams(); const [activeTab, setActiveTab] = useState( () => searchParams.get("tab") ?? "profile", ); const currentStreak = profile?.current_streak ?? 0; const longestStreak = profile?.longest_streak ?? currentStreak; const streakLabel = `${currentStreak} day${currentStreak === 1 ? "" : "s"} streak`; const linkedProviderMap = useMemo(() => { const map: Record = {}; linkedProviders.forEach((lp) => { map[lp.provider] = lp; }); return map; }, [linkedProviders]); useEffect(() => { const paramTab = searchParams.get("tab") ?? "profile"; if (paramTab !== activeTab) { setActiveTab(paramTab); } }, [searchParams, activeTab]); useEffect(() => { if (typeof window === "undefined" || !user) { return; } const sanitized = new URLSearchParams(window.location.search); const keysToStrip = [ "code", "state", "scope", "auth_error", "error_description", "access_token", "refresh_token", "token_type", "provider", "type", ]; let mutated = false; keysToStrip.forEach((key) => { if (sanitized.has(key)) { sanitized.delete(key); mutated = true; } }); if (mutated) { setSearchParams(sanitized, { replace: true }); } }, [user?.id, setSearchParams]); const handleTabChange = (value: string) => { setActiveTab(value); const next = new URLSearchParams(searchParams.toString()); if (value === "profile") { if (next.has("tab")) { next.delete("tab"); setSearchParams(next, { replace: true }); } return; } if (next.get("tab") !== value) { next.set("tab", value); setSearchParams(next, { replace: true }); } }; const handleRealmSave = async () => { if (!user) { aethexToast.error({ title: "Not signed in", description: "Sign in to update your realm preferences.", }); return; } if (!userRealm) { aethexToast.error({ title: "Select a realm", description: "Choose a realm before saving your preferences.", }); return; } setSavingRealm(true); const payload = { user_type: userRealm, experience_level: experienceLevel || "beginner", } as any; try { await updateProfile(payload); computeProfileCompletion({ ...(profile as any), ...payload, }); aethexToast.success({ title: "Realm updated", description: "Your AeThex experience has been refreshed.", }); } catch (error: any) { console.error("Failed to save realm:", error); aethexToast.error({ title: "Unable to save realm", description: error?.message || "Please try again or refresh the page.", }); } finally { setSavingRealm(false); } }; const oauthConnections = useMemo( () => [ { provider: "google", name: "Google", description: "Link your Google account for one-click access.", Icon: Globe, gradient: "from-red-500 to-yellow-500", }, { provider: "github", name: "GitHub", description: "Connect your GitHub account to sync contributions.", Icon: Github, gradient: "from-gray-600 to-gray-900", }, ], [], ); useEffect(() => { console.log("Dashboard useEffect:", { user: !!user, profile: !!profile, authLoading, }); // Only redirect to login when auth is resolved and there's no user if (!user && !authLoading) { console.log("No user after auth resolved, redirecting to login"); setIsLoading(false); navigate("/login", { replace: true }); return; } // While auth is still resolving, keep showing loading state if (!user && authLoading) { setIsLoading(true); return; } if (user && profile) { console.log("User and profile exist, loading dashboard data"); loadDashboardData(); } else if (user && !profile && !authLoading) { console.log("User exists but no profile, clearing loading"); setIsLoading(false); } }, [user, profile, authLoading, navigate]); // Sync local settings form with profile useEffect(() => { setDisplayName(profile?.full_name || ""); setLocationInput(profile?.location || ""); setBio(profile?.bio || ""); setWebsite((profile as any)?.website_url || ""); setLinkedin(profile?.linkedin_url || ""); setGithub(profile?.github_url || ""); setTwitter(profile?.twitter_url || ""); setUserRealm(((profile as any)?.user_type as RealmKey) ?? null); setExperienceLevel((profile as any)?.experience_level || "beginner"); if (profile) computeProfileCompletion(profile); }, [profile]); const saveProfile = async () => { if (!user) return; setSavingProfile(true); try { await updateProfile({ full_name: displayName, location: locationInput, bio, website_url: website as any, linkedin_url: linkedin, github_url: github, twitter_url: twitter, } as any); } finally { setSavingProfile(false); } }; const computeProfileCompletion = (p: any) => { const checks = [ !!p?.full_name, !!p?.bio, !!p?.location, !!p?.avatar_url, !!p?.banner_url, !!(p?.website_url || p?.github_url || p?.linkedin_url || p?.twitter_url), ]; const pct = Math.round( (checks.filter(Boolean).length / checks.length) * 100, ); setProfileCompletion(pct); }; const hasRealmChanges = useMemo(() => { const currentRealm = ((profile as any)?.user_type as RealmKey) ?? null; const currentExperience = ((profile as any)?.experience_level as string) || "beginner"; const selectedRealmValue = userRealm ?? null; const selectedExperienceValue = experienceLevel || "beginner"; return ( selectedRealmValue !== currentRealm || selectedExperienceValue !== currentExperience ); }, [profile, userRealm, experienceLevel]); const loadDashboardData = async () => { try { setIsLoading(true); // Load user's projects with error handling let userProjects = []; try { userProjects = await aethexProjectService.getUserProjects(user!.id); setProjects(userProjects); } catch (projectError) { console.warn("Could not load projects:", projectError); setProjects([]); } // Load user's recent posts try { const posts = await communityService.getUserPosts(user!.id); setUserPosts(posts.slice(0, 5)); } catch (e) { console.warn("Could not load user posts:", e); setUserPosts([]); } // Load project applications (if table exists) try { const { data, error } = await supabase .from("project_applications") .select(`*, projects!inner(id, title, user_id)`) .eq("projects.user_id", user!.id) .order("created_at", { ascending: false }) .limit(10); if (!error && Array.isArray(data)) setApplications(data); else setApplications([]); } catch (e) { console.warn("Applications fetch skipped or failed:", e); setApplications([]); } // Check and award project-related achievements, then load achievements try { await aethexAchievementService.checkAndAwardProjectAchievements( user!.id, ); } catch (e) { console.warn("checkAndAwardProjectAchievements failed:", e); } // Load user's achievements with error handling let userAchievements = []; try { userAchievements = await aethexAchievementService.getUserAchievements( user!.id, ); setAchievements(userAchievements); } catch (achievementError) { console.warn("Could not load achievements:", achievementError); setAchievements([]); } // Load follower count for real collaboration insight let followerCount = 0; try { const { count, error } = await supabase .from("user_follows") .select("id", { count: "exact", head: true }) .eq("following_id", user!.id); if (!error && typeof count === "number") { followerCount = count; } } catch (e) { console.warn("Could not load follower count:", e); } // Calculate stats (treat planning and in_progress as active) const activeCount = userProjects.filter( (p) => p.status === "in_progress" || p.status === "planning", ).length; const completedCount = userProjects.filter( (p) => p.status === "completed", ).length; const totalXp = typeof (profile as any)?.total_xp === "number" ? (profile as any).total_xp : 0; const performanceBase = 60 + activeCount * 5 + completedCount * 8 + userAchievements.length * 3; const performanceScore = Math.min( 100, Math.round(performanceBase + totalXp / 150), ); setStats({ activeProjects: activeCount, completedTasks: completedCount, teamMembers: followerCount, performanceScore: `${performanceScore}%`, }); } catch (error) { console.error("Error loading dashboard data:", error); aethexToast.error({ title: "Failed to load dashboard", description: "Please try refreshing the page", }); } finally { setIsLoading(false); } }; const handleLinkProvider = async (provider: ProviderKey) => { setConnectionAction(`${provider}-link`); try { await linkProvider(provider); } finally { setConnectionAction(null); } }; const handleUnlinkProvider = async (provider: ProviderKey) => { setConnectionAction(`${provider}-unlink`); try { await unlinkProvider(provider); } finally { setConnectionAction(null); } }; const handleQuickAction = async (actionTitle: string) => { switch (actionTitle) { case "Start New Project": navigate("/projects/new"); break; case "Join Team": navigate("/community"); break; case "Access Labs": navigate("/research"); break; case "View Analytics": aethexToast.info({ title: "Analytics", description: "Analytics dashboard coming soon!", }); break; default: aethexToast.info({ title: actionTitle, description: "Feature coming soon!", }); } }; // If no user and auth is resolved, let the redirect happen without flashing a loader if (!user && !authLoading) { return null; } // Show loading only while auth or data is loading if (authLoading || isLoading) { return ( ); } // Hide setup banner once onboarding is complete const showProfileSetup = !profileComplete; const statsDisplay = [ { label: "Active Projects", value: stats.activeProjects, icon: Rocket, color: "from-blue-500 to-purple-600", }, { label: "Completed Tasks", value: 47, icon: Trophy, color: "from-green-500 to-blue-600", }, { label: "Team Members", value: 8, icon: Users, color: "from-purple-500 to-pink-600", }, { label: "Performance Score", value: "94%", icon: TrendingUp, color: "from-orange-500 to-red-600", }, ]; const getProgressPercentage = (project: any) => { switch (project.status) { case "planning": return 20; case "in_progress": return 60; case "completed": return 100; default: return 0; } }; const getPriorityFromTech = (technologies: string[]) => { if ( technologies.some( (tech) => tech.toLowerCase().includes("ai") || tech.toLowerCase().includes("blockchain"), ) ) { return "High"; } return "Medium"; }; const getAchievementIcon = (iconName: string) => { switch (iconName.toLowerCase()) { case "code": return Code; case "users": return Users; case "rocket": return Rocket; case "database": return Database; case "shield": return Shield; case "trophy": return Trophy; default: return Star; } }; const quickActions = [ { title: "Start New Project", icon: Rocket, color: "from-blue-500 to-purple-600", }, { title: "Join Team", icon: Users, color: "from-green-500 to-blue-600" }, { title: "Access Labs", icon: Zap, color: "from-yellow-500 to-orange-600" }, { title: "View Analytics", icon: BarChart3, color: "from-purple-500 to-pink-600", }, ]; if (isLoading) { return ( ); } return (
{/* Profile Setup Banner */} {showProfileSetup && (

Complete Your Profile

Set up your profile to unlock all features

)} {/* Header */}

Welcome back,{" "} {profile?.full_name || user.email?.split("@")[0]}

{profile?.role || "Member"} • Level {profile?.level || 1} •{" "} {streakLabel} 🔥

{longestStreak > 0 && (
Longest streak: {longestStreak}d
)}
Profile {profileCompletion}% complete
{/* Left Sidebar - User Profile */}
{/* Profile Card */}
User Avatar

{profile?.full_name || user.email?.split("@")[0]}

{profile?.role || "Member"}

Level {profile?.level || 1} {profileComplete && ( Profile Complete )}
{/* XP Progress */}
XP Progress {profile?.total_xp || 0} /{" "} {(profile?.level || 1) * 1000}
{/* Quick Actions */} Quick Actions {quickActions.map((action, index) => { const Icon = action.icon; return ( ); })}
{/* Main Content */}
{/* Stats Grid */}
{statsDisplay.map((stat, index) => { const Icon = stat.icon; return (

{stat.label}

{stat.value}

); })}
{/* Central Post Composer */} Create a Post Share updates, images, or videos {/* Settings Section */} Account Settings Manage your profile, notifications, and privacy Profile Connections Notifications Privacy
setDisplayName(e.target.value)} />
setLocationInput(e.target.value)} />
{ const file = e.target.files?.[0]; if (!file || !user) return; const storeDataUrl = () => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result as string); reader.onerror = () => reject(new Error("Failed to read file")); reader.readAsDataURL(file); }); try { const path = `${user.id}/avatar-${Date.now()}-${file.name}`; const { error } = await supabase.storage .from("avatars") .upload(path, file, { upsert: true }); if (error) throw error; const { data } = supabase.storage .from("avatars") .getPublicUrl(path); await updateProfile({ avatar_url: data.publicUrl, } as any); computeProfileCompletion({ ...(profile as any), avatar_url: data.publicUrl, }); aethexToast.success({ title: "Avatar updated", }); } catch (err: any) { try { const dataUrl = await storeDataUrl(); await updateProfile({ avatar_url: dataUrl, } as any); computeProfileCompletion({ ...(profile as any), avatar_url: dataUrl, }); aethexToast.success({ title: "Avatar saved (local)", }); } catch (e: any) { aethexToast.error({ title: "Upload failed", description: err?.message || "Unable to upload image", }); } } }} />
{ const file = e.target.files?.[0]; if (!file || !user) return; const storeDataUrl = () => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result as string); reader.onerror = () => reject(new Error("Failed to read file")); reader.readAsDataURL(file); }); try { const path = `${user.id}/banner-${Date.now()}-${file.name}`; const { error } = await supabase.storage .from("banners") .upload(path, file, { upsert: true }); if (error) throw error; const { data } = supabase.storage .from("banners") .getPublicUrl(path); await updateProfile({ banner_url: data.publicUrl, } as any); computeProfileCompletion({ ...(profile as any), banner_url: data.publicUrl, }); aethexToast.success({ title: "Banner updated", }); } catch (err: any) { try { const dataUrl = await storeDataUrl(); await updateProfile({ banner_url: dataUrl, } as any); computeProfileCompletion({ ...(profile as any), banner_url: dataUrl, }); aethexToast.success({ title: "Banner saved (local)", }); } catch (e: any) { aethexToast.error({ title: "Upload failed", description: err?.message || "Unable to upload image", }); } } }} />