Skip to content

Commit

Permalink
feat: implement leaderboard UI with animations and scrollable layout
Browse files Browse the repository at this point in the history
  • Loading branch information
harshaldulera committed Dec 7, 2024
1 parent ac0f7e8 commit b69ecae
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 51 deletions.
21 changes: 21 additions & 0 deletions packages/nextjs/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
}

.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}

.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}

.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 20px;
}

.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background-color: rgba(255, 255, 255, 0.2);
}
143 changes: 92 additions & 51 deletions packages/nextjs/app/leaderboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,102 @@ interface LeaderboardEntry {
eth: number;
}

const LeaderBoard = () => {
// Sample data
const leaderboardData: LeaderboardEntry[] = [
{ rank: 1, address: "0x1234...5678", score: 1500, eth: 2.5 },
{ rank: 2, address: "0x8765...4321", score: 1400, eth: 2.2 },
{ rank: 3, address: "0x9876...1234", score: 1300, eth: 2.0 },
{ rank: 4, address: "0x5432...8765", score: 1200, eth: 1.8 },
{ rank: 5, address: "0x2468...1357", score: 1100, eth: 1.5 },
{ rank: 6, address: "0x1357...2468", score: 1000, eth: 1.3 },
{ rank: 7, address: "0x3691...2580", score: 900, eth: 1.1 },
{ rank: 8, address: "0x2580...3691", score: 800, eth: 0.9 },
{ rank: 9, address: "0x1470...2581", score: 700, eth: 0.7 },
{ rank: 10, address: "0x3692...1470", score: 600, eth: 0.5 },
];
export default function Leaderboard() {
// Extended sample data for scrolling
const leaderboardData: LeaderboardEntry[] = Array.from({ length: 50 }, (_, i) => ({
rank: i + 1,
address: `0x${Math.random().toString(16).slice(2, 6)}...${Math.random().toString(16).slice(2, 6)}`,
score: Math.floor(1500 - (i * 20) + Math.random() * 10),
eth: Number((2.5 - (i * 0.05) + Math.random() * 0.1).toFixed(2)),
}));

return (
<div className="max-w-2xl mx-auto p-6">
<h2 className="text-2xl font-bold mb-6 text-center text-white">Leaderboard</h2>

<div className="space-y-3">
{leaderboardData.map((entry, index) => (
<motion.div
key={entry.rank}
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
className={`p-4 rounded-lg ${
entry.rank === 1 ? 'bg-yellow-500/20' :
entry.rank === 2 ? 'bg-gray-400/20' :
entry.rank === 3 ? 'bg-orange-600/20' :
'bg-base-200'
} hover:bg-base-300 transition-colors`}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<span className={`text-xl font-bold ${
entry.rank === 1 ? 'text-yellow-500' :
entry.rank === 2 ? 'text-gray-400' :
entry.rank === 3 ? 'text-orange-600' :
'text-white'
}`}>
#{entry.rank}
</span>
<span className="text-white">{entry.address}</span>
</div>
<div className="flex items-center gap-4">
<span className="text-primary">{entry.score} pts</span>
<span className="text-green-500">{entry.eth} ETH</span>
<div className="flex flex-col h-screen">
{/* Fixed Hero Section */}
<div className="bg-base-300 py-8">
<div className="max-w-2xl mx-auto text-center">
<h1 className="text-4xl font-bold mb-4 text-white">Top Stakers</h1>
<p className="text-base-content">
The highest stakers on our platform are showcased here. Join them by staking your ETH!
</p>
</div>
</div>

{/* Scrollable Leaderboard Section */}
<div className="flex-grow overflow-hidden">
<div className="max-w-2xl mx-auto p-6 h-full">
{/* Sticky Header */}
<div className="sticky top-0 bg-base-100 p-4 rounded-lg mb-3 z-10 shadow-lg">
<div className="flex justify-between text-sm text-base-content">
<span>Rank & Address</span>
<div className="flex gap-8 pr-4">
<span>Score</span>
<span>Staked</span>
</div>
</div>
</motion.div>
))}
</div>

{/* Scrollable Content */}
<div className="space-y-3 overflow-y-auto h-[calc(100vh-400px)] pr-2 custom-scrollbar">
{leaderboardData.map((entry, index) => (
<motion.div
key={entry.rank}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
className={`p-4 rounded-lg ${
entry.rank === 1 ? 'bg-yellow-500/20 border border-yellow-500/50' :
entry.rank === 2 ? 'bg-gray-400/20 border border-gray-400/50' :
entry.rank === 3 ? 'bg-orange-600/20 border border-orange-600/50' :
'bg-base-200'
} hover:bg-base-300 transition-all duration-200 transform hover:scale-[1.02]`}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
entry.rank === 1 ? 'bg-yellow-500' :
entry.rank === 2 ? 'bg-gray-400' :
entry.rank === 3 ? 'bg-orange-600' :
'bg-base-300'
}`}>
<span className="font-bold text-base-100">
{entry.rank}
</span>
</div>
<span className="text-white font-mono">{entry.address}</span>
</div>
<div className="flex items-center gap-4">
<div className="flex flex-col items-end">
<span className="text-primary font-bold">{entry.score}</span>
</div>
<div className="flex flex-col items-end min-w-[80px]">
<span className="text-green-500 font-bold">{entry.eth} ETH</span>
</div>
</div>
</div>
</motion.div>
))}
</div>
</div>
</div>

{/* Fixed Stats Section */}
<div className="bg-base-300 py-8">
<div className="max-w-2xl mx-auto grid grid-cols-3 gap-4 text-center">
<div>
<h3 className="text-2xl font-bold text-primary">100+</h3>
<p className="text-base-content">Total Stakers</p>
</div>
<div>
<h3 className="text-2xl font-bold text-primary">50 ETH</h3>
<p className="text-base-content">Total Staked</p>
</div>
<div>
<h3 className="text-2xl font-bold text-primary">10%</h3>
<p className="text-base-content">APY</p>
</div>
</div>
</div>
</div>
);
};

export default LeaderBoard;
}

0 comments on commit b69ecae

Please sign in to comment.