From 851a0381b6bcbb439a01c81e2b14a09878823e47 Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Thu, 12 Sep 2024 18:51:05 +0100 Subject: [PATCH] Add leaderboard page --- src/components/HeroSection/HeroSection.astro | 2 - .../Leaderboard/LeaderboardPage.tsx | 135 +++++++++--------- src/components/ReactIcons/RankBadgeIcon.tsx | 2 +- .../ReactIcons/SecondPlaceMedalEmoji.tsx | 25 ++++ .../ReactIcons/ThirdPlaceMedalEmoji.tsx | 25 ++++ src/components/ReactIcons/TrophyEmoji.tsx | 31 ++++ 6 files changed, 153 insertions(+), 67 deletions(-) create mode 100644 src/components/ReactIcons/SecondPlaceMedalEmoji.tsx create mode 100644 src/components/ReactIcons/ThirdPlaceMedalEmoji.tsx create mode 100644 src/components/ReactIcons/TrophyEmoji.tsx diff --git a/src/components/HeroSection/HeroSection.astro b/src/components/HeroSection/HeroSection.astro index a55867e59..1997afe75 100644 --- a/src/components/HeroSection/HeroSection.astro +++ b/src/components/HeroSection/HeroSection.astro @@ -1,5 +1,4 @@ --- -import { FavoriteRoadmaps } from './FavoriteRoadmaps'; import { FeatureAnnouncement } from "../FeatureAnnouncement"; --- @@ -31,5 +30,4 @@ import { FeatureAnnouncement } from "../FeatureAnnouncement"; their career.

- diff --git a/src/components/Leaderboard/LeaderboardPage.tsx b/src/components/Leaderboard/LeaderboardPage.tsx index 944ef57d6..16ba0155e 100644 --- a/src/components/Leaderboard/LeaderboardPage.tsx +++ b/src/components/Leaderboard/LeaderboardPage.tsx @@ -4,8 +4,11 @@ import type { ListLeaderboardStatsResponse, } from '../../api/leaderboard'; import { cn } from '../../lib/classname'; -import { FolderKanban, Zap } from 'lucide-react'; -import { RankBadeIcon } from '../ReactIcons/RankBadgeIcon'; +import { FolderKanban, Zap, Trophy } from 'lucide-react'; +import { RankBadgeIcon } from '../ReactIcons/RankBadgeIcon'; +import { TrophyEmoji } from '../ReactIcons/TrophyEmoji'; +import { SecondPlaceMedalEmoji } from '../ReactIcons/SecondPlaceMedalEmoji'; +import { ThirdPlaceMedalEmoji } from '../ReactIcons/ThirdPlaceMedalEmoji'; type LeaderboardPageProps = { stats: ListLeaderboardStatsResponse; @@ -17,42 +20,45 @@ export function LeaderboardPage(props: LeaderboardPageProps) { return (
-

- Leaderboard -

-

- Top users based on their activity on roadmap.sh -

- -
- , - emptyText: 'No users with streaks yet', - }, - ]} - /> - , - emptyText: 'No projects submitted this month', - }, - { - title: 'Lifetime', - users: stats.projectSubmissions.lifetime, - emptyIcon: , - emptyText: 'No projects submitted yet', - }, - ]} - /> +
+
+ +

Leaderboard

+
+

+ Top users based on their activity on roadmap.sh +

+ +
+ , + emptyText: 'No users with streaks yet', + }, + ]} + /> + , + emptyText: 'No projects submitted this month', + }, + { + title: 'Lifetime', + users: stats.projectSubmissions.lifetime, + emptyIcon: , + emptyText: 'No projects submitted yet', + }, + ]} + /> +
@@ -76,12 +82,12 @@ function LeaderboardLane(props: LeaderboardLaneProps) { const { users: usersToShow, emptyIcon, emptyText } = activeTab; return ( -
-
-

{title}

+
+
+

{title}

{tabs.length > 1 && ( -
+
{tabs.map((tab) => { const isActive = tab === activeTab; @@ -90,8 +96,11 @@ function LeaderboardLane(props: LeaderboardLaneProps) { key={tab.title} onClick={() => setActiveTab(tab)} className={cn( - 'px-2 py-0.5 text-sm text-gray-500 hover:bg-gray-100', - isActive ? 'bg-gray-200 text-black' : 'bg-white', + 'text-xs font-medium underline-offset-2 transition-colors', + { + 'text-black underline': isActive, + 'text-gray-400 hover:text-gray-600': !isActive, + }, )} > {tab.title} @@ -110,7 +119,7 @@ function LeaderboardLane(props: LeaderboardLaneProps) { )} {usersToShow.length > 0 && ( -
    +
      {usersToShow.map((user, counter) => { const avatar = user?.avatar ? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${user.avatar}` @@ -120,38 +129,36 @@ function LeaderboardLane(props: LeaderboardLaneProps) { return (
    • 3 && 'text-gray-400', + 'relative text-xs mr-1 flex size-6 shrink-0 items-center justify-center rounded-full tabular-nums', + { + 'text-black': rank <= 3, + 'text-gray-400': rank > 3, + }, )} > - {rank} - - {rank <= 3 && ( - - )} + {rank} {user.name} {user.name} + {rank === 1 ? ( + + ) : rank === 2 ? ( + + ) : rank === 3 ? ( + + ) : ( + '' + )}
      {user.count} diff --git a/src/components/ReactIcons/RankBadgeIcon.tsx b/src/components/ReactIcons/RankBadgeIcon.tsx index 75170b391..541671754 100644 --- a/src/components/ReactIcons/RankBadgeIcon.tsx +++ b/src/components/ReactIcons/RankBadgeIcon.tsx @@ -1,6 +1,6 @@ import type { SVGProps } from 'react'; -export function RankBadeIcon(props: SVGProps) { +export function RankBadgeIcon(props: SVGProps) { return ( ) { + return ( + + + + + + + ); +} diff --git a/src/components/ReactIcons/ThirdPlaceMedalEmoji.tsx b/src/components/ReactIcons/ThirdPlaceMedalEmoji.tsx new file mode 100644 index 000000000..103739ffd --- /dev/null +++ b/src/components/ReactIcons/ThirdPlaceMedalEmoji.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import type { SVGProps } from 'react'; + +export function ThirdPlaceMedalEmoji(props: SVGProps) { + return ( + + + + + + + ); +} diff --git a/src/components/ReactIcons/TrophyEmoji.tsx b/src/components/ReactIcons/TrophyEmoji.tsx new file mode 100644 index 000000000..daab7f292 --- /dev/null +++ b/src/components/ReactIcons/TrophyEmoji.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import type { SVGProps } from 'react'; + +export function TrophyEmoji(props: SVGProps) { + return ( + + + + + + + ); +}