import { useEffect, useState, useCallback, useRef, useMemo, type MouseEvent } from "react"; import { useDiscordActivity } from "@/contexts/DiscordActivityContext"; import LoadingScreen from "@/components/LoadingScreen"; import { supabase } from "@/lib/supabase"; import { Heart, MessageCircle, Zap, Gamepad2, Briefcase, BookOpen, Sparkles, Shield, ExternalLink, Flame, Star, Target, Gift, CheckCircle, AlertCircle, Loader2, ChevronRight, Users, TrendingUp, Calendar, Award, X, Send, MessagesSquare, BarChart3, Plus, Vote, Trophy, Clock, ThumbsUp, Layers, Eye, UserPlus, Crosshair, Crown, Dice6, Video, Mic, MapPin, FileText, } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; const APP_URL = "https://aethex.dev"; type ArmType = "labs" | "gameforge" | "corp" | "foundation" | "nexus" | "staff"; const ARM_CONFIG: Record = { labs: { label: "Labs", icon: Zap, color: "#facc15", gradient: "from-yellow-500/20 via-amber-500/10 to-transparent", glow: "shadow-yellow-500/30" }, gameforge: { label: "GameForge", icon: Gamepad2, color: "#4ade80", gradient: "from-green-500/20 via-emerald-500/10 to-transparent", glow: "shadow-green-500/30" }, corp: { label: "Corp", icon: Briefcase, color: "#60a5fa", gradient: "from-blue-500/20 via-sky-500/10 to-transparent", glow: "shadow-blue-500/30" }, foundation: { label: "Foundation", icon: BookOpen, color: "#f87171", gradient: "from-red-500/20 via-rose-500/10 to-transparent", glow: "shadow-red-500/30" }, nexus: { label: "Nexus", icon: Sparkles, color: "#c084fc", gradient: "from-purple-500/20 via-violet-500/10 to-transparent", glow: "shadow-purple-500/30" }, staff: { label: "Staff", icon: Shield, color: "#818cf8", gradient: "from-indigo-500/20 via-blue-500/10 to-transparent", glow: "shadow-indigo-500/30" }, }; interface Post { id: string; title: string; content: string; arm_affiliation: ArmType; author_id: string; created_at: string; likes_count: number; comments_count: number; tags?: string[]; user_profiles?: { id: string; username?: string; full_name?: string; avatar_url?: string }; } interface LeaderboardEntry { rank: number; user_id: string; username: string; avatar_url?: string; total_xp: number; level: number; current_streak?: number; } interface UserStats { total_xp: number; level: number; current_streak: number; longest_streak: number; rank?: number; } function XPRing({ xp, level, size = 64, strokeWidth = 4, color }: { xp: number; level: number; size?: number; strokeWidth?: number; color: string }) { const xpForLevel = 1000; const xpInCurrentLevel = xp % xpForLevel; const progress = xpInCurrentLevel / xpForLevel; const radius = (size - strokeWidth) / 2; const circumference = 2 * Math.PI * radius; const strokeDashoffset = circumference * (1 - progress); return (
{level}
); } function XPGainAnimation({ amount, onComplete }: { amount: number; onComplete: () => void }) { useEffect(() => { const timer = setTimeout(onComplete, 2000); return () => clearTimeout(timer); }, [onComplete]); return ( +{amount} XP ); } function ConfettiEffect() { const colors = ["#facc15", "#4ade80", "#60a5fa", "#c084fc", "#f87171"]; return (
{Array.from({ length: 50 }).map((_, i) => ( 0.5 ? 1 : -1), x: (Math.random() - 0.5) * 200 }} transition={{ duration: 2 + Math.random() * 2, delay: Math.random() * 0.5, ease: "easeOut" }} /> ))}
); } function FeedTab({ openExternalLink, userId }: { openExternalLink: (url: string) => Promise; userId?: string }) { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [likingPost, setLikingPost] = useState(null); const [likedPosts, setLikedPosts] = useState>(new Set()); const fetchPosts = useCallback(async () => { setLoading(true); setError(null); try { const response = await fetch("/api/feed?limit=10"); if (!response.ok) throw new Error("Failed to load"); const data = await response.json(); setPosts(data.data || []); } catch { setError("Couldn't load feed"); } finally { setLoading(false); } }, []); const handleQuickLike = async (postId: string, e: MouseEvent) => { e.stopPropagation(); if (!userId || likingPost) return; setLikingPost(postId); try { const response = await fetch(`/api/feed/${postId}/like`, { method: "POST", headers: { "Content-Type": "application/json" }, }); if (response.ok) { setLikedPosts(prev => new Set(prev).add(postId)); setPosts(prev => prev.map(p => p.id === postId ? { ...p, likes_count: p.likes_count + 1 } : p )); } } catch { // Silent fail for likes } finally { setLikingPost(null); } }; useEffect(() => { fetchPosts(); }, [fetchPosts]); if (loading) return (
); if (error) return (

{error}

); return ( {posts.length === 0 ? ( ) : ( posts.map((post, index) => { const config = ARM_CONFIG[post.arm_affiliation] || ARM_CONFIG.nexus; const isLiked = likedPosts.has(post.id); const isLiking = likingPost === post.id; return ( {post.comments_count} ); }) )} ); } function RealmsTab({ currentRealm, openExternalLink }: { currentRealm: ArmType; openExternalLink: (url: string) => Promise }) { const realms = Object.entries(ARM_CONFIG) as [ArmType, typeof ARM_CONFIG[ArmType]][]; return (
{realms.map(([key, config], index) => { const Icon = config.icon; const isActive = currentRealm === key; return ( openExternalLink(`${APP_URL}/${key}`)} initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: index * 0.05 }} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} className={`p-3 rounded-xl transition-all flex items-center gap-3 border ${ isActive ? `bg-gradient-to-br ${config.gradient} border-[${config.color}]/30 shadow-lg ${config.glow}` : "bg-[#232428] hover:bg-[#2b2d31] border-[#3f4147]" }`} >
{config.label} {isActive && }
); })}

