Skip to content

Commit 55f0eff

Browse files
feat: add github contributors leaderboard (#7277)
* feat: add github contributors leaderboard * Improve UI for leaderboard --------- Co-authored-by: Kamran Ahmed <[email protected]>
1 parent 4793680 commit 55f0eff

File tree

9 files changed

+69
-22
lines changed

9 files changed

+69
-22
lines changed

public/images/gifs/party-popper.gif

386 KB
Loading
File renamed without changes.

public/images/gifs/star.gif

145 KB
Loading

public/images/gifs/starstruck.gif

1.01 MB
Loading

public/images/gifs/sunglasses.gif

1010 KB
Loading

src/api/leaderboard.ts

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ export type ListLeaderboardStatsResponse = {
1717
currentMonth: LeadeboardUserDetails[];
1818
lifetime: LeadeboardUserDetails[];
1919
};
20+
githubContributors: {
21+
currentMonth: LeadeboardUserDetails[];
22+
};
2023
};
2124

2225
export function leaderboardApi(context: APIContext) {

src/components/Changelog/ChangelogLaunch.astro

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const formattedDate = DateTime.fromISO('2024-09-13').toFormat('dd LLL, yyyy');
2323
<div
2424
class='flex flex-col items-center justify-center gap-2 sm:gap-2 rounded-xl border bg-white px-8 py-12 text-center'
2525
>
26-
<img src='/images/rocket.gif' class='w-[70px] mb-4' />
26+
<img src='/images/gifs/rocket.gif' class='w-[70px] mb-4' />
2727
<h2 class='text-balance text-xl font-medium'>Changelog page is launched</h2>
2828
<p class='font-normal text-balance text-gray-400 text-sm sm:text-base'>
2929
We will be sharing a selected list of updates, improvements, and fixes made to

src/components/ChangelogBanner.astro

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const top10Changelogs = allChangelogs.slice(0, 10);
1010
<div class='container !max-w-[650px]'>
1111
<p class='text-2xl font-bold sm:text-5xl'>
1212
<img
13-
src='/images/rocket.gif'
13+
src='/images/gifs/rocket.gif'
1414
alt='Rocket'
1515
class='mr-2 hidden sm:inline h-12 w-12'
1616
/>

src/components/Leaderboard/LeaderboardPage.tsx

+64-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { useState, type ReactNode } from 'react';
1+
import { type ReactNode, useState } from 'react';
22
import type {
33
LeadeboardUserDetails,
44
ListLeaderboardStatsResponse,
55
} from '../../api/leaderboard';
66
import { cn } from '../../lib/classname';
7-
import { FolderKanban, Zap, Trophy } from 'lucide-react';
8-
import { RankBadgeIcon } from '../ReactIcons/RankBadgeIcon';
7+
import { FolderKanban, GitPullRequest, Trophy, Zap } from 'lucide-react';
98
import { TrophyEmoji } from '../ReactIcons/TrophyEmoji';
109
import { SecondPlaceMedalEmoji } from '../ReactIcons/SecondPlaceMedalEmoji';
1110
import { ThirdPlaceMedalEmoji } from '../ReactIcons/ThirdPlaceMedalEmoji';
@@ -19,17 +18,25 @@ export function LeaderboardPage(props: LeaderboardPageProps) {
1918

2019
return (
2120
<div className="min-h-screen bg-gray-50">
22-
<div className="container py-10">
21+
<div className="container py-5 sm:py-10">
2322
<div className="mb-8 text-center">
24-
<div className="mb-2 flex items-center justify-center gap-3">
25-
<Trophy className="size-8 text-yellow-500" />
26-
<h2 className="text-2xl font-bold sm:text-3xl">Leaderboard</h2>
23+
<div className="flex flex-col items-start sm:items-center justify-center">
24+
<img
25+
src={'/images/gifs/star.gif'}
26+
alt="party-popper"
27+
className="mb-4 mt-0 sm:mt-3 h-14 w-14 hidden sm:block"
28+
/>
29+
<div className="mb-0 sm:mb-4 flex flex-col items-start sm:items-center justify-start sm:justify-center">
30+
<h2 className="mb-1.5 sm:mb-2 text-2xl font-semibold sm:text-4xl">
31+
Leaderboard
32+
</h2>
33+
<p className="max-w-2xl text-left sm:text-center text-balance text-sm text-gray-500 sm:text-base">
34+
Top users based on their activity on roadmap.sh
35+
</p>
36+
</div>
2737
</div>
28-
<p className="mx-auto max-w-2xl text-balance text-sm text-gray-500 sm:text-base">
29-
Top users based on their activity on roadmap.sh
30-
</p>
3138

32-
<div className="mt-8 grid gap-2 md:grid-cols-2">
39+
<div className="mt-5 sm:mt-8 grid gap-2 md:grid-cols-2">
3340
<LeaderboardLane
3441
title="Longest Visit Streak"
3542
tabs={[
@@ -64,6 +71,19 @@ export function LeaderboardPage(props: LeaderboardPageProps) {
6471
},
6572
]}
6673
/>
74+
<LeaderboardLane
75+
title="Top Contributors"
76+
tabs={[
77+
{
78+
title: 'This Month',
79+
users: stats.githubContributors.currentMonth,
80+
emptyIcon: (
81+
<GitPullRequest className="size-16 text-gray-300" />
82+
),
83+
emptyText: 'No contributors this month',
84+
},
85+
]}
86+
/>
6787
</div>
6888
</div>
6989
</div>
@@ -88,8 +108,8 @@ function LeaderboardLane(props: LeaderboardLaneProps) {
88108
const { users: usersToShow, emptyIcon, emptyText } = activeTab;
89109

90110
return (
91-
<div className="overflow-hidden rounded-md border bg-white shadow-sm">
92-
<div className="flex items-center justify-between gap-2 bg-gray-100 px-3 py-3 mb-3">
111+
<div className="flex flex-col overflow-hidden rounded-xl border bg-white min-h-[450px] ">
112+
<div className="mb-3 flex items-center justify-between gap-2 px-3 py-3">
93113
<h3 className="text-base font-medium">{title}</h3>
94114

95115
{tabs.length > 1 && (
@@ -118,7 +138,7 @@ function LeaderboardLane(props: LeaderboardLaneProps) {
118138
</div>
119139

120140
{usersToShow.length === 0 && emptyText && (
121-
<div className="flex flex-col items-center justify-center p-8">
141+
<div className="flex flex-grow flex-col items-center justify-center p-8">
122142
{emptyIcon}
123143
<p className="mt-4 text-sm text-gray-500">{emptyText}</p>
124144
</div>
@@ -128,19 +148,23 @@ function LeaderboardLane(props: LeaderboardLaneProps) {
128148
<ul className="divide-y divide-gray-100 pb-4">
129149
{usersToShow.map((user, counter) => {
130150
const avatar = user?.avatar
131-
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${user.avatar}`
151+
? user?.avatar?.startsWith('http')
152+
? user?.avatar
153+
: `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${user.avatar}`
132154
: '/images/default-avatar.png';
155+
133156
const rank = counter + 1;
157+
const isGitHubUser = avatar?.indexOf('github') > -1;
134158

135159
return (
136160
<li
137161
key={user.id}
138-
className="flex items-center justify-between gap-1 pl-2 pr-5 py-2.5 hover:bg-gray-50"
162+
className="flex items-center justify-between gap-1 py-2.5 pl-2 pr-5"
139163
>
140164
<div className="flex min-w-0 items-center gap-2">
141165
<span
142166
className={cn(
143-
'relative text-xs mr-1 flex size-6 shrink-0 items-center justify-center rounded-full tabular-nums',
167+
'relative mr-1 flex size-6 shrink-0 items-center justify-center rounded-full text-xs tabular-nums',
144168
{
145169
'text-black': rank <= 3,
146170
'text-gray-400': rank > 3,
@@ -153,9 +177,19 @@ function LeaderboardLane(props: LeaderboardLaneProps) {
153177
<img
154178
src={avatar}
155179
alt={user.name}
156-
className="size-7 shrink-0 rounded-full"
180+
className="mr-1 size-7 shrink-0 rounded-full"
157181
/>
158-
<span className="truncate">{user.name}</span>
182+
{isGitHubUser ? (
183+
<a
184+
href={`https://github.com/${user.name}`}
185+
target="_blank"
186+
className="truncate font-medium underline underline-offset-2"
187+
>
188+
{user.name}
189+
</a>
190+
) : (
191+
<span className="truncate">{user.name}</span>
192+
)}
159193
{rank === 1 ? (
160194
<TrophyEmoji className="size-5" />
161195
) : rank === 2 ? (
@@ -167,7 +201,17 @@ function LeaderboardLane(props: LeaderboardLaneProps) {
167201
)}
168202
</div>
169203

170-
<span className="text-sm text-gray-500">{user.count}</span>
204+
{isGitHubUser ? (
205+
<a
206+
target={'_blank'}
207+
href={`https://github.com/kamranahmedse/developer-roadmap/pulls/${user.name}`}
208+
className="text-sm text-gray-500"
209+
>
210+
{user.count}
211+
</a>
212+
) : (
213+
<span className="text-sm text-gray-500">{user.count}</span>
214+
)}
171215
</li>
172216
);
173217
})}

0 commit comments

Comments
 (0)