Skip to content

Commit 5fe8c13

Browse files
committed
πŸ† Ranking Tiers
1 parent 09fa434 commit 5fe8c13

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1651
-468
lines changed

β€Žapp/app.consts.tsβ€Ž

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Must be 7 categories
2+
export const TIER_NAMES = ['Beginner', 'Adept', 'Advanced', 'Expert', 'Master', 'Elite', 'Legend'];
3+
4+
const MIN_VALUE = 5;
5+
const MIN_PROFILES_THRESHOLD = 100;
6+
7+
export const NOT_AVAILABLE = `Tiers are available for rankings with over ${MIN_PROFILES_THRESHOLD} profiles.`;
8+
9+
export const RANK_DESCRIPTIONS = {
10+
s: {
11+
title: 'Stars rank',
12+
descriptionList: `Rank is based on the total number of stars across repositories owned by a user.`,
13+
descriptionProfile: `Counts stars on repositories owned by the profile. The ranking includes only profiles that have at least one repository with ${MIN_VALUE} or more stars.`,
14+
entityName: 'star',
15+
notRankedMessage: `A profile must own at least one repository with ${MIN_VALUE}+ stars to be ranked.`,
16+
},
17+
c: {
18+
title: 'Contributor rank',
19+
descriptionList: 'Ranks count stars from repos where a developer has merged PRs β€” excluding their own.',
20+
descriptionProfile: `Counts stars on repos owned by others with merged PRs from this profile. Listed only if it has contributed to at least one repo with ${MIN_VALUE}+ stars.`,
21+
entityName: 'star',
22+
notRankedMessage: `To be ranked, you need a merged PR in a repository with ${MIN_VALUE}+ stars.`,
23+
},
24+
f: {
25+
title: 'Followers rank',
26+
descriptionList: 'Rank is based on the number of followers the user has on GitHub.',
27+
descriptionProfile: `Counts users who follow this profile. The ranking includes only profiles that have at least ${MIN_VALUE} followers.`,
28+
entityName: 'follower',
29+
notRankedMessage: `A profile needs at least ${MIN_VALUE} followers to be ranked.`,
30+
},
31+
};

β€Žapp/components/global-ranking-section.tsxβ€Ž

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Link } from '@/components/link/link';
22
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
33