Tap to explore each realm

); } function LeaderboardTab({ openExternalLink, currentUserId }: { openExternalLink: (url: string) => Promise; currentUserId?: string }) { const [leaderboard, setLeaderboard] = useState([]); const [loading, setLoading] = useState(true); const [userRank, setUserRank] = useState(null); useEffect(() => { const fetchLeaderboard = async () => { try { const response = await fetch('/api/activity/leaderboard'); if (response.ok) { const data = await response.json(); const entries = (data.data || data || []).map((e: any, i: number) => ({ rank: i + 1, user_id: e.user_id || e.id, username: e.username || e.full_name || 'Anonymous', avatar_url: e.avatar_url, total_xp: e.total_xp || 0, level: e.level || Math.floor((e.total_xp || 0) / 1000) + 1, current_streak: e.current_streak || 0, })); setLeaderboard(entries); if (currentUserId) { const userIdx = entries.findIndex((e: any) => e.user_id === currentUserId); if (userIdx >= 0) setUserRank(userIdx + 1); } } } catch { setLeaderboard([]); } finally { setLoading(false); } }; fetchLeaderboard(); }, [currentUserId]); const medals = ["๐Ÿฅ‡", "๐Ÿฅˆ", "๐Ÿฅ‰"]; if (loading) { return (
); } return (
Your Rank {userRank ? `#${userRank}` : 'Unranked'}

Keep earning XP to climb!

{leaderboard.length === 0 && (

No leaderboard data yet

)} {leaderboard.map((entry, index) => ( {medals[index] || `#${index + 1}`} {entry.avatar_url ? ( ) : (
{entry.username?.[0]?.toUpperCase() || "?"}
)}

{entry.username}

Lvl {entry.level} ยท {entry.total_xp.toLocaleString()} XP

{entry.current_streak && entry.current_streak > 0 && ( {entry.current_streak} )}
))}
); } function QuestsTab({ userId, onXPGain }: { userId?: string; onXPGain: (amount: number) => void }) { const [dailyClaimed, setDailyClaimed] = useState(() => { try { const today = new Date().toISOString().slice(0, 10); const lastClaim = localStorage.getItem('aethex_daily_claim'); return lastClaim === today; } catch { return false; } }); const [claiming, setClaiming] = useState(false); const claimDailyXP = () => { if (claiming || dailyClaimed) return; setClaiming(true); setTimeout(() => { try { const today = new Date().toISOString().slice(0, 10); localStorage.setItem('aethex_daily_claim', today); } catch {} setDailyClaimed(true); onXPGain(25); setClaiming(false); }, 500); }; const quests = [ { id: "daily", title: "Daily Login", description: "Log in today", xp: 25, icon: Calendar, canClaim: !dailyClaimed, onClaim: claimDailyXP, claiming }, { id: "post", title: "Share Your Work", description: "Create a post", xp: 20, icon: Star, progress: 0, total: 1 }, { id: "like", title: "Show Support", description: "Like 5 posts", xp: 15, icon: Heart, progress: 3, total: 5 }, { id: "explore", title: "Realm Explorer", description: "Visit 3 realms", xp: 30, icon: Sparkles, progress: 2, total: 3 }, ]; return (
Daily Quests Resets at midnight UTC
{quests.map((quest, index) => { const Icon = quest.icon; const isCompleted = quest.progress !== undefined && quest.progress >= (quest.total || 1); return (
{quest.title} +{quest.xp} XP

{quest.description}

{quest.progress !== undefined && quest.total && (
{quest.progress}/{quest.total}
)}
{quest.canClaim !== undefined && ( {quest.claiming ? ( ) : quest.canClaim ? ( "Claim" ) : ( )} )} {isCompleted && !quest.canClaim && ( )}
); })}
); } function JobsTab({ openExternalLink, userId }: { openExternalLink: (url: string) => Promise; userId?: string }) { const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const [appliedJobs, setAppliedJobs] = useState>(() => { try { const saved = localStorage.getItem('aethex_job_applications'); return saved ? new Set(JSON.parse(saved)) : new Set(); } catch { return new Set(); } }); const [applyingTo, setApplyingTo] = useState(null); const [showApplyModal, setShowApplyModal] = useState(null); const [applyMessage, setApplyMessage] = useState(""); const [applySuccess, setApplySuccess] = useState(null); useEffect(() => { const fetchJobs = async () => { try { const response = await fetch("/api/opportunities?limit=5&status=active"); if (response.ok) { const data = await response.json(); setJobs(data.data || []); } } catch { setJobs([]); } finally { setLoading(false); } }; fetchJobs(); }, []); const handleQuickApply = (jobId: string, e: MouseEvent) => { e.stopPropagation(); if (!userId) return; setShowApplyModal(jobId); setApplyMessage(""); }; const submitApplication = () => { if (!showApplyModal || !userId) return; setApplyingTo(showApplyModal); setTimeout(() => { const newApplied = new Set(appliedJobs).add(showApplyModal); setAppliedJobs(newApplied); try { localStorage.setItem('aethex_job_applications', JSON.stringify([...newApplied])); } catch {} setApplySuccess(showApplyModal); setApplyingTo(null); setShowApplyModal(null); setApplyMessage(""); setTimeout(() => setApplySuccess(null), 3000); }, 800); }; const categories = [ { label: "Full-Time", icon: Briefcase, color: "#60a5fa" }, { label: "Contract", icon: Target, color: "#4ade80" }, { label: "Freelance", icon: Star, color: "#facc15" }, ]; if (loading) return (
); const selectedJob = jobs.find(j => j.id === showApplyModal); return ( {applySuccess && ( Application submitted! )} {showApplyModal && selectedJob && ( setShowApplyModal(null)} > e.stopPropagation()} className="bg-[#2b2d31] rounded-2xl p-5 w-full max-w-sm border border-[#3f4147] shadow-2xl" >

Quick Apply

{selectedJob.title}

{selectedJob.company_name || "Remote Opportunity"}