|
|
@ -4,7 +4,7 @@ import type { |
|
|
|
ListLeaderboardStatsResponse, |
|
|
|
ListLeaderboardStatsResponse, |
|
|
|
} from '../../api/leaderboard'; |
|
|
|
} from '../../api/leaderboard'; |
|
|
|
import { cn } from '../../lib/classname'; |
|
|
|
import { cn } from '../../lib/classname'; |
|
|
|
import { FolderKanban, Zap, Trophy } from 'lucide-react'; |
|
|
|
import { FolderKanban, Zap, Trophy, GitPullRequest } from 'lucide-react'; |
|
|
|
import { RankBadgeIcon } from '../ReactIcons/RankBadgeIcon'; |
|
|
|
import { RankBadgeIcon } from '../ReactIcons/RankBadgeIcon'; |
|
|
|
import { TrophyEmoji } from '../ReactIcons/TrophyEmoji'; |
|
|
|
import { TrophyEmoji } from '../ReactIcons/TrophyEmoji'; |
|
|
|
import { SecondPlaceMedalEmoji } from '../ReactIcons/SecondPlaceMedalEmoji'; |
|
|
|
import { SecondPlaceMedalEmoji } from '../ReactIcons/SecondPlaceMedalEmoji'; |
|
|
@ -64,6 +64,19 @@ export function LeaderboardPage(props: LeaderboardPageProps) { |
|
|
|
}, |
|
|
|
}, |
|
|
|
]} |
|
|
|
]} |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
<LeaderboardLane |
|
|
|
|
|
|
|
title="Top Contributors" |
|
|
|
|
|
|
|
tabs={[ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
title: 'This Month', |
|
|
|
|
|
|
|
users: stats.githubContributors.currentMonth, |
|
|
|
|
|
|
|
emptyIcon: ( |
|
|
|
|
|
|
|
<GitPullRequest className="size-16 text-gray-300" /> |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
emptyText: 'No contributors this month', |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
]} |
|
|
|
|
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -89,7 +102,7 @@ function LeaderboardLane(props: LeaderboardLaneProps) { |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div className="overflow-hidden rounded-md border bg-white shadow-sm"> |
|
|
|
<div className="overflow-hidden rounded-md border bg-white shadow-sm"> |
|
|
|
<div className="flex items-center justify-between gap-2 bg-gray-100 px-3 py-3 mb-3"> |
|
|
|
<div className="mb-3 flex items-center justify-between gap-2 bg-gray-100 px-3 py-3"> |
|
|
|
<h3 className="text-base font-medium">{title}</h3> |
|
|
|
<h3 className="text-base font-medium">{title}</h3> |
|
|
|
|
|
|
|
|
|
|
|
{tabs.length > 1 && ( |
|
|
|
{tabs.length > 1 && ( |
|
|
@ -128,19 +141,21 @@ function LeaderboardLane(props: LeaderboardLaneProps) { |
|
|
|
<ul className="divide-y divide-gray-100 pb-4"> |
|
|
|
<ul className="divide-y divide-gray-100 pb-4"> |
|
|
|
{usersToShow.map((user, counter) => { |
|
|
|
{usersToShow.map((user, counter) => { |
|
|
|
const avatar = user?.avatar |
|
|
|
const avatar = user?.avatar |
|
|
|
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${user.avatar}` |
|
|
|
? user?.avatar?.startsWith('http') |
|
|
|
|
|
|
|
? user?.avatar |
|
|
|
|
|
|
|
: `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${user.avatar}` |
|
|
|
: '/images/default-avatar.png'; |
|
|
|
: '/images/default-avatar.png'; |
|
|
|
const rank = counter + 1; |
|
|
|
const rank = counter + 1; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<li |
|
|
|
<li |
|
|
|
key={user.id} |
|
|
|
key={user.id} |
|
|
|
className="flex items-center justify-between gap-1 pl-2 pr-5 py-2.5 hover:bg-gray-50" |
|
|
|
className="flex items-center justify-between gap-1 py-2.5 pl-2 pr-5 hover:bg-gray-50" |
|
|
|
> |
|
|
|
> |
|
|
|
<div className="flex min-w-0 items-center gap-2"> |
|
|
|
<div className="flex min-w-0 items-center gap-2"> |
|
|
|
<span |
|
|
|
<span |
|
|
|
className={cn( |
|
|
|
className={cn( |
|
|
|
'relative text-xs mr-1 flex size-6 shrink-0 items-center justify-center rounded-full tabular-nums', |
|
|
|
'relative mr-1 flex size-6 shrink-0 items-center justify-center rounded-full text-xs tabular-nums', |
|
|
|
{ |
|
|
|
{ |
|
|
|
'text-black': rank <= 3, |
|
|
|
'text-black': rank <= 3, |
|
|
|
'text-gray-400': rank > 3, |
|
|
|
'text-gray-400': rank > 3, |
|
|
|