4+
import { RANK_DESCRIPTIONS } from '../app.consts';
5+
46
export const GlobalRankingSection = () => {
57
return (
68
<div className="flex flex-col gap-4 grow py-8">
@@ -12,30 +14,26 @@ export const GlobalRankingSection = () => {
1214
<div className="flex flex-col md:flex-row gap-4">
1315
<Card className="flex-grow gap-4">
1416
<CardHeader>
15-
<CardTitle>Star ranking</CardTitle>
16-
<CardDescription>
17-
Rank is based on the total number of stars across repositories owned by a user
18-
</CardDescription>
17+
<CardTitle>{RANK_DESCRIPTIONS.s.title}ing</CardTitle>
18+
<CardDescription>{RANK_DESCRIPTIONS.s.descriptionList}</CardDescription>
1919
</CardHeader>
2020
<CardFooter>
2121
<Link href="/by/stars/1">View</Link>
2222
</CardFooter>
2323
</Card>
2424
<Card className="flex-grow gap-4">
2525
<CardHeader>
26-
<CardTitle>Contribution ranking</CardTitle>
27-
<CardDescription>
28-
Rank is based on the stars from repositories where you&apos;ve merged pull requests β€” excluding your own
29-
</CardDescription>
26+
<CardTitle>{RANK_DESCRIPTIONS.c.title}ing</CardTitle>
27+
<CardDescription>{RANK_DESCRIPTIONS.c.descriptionList}</CardDescription>
3028
</CardHeader>
3129
<CardFooter>
3230
<Link href="/by/contributions/1">View</Link>
3331
</CardFooter>
3432
</Card>
3533
<Card className="flex-grow gap-4">
3634
<CardHeader>
37-
<CardTitle>Follower ranking</CardTitle>
38-
<CardDescription>Rank is based on the number of followers the user has on GitHub</CardDescription>
35+
<CardTitle>{RANK_DESCRIPTIONS.f.title}ing</CardTitle>
36+
<CardDescription>{RANK_DESCRIPTIONS.f.descriptionList}</CardDescription>
3937
</CardHeader>
4038
<CardFooter>
4139
<Link href="/by/followers/1">View</Link>

β€Žapp/countries/[orderBy]/[page]/layout.tsxβ€Ž

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,6 @@ export const metadata: Metadata = {
1414
'Explore ranks based on stars, followers, contributions, and more. Dive into dynamic leaderboards and find out how you measure up against developers worldwide.',
1515
};
1616

17-
// function LayoutLoading() {
18-
// return (
19-
// <>
20-
// <Header />
21-
// <Loading />
22-
// </>
23-
// );
24-
// }
25-
26-
// async function ProfileLayoutAwaitParams({ children, params }: ProfileLayoutProps) {
27-
// const { login } = await params;
28-
29-
// return (
30-
// <>
31-
// <Header login={login} />
32-
// <TabsBar className="mb-4">
33-
// <Tab href={`/profile/${login}`} active>
34-
// Overview
35-
// </Tab>
36-
// <Tab href={`/profile/${login}/ranks`}>Ranks</Tab>
37-
// <Tab href={`/profile/${login}/repositories`}>Repositories</Tab>
38-
// </TabsBar>
39-
// {children}
40-
// </>
41-
// );
42-
// }
43-
44-
// export default async function ProfileLayout({ children, params }: ProfileLayoutProps) {
45-
// return (
46-
// <Suspense fallback={<LayoutLoading />}>
47-
// <ProfileLayoutAwaitParams params={params}>{children}</ProfileLayoutAwaitParams>
48-
// </Suspense>
49-
// );
50-
// }
51-
5217
type CountriesLayoutProps = Readonly<{ children: React.ReactNode; params: Promise<{ orderBy: CountrySummaryOrder }> }>;
5318

5419
export default async function CountriesLayout({ children, params }: CountriesLayoutProps) {

β€Žapp/profile/[login]/components/messenger-integration.tsxβ€Ž

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { FaWhatsapp } from 'react-icons/fa';
33
import { FaTelegramPlane } from 'react-icons/fa';
44

55
import { Link as LinkUnderlined } from '@/components/link/link';
6-
import { NewBadge } from '@/components/new-badge/new-badge';
76

87
type LayoutLeftColumnProps = Readonly<{
98
login: string;
@@ -12,9 +11,7 @@ type LayoutLeftColumnProps = Readonly<{
1211
export const MessengerIntegration: FC<LayoutLeftColumnProps> = ({ login }) => {
1312
return (
1413
<div className="flex items-center gap-2 flex-wrap text-sm justify-between px-4 py-4 rounded-xl border-1">
15-
<div className="flex items-center gap-2">
16-
<NewBadge /> Subscribe to {login}’s GitHub rank updates in your messenger
17-
</div>
14+
<div className="flex items-center gap-2">Subscribe to {login}’s GitHub rank updates in your messenger</div>
1815
<div className="flex items-center gap-4">
1916
<LinkUnderlined
2017
className="flex items-center gap-2"

β€Žapp/profile/[login]/components/profile-card.tsxβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type ProfileCardHeaderProps = {
2727
export const ProfileCardHeader: FC<ProfileCardHeaderProps> = ({ children, meta, className }) => {
2828
return (
2929
<CardHeader className={cn('p-0 flex flex-row items-center gap-2', className)}>
30-
<CardTitle className="text-lg">{children}</CardTitle>
30+
<CardTitle className="text-lg font-normal">{children}</CardTitle>
3131
{!!meta ? meta : null}
3232
</CardHeader>
3333
);
@@ -39,7 +39,7 @@ type ProfileCardContentProps = {
3939
};
4040

4141
export const ProfileCardContent: FC<ProfileCardContentProps> = ({ children, className }) => {
42-
return <CardContent className={cn('p-0 flex flex-col gap-1.5', className)}>{children}</CardContent>;
42+
return <CardContent className={cn('p-0 flex grow flex-col gap-1.5', className)}>{children}</CardContent>;
4343
};
4444

4545
type ProfileCardActionsProps = {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { FC, PropsWithChildren } from 'react';
2+
3+
import { ProfileCard, ProfileCardContent } from './profile-card';
4+
5+
type ProfileChartDataSlotProps = {
6+
title: string;
7+
value: string | number;
8+
onClick?: () => void;
9+
};
10+
11+
export const ProfileChartCard: FC<PropsWithChildren> = ({ children }) => (
12+
<ProfileCard className="p-0 md:p-0">
13+
<ProfileCardContent>
14+
<div className="flex">{children}</div>
15+
</ProfileCardContent>
16+
</ProfileCard>
17+
);
18+
19+
export const ProfileChartSlot: FC<PropsWithChildren> = ({ children }) => {
20+
return children;
21+
};
22+
23+
export const ProfileChartDataSlot: FC<ProfileChartDataSlotProps> = ({ title, value, onClick }) => {
24+
return (
25+
<div className="flex flex-col justify-center items-center flex-grow m-4">
26+
<div>
27+
<div className="text-lg">{title}</div>
28+
<div className="text-2xl font-semibold" onClick={onClick}>
29+
{value}
30+
</div>
31+
</div>
32+
</div>
33+
);
34+
};
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { FC } from 'react';
2+
3+
import { TIER_NAMES } from '@/app/app.consts';
4+
import { PersonaChart } from '@/components/persona-chart/persona-chart';
5+
import { RankChart } from '@/components/rank-chart/rank-chart';
6+
import { Tier } from '@/types/generated/graphql';
7+
import { PersonaType } from '@/types/persona.types';
8+
9+
import { ProfileCardsGrid } from './profile-card';
10+
import { ProfileChartCard, ProfileChartDataSlot, ProfileChartSlot } from './profile-chart-card';
11+
import { BestTierResult, ProfileTierType } from '../utils/calculate-tiers/calculate-tiers.types';
12+
13+
type ProfileChartsProps = {
14+
rankChartTitle: string;
15+
tiers?: Tier[] | null;
16+
sTier: ProfileTierType;
17+
cTier: ProfileTierType;
18+
fTier: ProfileTierType;
19+
bestTier?: BestTierResult | null;
20+
};
21+
22+
export const ProfileCharts: FC<ProfileChartsProps> = ({ rankChartTitle, tiers, sTier, cTier, fTier, bestTier }) => {
23+
if (!bestTier?.data?.tier) {
24+
return null;
25+
}
26+
27+
return (
28+
<ProfileCardsGrid>
29+
<ProfileChartCard>
30+
<ProfileChartSlot>
31+
<RankChart progress={bestTier?.data} tiers={tiers} />
32+
</ProfileChartSlot>
33+
<ProfileChartDataSlot
34+
title={rankChartTitle}
35+
value={`${TIER_NAMES[bestTier?.data?.tier - 1]} ${bestTier?.data?.level}`}
36+
/>
37+
</ProfileChartCard>
38+
<ProfileChartCard>
39+
<ProfileChartSlot>
40+
<PersonaChart sTier={sTier.data} cTier={cTier.data} fTier={fTier.data} />
41+
</ProfileChartSlot>
42+
<ProfileChartDataSlot title="Persona" value={`${bestTier?.source.map((t) => PersonaType[t]).join(', ')}`} />
43+
</ProfileChartCard>
44+
</ProfileCardsGrid>
45+
);
46+
};

app/profile/[login]/ranks/components/profile-ranking-switcher.tsx renamed to app/profile/[login]/components/profile-ranking-switcher.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ export const ProfileRankingSwitcher: FC<ProfileRankingSwitcherProps> = ({ login,
1111
return (
1212
<div className="text-sm">
1313
<LinkGroup>
14-
<LinkGroupItem href={`/profile/${login}/ranks`} active={ranking === 'global'}>
14+
<LinkGroupItem href={`/profile/${login}`} active={ranking === 'global'}>
1515
Global
1616
</LinkGroupItem>
17-
<LinkGroupItem href={`/profile/${login}/ranks/country`} active={ranking === 'country'}>
17+
<LinkGroupItem href={`/profile/${login}/country`} active={ranking === 'country'}>
1818
Country
1919
</LinkGroupItem>
2020
</LinkGroup>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client';
2+
import { Info } from 'lucide-react';
3+
4+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
5+
6+
export const RankBreakdownTooltip = () => {
7+
return (
8+
<TooltipProvider>
9+
<Tooltip>
10+
<TooltipTrigger asChild>
11+
<Info size={20} />
12+
</TooltipTrigger>
13+
<TooltipContent>
14+
<p className="max-w-96">
15+
We make the most of the GitHub API. On average the top 20% of GitHub profiles are updated every 2 weeks,
16+
while all other profiles are refreshed every 3–5 weeks. You can also trigger a manual refresh at any time
17+
(sign-in required so we can use your token). Ranks are recalculated daily based on the data in the database.
18+
</p>
19+
</TooltipContent>
20+
</Tooltip>
21+
</TooltipProvider>
22+
);
23+
};

β€Žapp/profile/[login]/components/ranks-overview.tsxβ€Ž

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
Β (0)