diff --git a/client/components/AchievementsWidget.tsx b/client/components/AchievementsWidget.tsx new file mode 100644 index 00000000..775154e2 --- /dev/null +++ b/client/components/AchievementsWidget.tsx @@ -0,0 +1,178 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Trophy, Lock, Star, ArrowRight } from "lucide-react"; + +export interface Achievement { + id: string; + title: string; + description?: string; + icon_url?: string; + category?: string; + rarity: "common" | "uncommon" | "rare" | "epic" | "legendary"; + earned_at?: string; + progress?: number; + max_progress?: number; +} + +interface AchievementsWidgetProps { + achievements: Achievement[]; + title?: string; + description?: string; + onViewAll?: () => void; + maxDisplay?: number; + accentColor?: "red" | "blue" | "purple" | "gold"; +} + +const rarityMap = { + common: { color: "bg-gray-600/50 text-gray-100", border: "border-gray-500/30" }, + uncommon: { color: "bg-green-600/50 text-green-100", border: "border-green-500/30" }, + rare: { color: "bg-blue-600/50 text-blue-100", border: "border-blue-500/30" }, + epic: { color: "bg-purple-600/50 text-purple-100", border: "border-purple-500/30" }, + legendary: { color: "bg-yellow-600/50 text-yellow-100", border: "border-yellow-500/30" }, +}; + +const colorMap = { + red: { + bg: "bg-gradient-to-br from-red-950/40 to-red-900/20", + border: "border-red-500/20", + }, + blue: { + bg: "bg-gradient-to-br from-blue-950/40 to-blue-900/20", + border: "border-blue-500/20", + }, + purple: { + bg: "bg-gradient-to-br from-purple-950/40 to-purple-900/20", + border: "border-purple-500/20", + }, + gold: { + bg: "bg-gradient-to-br from-amber-950/40 to-amber-900/20", + border: "border-amber-500/20", + }, +}; + +export function AchievementsWidget({ + achievements, + title = "My Achievements", + description = "Trophy case of earned badges", + onViewAll, + maxDisplay = 5, + accentColor = "gold", +}: AchievementsWidgetProps) { + const colors = colorMap[accentColor]; + const displayedAchievements = achievements.slice(0, maxDisplay); + const hasMore = achievements.length > maxDisplay; + + const getRarityIcon = (rarity: string) => { + switch (rarity) { + case "legendary": + return "👑"; + case "epic": + return "⭐"; + case "rare": + return "✨"; + case "uncommon": + return "🌟"; + default: + return "🏅"; + } + }; + + return ( + + + + + {title} + + {description} + + + {achievements.length === 0 ? ( +
+ +

No achievements yet

+

Complete courses and challenges to earn badges

+
+ ) : ( +
+ {/* Trophy Case Grid */} +
+ {displayedAchievements.map((achievement) => { + const rarityInfo = rarityMap[achievement.rarity]; + const icon = getRarityIcon(achievement.rarity); + + return ( +
+ {/* Badge Icon */} +
+ {achievement.icon_url ? ( + {achievement.title} + ) : ( +
+ {icon} +
+ )} +
+ + {/* Title */} +
+

+ {achievement.title} +

+ + {achievement.rarity} + +
+ + {/* Earned Date */} + {achievement.earned_at && ( +

+ {new Date(achievement.earned_at).toLocaleDateString()} +

+ )} +
+ ); + })} + + {/* Locked Achievements Placeholder */} + {hasMore && ( +
+
+ +
+

+ +{achievements.length - maxDisplay} more +

+
+ )} +
+ + {/* View All Button */} + {onViewAll && ( + + )} +
+ )} +
+
+ ); +} + +export default AchievementsWidget;