Untitled
unknown
plain_text
a month ago
7.5 kB
3
Indexable
import React, { useEffect, useState } from 'react'; import { useMetrics } from '@/hooks/useMetrics'; import { AppSidebar } from "@/components/app-sidebar"; import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; import { Separator } from "@/components/ui/separator"; import { SidebarInset, SidebarProvider, SidebarTrigger, } from "@/components/ui/sidebar"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; import { Badge } from "@/components/ui/badge"; import { Trophy, Lock, Unlock, Award, Target, Calendar } from 'lucide-react'; import { useTranslation, type SupportedLanguages } from "@/utils/translations"; import { useSettings } from "@/hooks/useSettings"; import confetti from 'canvas-confetti'; const AchievementsPage = () => { const { metrics, loading, error } = useMetrics(); const { settings } = useSettings(); const { t } = useTranslation(settings?.language as SupportedLanguages || 'EN'); const [confettiTriggered, setConfettiTrigered] = useState(false); useEffect(() => { if (metrics?.achievements && !confettiTriggered) { const unlockedAchievements = Object.values(metrics.achievements).filter( achievement => achievement.status === 'completed' ); if (unlockedAchievements.length > 0) { const container = document.querySelector('.achievements-container'); if (container) { const rect = container.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; confetti({ particleCount: 100, spread: 70, origin: { x: centerX / window.innerWidth, y: centerY / window.innerHeight }, disableForReducedMotion: true }); setConfettiTrigered(true); } } } }, [metrics, confettiTriggered]); if (loading) return <div className="p-4">Loading achievements...</div>; if (error) return <div className="p-4 text-red-500">Error loading achievements</div>; if (!metrics) return null; const achievements = Object.entries(metrics.achievements); const unlockedCount = achievements.filter(([_, a]) => a.status === 'completed').length; const totalCount = achievements.length; const getAchievementIcon = (achievement) => { switch (achievement.tier) { case 'bronze': return <Trophy className="w-8 h-8 text-amber-600" />; case 'silver': return <Trophy className="w-8 h-8 text-gray-400" />; case 'gold': return <Trophy className="w-8 h-8 text-yellow-400" />; default: return <Award className="w-8 h-8 text-primary" />; } }; return ( <SidebarProvider> <AppSidebar /> <SidebarInset> <header className="flex shrink-0 items-center gap-2 border-b px-4 py-6"> <SidebarTrigger className="-ml-1" /> <Separator orientation="vertical" className="mr-2 h-4" /> <Breadcrumb> <BreadcrumbList> <BreadcrumbItem> <BreadcrumbLink href="/">{t('navigation.home')}</BreadcrumbLink> </BreadcrumbItem> <BreadcrumbSeparator /> <BreadcrumbItem> <BreadcrumbLink href="/achievements">{t('navigation.achievements')}</BreadcrumbLink> </BreadcrumbItem> </BreadcrumbList> </Breadcrumb> </header> <main className="p-4"> <Card className="mb-6"> <CardHeader> <CardTitle className="flex items-center gap-2"> <Trophy className="w-6 h-6 text-primary" /> Achievement Progress </CardTitle> </CardHeader> <CardContent> <div className="flex items-center gap-4"> <Progress value={(unlockedCount / totalCount) * 100} className="w-full" /> <span className="text-sm font-medium"> {unlockedCount}/{totalCount} </span> </div> </CardContent> </Card> <div className="achievements-container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> {achievements.sort(([, a], [, b]) => { // Sort completed first, then by progress percentage if (a.status === 'completed' && b.status !== 'completed') return -1; if (a.status !== 'completed' && b.status === 'completed') return 1; return (b.progress / b.target) - (a.progress / a.target); }).map(([id, achievement]) => ( <Card key={id} className={`relative overflow-hidden ${ achievement.status === 'completed' ? 'border-primary/50 bg-primary/5' : 'opacity-75' }`} > <CardHeader> <CardTitle className="flex items-center justify-between"> <div className="flex items-center gap-2"> {getAchievementIcon(achievement)} <span>{achievement.name}</span> </div> {achievement.status === 'completed' ? ( <Unlock className="w-5 h-5 text-primary" /> ) : ( <Lock className="w-5 h-5" /> )} </CardTitle> </CardHeader> <CardContent> <p className="mb-4 text-sm text-muted-foreground"> {achievement.description} </p> <div className="space-y-4"> <div className="flex items-center gap-2"> <Target className="w-4 h-4" /> <Progress value={(achievement.progress / achievement.target) * 100} className="flex-1" /> <span className="text-sm font-medium"> {achievement.progress}/{achievement.target} </span> </div> {achievement.unlock_date && ( <div className="flex items-center gap-2 text-sm text-muted-foreground"> <Calendar className="w-4 h-4" /> <span>Unlocked: {new Date(achievement.unlock_date).toLocaleDateString()}</span> </div> )} {achievement.tier && ( <Badge variant={ achievement.tier === 'gold' ? 'default' : achievement.tier === 'silver' ? 'secondary' : 'outline' }> {achievement.tier.charAt(0).toUpperCase() + achievement.tier.slice(1)} Tier </Badge> )} </div> </CardContent> </Card> ))} </div> </main> </SidebarInset> </SidebarProvider> ); }; export default AchievementsPage;
Editor is loading...
Leave a Comment