import { useSeoMeta } from '@unhead/react'; import { useMemo } from 'react'; import { Layout } from '@/components/Layout'; import { Card, CardContent } from '@/components/ui/card'; import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'; import { useSkateSpots } from '@/hooks/useSkateSpots'; import { useSkateEvents } from '@/hooks/useSkateEvents'; import { useAuthor } from '@/hooks/useAuthor'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useTrustScore } from '@/hooks/useTrustScore'; import { genUserName } from '@/lib/genUserName'; import { Trophy, Medal } from 'lucide-react'; import { cn } from '@/lib/utils'; function ContributorRow({ pubkey, spots, events, rank }: { pubkey: string; spots: number; events: number; rank: number }) { const author = useAuthor(pubkey); const { data: trustScore } = useTrustScore(pubkey); const metadata = author.data?.metadata; const displayName = metadata?.name ?? genUserName(pubkey); const total = spots + events; const rankColor = rank === 1 ? 'text-yellow-400' : rank === 2 ? 'text-gray-300' : rank === 3 ? 'text-orange-400' : 'text-gray-500'; const rankEmoji = rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : ''; // Trust score color coding const trustScoreColor = trustScore === undefined ? 'text-gray-500' : trustScore >= 80 ? 'text-green-500' : trustScore >= 50 ? 'text-yellow-500' : 'text-gray-500'; return (
{rankEmoji || `#${rank}`}
{displayName.slice(0, 2).toUpperCase()}
{displayName}
{metadata?.nip05 && (
{metadata.nip05}
)}
{spots}
Spots
{events}
Events
{total}
Total
{trustScore ?? '—'}
Trust
); } export default function PyramidPage() { useSeoMeta({ title: 'Pyramid Rankings - SkateSpots', description: 'Top contributors and community leaders.', }); const { user } = useCurrentUser(); const { data: allSpots } = useSkateSpots(500); const { data: allEvents } = useSkateEvents(500); const { data: userTrustScore } = useTrustScore(user?.pubkey); // Calculate contributor rankings const rankings = useMemo(() => { if (!allSpots && !allEvents) return []; const contributorMap = new Map(); // Count spots per contributor allSpots?.forEach(spot => { const current = contributorMap.get(spot.pubkey) || { spots: 0, events: 0 }; contributorMap.set(spot.pubkey, { ...current, spots: current.spots + 1 }); }); // Count events per contributor allEvents?.forEach(event => { const current = contributorMap.get(event.pubkey) || { spots: 0, events: 0 }; contributorMap.set(event.pubkey, { ...current, events: current.events + 1 }); }); // Convert to array and sort by total contributions return Array.from(contributorMap.entries()) .map(([pubkey, stats]) => ({ pubkey, spots: stats.spots, events: stats.events, total: stats.spots + stats.events, })) .sort((a, b) => b.total - a.total); }, [allSpots, allEvents]); const userRank = rankings.findIndex(r => r.pubkey === user?.pubkey) + 1; const userStats = rankings.find(r => r.pubkey === user?.pubkey); return (

PYRAMID RANKINGS

Top contributors in your city

{/* Your Stats */} {user && userStats && (
#{userRank}
Your Rank
Keep contributing to climb higher!
{userStats.spots}
Spots
{userStats.events}
Events
{userStats.total}
Total
= 80 ? "text-green-500" : userTrustScore >= 50 ? "text-yellow-500" : "text-gray-500" )}> {userTrustScore ?? '—'}
Trust
)} {/* Leaderboard */}

Top Contributors

{rankings.length > 0 ? (
{rankings.slice(0, 20).map((contributor, idx) => ( ))}
) : (

No contributors yet. Be the first to add a spot or create an event!

)}
); }