Create Foundation Achievements display page
cgen-36ccfdce889c49dabcd7084b76cc08c6
This commit is contained in:
parent
77067d1b1f
commit
eaf7402fce
1 changed files with 336 additions and 0 deletions
336
client/pages/foundation/FoundationAchievements.tsx
Normal file
336
client/pages/foundation/FoundationAchievements.tsx
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import Layout from "@/components/Layout";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ArrowLeft, Award, Loader2, Trophy } from "lucide-react";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
|
||||
interface Achievement {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
points: number;
|
||||
difficulty: "bronze" | "silver" | "gold" | "platinum";
|
||||
category: string;
|
||||
earned_count: number;
|
||||
}
|
||||
|
||||
const difficultyColors: Record<
|
||||
"bronze" | "silver" | "gold" | "platinum",
|
||||
string
|
||||
> = {
|
||||
bronze: "bg-amber-700/20 text-amber-600",
|
||||
silver: "bg-slate-400/20 text-slate-300",
|
||||
gold: "bg-yellow-500/20 text-yellow-400",
|
||||
platinum: "bg-purple-500/20 text-purple-400",
|
||||
};
|
||||
|
||||
const mockAchievements: Achievement[] = [
|
||||
{
|
||||
id: "1",
|
||||
name: "First Course Completed",
|
||||
description: "Complete your first Foundation course",
|
||||
icon: "🎓",
|
||||
points: 50,
|
||||
difficulty: "bronze",
|
||||
category: "Learning",
|
||||
earned_count: 1240,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Master Mathematician",
|
||||
description: "Complete all mathematics courses",
|
||||
icon: "🧮",
|
||||
points: 200,
|
||||
difficulty: "silver",
|
||||
category: "Specialty",
|
||||
earned_count: 342,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Game Developer Pro",
|
||||
description: "Complete all game development courses",
|
||||
icon: "🎮",
|
||||
points: 300,
|
||||
difficulty: "gold",
|
||||
category: "Specialty",
|
||||
earned_count: 234,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "Knowledge Keeper",
|
||||
description: "Contribute 50 hours of learning content",
|
||||
icon: "📚",
|
||||
points: 150,
|
||||
difficulty: "silver",
|
||||
category: "Community",
|
||||
earned_count: 156,
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
name: "Legendary Mentor",
|
||||
description: "Mentor 10 students to course completion",
|
||||
icon: "👨🏫",
|
||||
points: 400,
|
||||
difficulty: "platinum",
|
||||
category: "Mentorship",
|
||||
earned_count: 45,
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
name: "Code Reviewer",
|
||||
description: "Review 100 pieces of student code",
|
||||
icon: "👀",
|
||||
points: 200,
|
||||
difficulty: "gold",
|
||||
category: "Community",
|
||||
earned_count: 89,
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
name: "Weekend Warrior",
|
||||
description: "Complete a course in 7 days or less",
|
||||
icon: "⚡",
|
||||
points: 100,
|
||||
difficulty: "bronze",
|
||||
category: "Speed",
|
||||
earned_count: 567,
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
name: "Perfect Score",
|
||||
description: "Achieve 100% on all course assessments",
|
||||
icon: "💯",
|
||||
points: 250,
|
||||
difficulty: "gold",
|
||||
category: "Excellence",
|
||||
earned_count: 123,
|
||||
},
|
||||
];
|
||||
|
||||
const CATEGORIES = [
|
||||
"All",
|
||||
"Learning",
|
||||
"Specialty",
|
||||
"Community",
|
||||
"Mentorship",
|
||||
"Speed",
|
||||
"Excellence",
|
||||
];
|
||||
|
||||
export default function FoundationAchievements() {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedCategory, setSelectedCategory] = useState("All");
|
||||
const [userAchievements, setUserAchievements] = useState<string[]>([
|
||||
"1",
|
||||
"3",
|
||||
"7",
|
||||
]); // Mock data
|
||||
|
||||
const filteredAchievements =
|
||||
selectedCategory === "All"
|
||||
? mockAchievements
|
||||
: mockAchievements.filter((a) => a.category === selectedCategory);
|
||||
|
||||
const totalPoints = mockAchievements
|
||||
.filter((a) => userAchievements.includes(a.id))
|
||||
.reduce((sum, a) => sum + a.points, 0);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="relative min-h-screen bg-black text-white overflow-hidden pb-12">
|
||||
{/* Background */}
|
||||
<div className="pointer-events-none absolute inset-0 opacity-[0.12] [background-image:radial-gradient(circle_at_top,#ef4444_0,rgba(0,0,0,0.45)_55%,rgba(0,0,0,0.9)_100%)]" />
|
||||
<div className="pointer-events-none absolute inset-0 bg-[linear-gradient(transparent_0,transparent_calc(100%-1px),rgba(239,68,68,0.05)_calc(100%-1px))] bg-[length:100%_32px]" />
|
||||
<div className="pointer-events-none absolute top-20 left-10 w-72 h-72 bg-red-500/20 rounded-full blur-3xl animate-blob" />
|
||||
<div className="pointer-events-none absolute bottom-20 right-10 w-72 h-72 bg-rose-600/10 rounded-full blur-3xl animate-blob animation-delay-2000" />
|
||||
|
||||
<main className="relative z-10">
|
||||
{/* Header */}
|
||||
<section className="border-b border-slate-800 py-8">
|
||||
<div className="container mx-auto max-w-7xl px-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigate("/foundation")}
|
||||
className="mb-4 text-slate-400"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Back to Foundation
|
||||
</Button>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Trophy className="h-8 w-8 text-red-400" />
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">Achievements</h1>
|
||||
<p className="text-slate-300">Earn badges and unlock rewards</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{user && (
|
||||
<div className="text-right">
|
||||
<p className="text-sm text-slate-400">Total Points</p>
|
||||
<p className="text-3xl font-bold text-red-400">
|
||||
{totalPoints}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats Section */}
|
||||
{user && (
|
||||
<section className="border-b border-slate-800 py-6">
|
||||
<div className="container mx-auto max-w-7xl px-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<Card className="bg-slate-800/30 border-slate-700">
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-slate-400 mb-1">Achieved</p>
|
||||
<p className="text-2xl font-bold text-red-400">
|
||||
{userAchievements.length}/{mockAchievements.length}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="bg-slate-800/30 border-slate-700">
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-slate-400 mb-1">Total Points</p>
|
||||
<p className="text-2xl font-bold text-red-400">
|
||||
{totalPoints}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="bg-slate-800/30 border-slate-700">
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-slate-400 mb-1">
|
||||
Rarest Badge
|
||||
</p>
|
||||
<p className="text-2xl font-bold text-red-400">Platinum</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Category Filter */}
|
||||
<section className="border-b border-slate-800 py-6">
|
||||
<div className="container mx-auto max-w-7xl px-4">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{CATEGORIES.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
onClick={() => setSelectedCategory(category)}
|
||||
className={`px-4 py-2 rounded-lg transition ${
|
||||
selectedCategory === category
|
||||
? "bg-red-500/20 text-red-300 border border-red-500/50"
|
||||
: "bg-slate-800/50 text-slate-400 border border-slate-700"
|
||||
}`}
|
||||
>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Achievements Grid */}
|
||||
<section className="py-12">
|
||||
<div className="container mx-auto max-w-7xl px-4">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-red-400" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{filteredAchievements.map((achievement) => {
|
||||
const isEarned = userAchievements.includes(achievement.id);
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={achievement.id}
|
||||
className={`border-slate-700 transition cursor-pointer ${
|
||||
isEarned
|
||||
? "bg-slate-800/50 hover:border-red-500/50"
|
||||
: "bg-slate-900/50 opacity-60 hover:opacity-80"
|
||||
}`}
|
||||
>
|
||||
<CardContent className="p-6 flex flex-col items-center text-center">
|
||||
<div className="text-5xl mb-4">{achievement.icon}</div>
|
||||
|
||||
<h3 className="font-bold text-white mb-2">
|
||||
{achievement.name}
|
||||
</h3>
|
||||
|
||||
<p className="text-sm text-slate-400 mb-4">
|
||||
{achievement.description}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-center gap-2 mb-4">
|
||||
<Badge
|
||||
className={difficultyColors[achievement.difficulty]}
|
||||
>
|
||||
{achievement.difficulty.toUpperCase()}
|
||||
</Badge>
|
||||
<Badge className="bg-slate-700/50 text-gray-300">
|
||||
+{achievement.points} pts
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-slate-500">
|
||||
{achievement.earned_count} earned
|
||||
</div>
|
||||
|
||||
{isEarned && (
|
||||
<div className="mt-4 pt-4 border-t border-slate-700 w-full">
|
||||
<Badge className="bg-green-500/20 text-green-300">
|
||||
<Award className="h-3 w-3 mr-1 inline" />
|
||||
Unlocked
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
{!user && (
|
||||
<section className="py-12 border-t border-slate-800">
|
||||
<div className="container mx-auto max-w-7xl px-4">
|
||||
<Card className="bg-slate-800/30 border-slate-700">
|
||||
<CardContent className="p-12 text-center">
|
||||
<Trophy className="h-12 w-12 text-slate-600 mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-white mb-2">
|
||||
Start Earning Badges
|
||||
</h3>
|
||||
<p className="text-slate-400 mb-6 max-w-2xl mx-auto">
|
||||
Sign in to track your achievements, earn points, and unlock
|
||||
exclusive rewards as you progress through Foundation courses.
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => navigate("/login")}
|
||||
className="bg-red-500 hover:bg-red-600"
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue