Skip to content

Commit 489dc71

Browse files
committed
2025.02.18 add basic ranking tables
1 parent 20f9201 commit 489dc71

File tree

13 files changed

+547
-119
lines changed

13 files changed

+547
-119
lines changed

app/by/[type]/global-ranking.gql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
query StarsRanking($order: RankOrder, $offset: Int) {
2+
globalRanks(order: $order, offset: $offset) {
3+
githubId
4+
contributedStars
5+
contributedStarsM
6+
followersCount
7+
followersCountM
8+
ownedStars
9+
ownedStarsM
10+
user {
11+
login
12+
avatarUrl
13+
ownedStars
14+
contributedStars
15+
followersCount
16+
location
17+
}
18+
}
19+
}

app/by/[type]/page.tsx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { graphqlRequest } from '@/lib/graphql-request';
2+
import { RankOrder, StarsRankingDocument } from '@/types/generated/graphql';
3+
import Image from 'next/image';
4+
import {
5+
Pagination,
6+
PaginationContent,
7+
PaginationItem,
8+
PaginationNext,
9+
PaginationPrevious,
10+
} from '@/components/ui/pagination';
11+
12+
const ITEMS_PER_PAGE = 100;
13+
14+
function getRankIcon(ownedStars: number, ownedStarsM: number | null | undefined): string {
15+
if (!ownedStarsM) {
16+
return '';
17+
}
18+
19+
const difference = ownedStars - ownedStarsM;
20+
return difference > 0 ? '+' : difference < 0 ? '-' : '';
21+
}
22+
23+
function getConfigByType(rankingType: string) {
24+
let propName: 'contributedStars' | 'followersCount' | 'ownedStars';
25+
let queryOrder: RankOrder;
26+
27+
switch (rankingType) {
28+
case 'contributed-stars':
29+
queryOrder = RankOrder.StarsContributed;
30+
propName = 'contributedStars';
31+
break;
32+
case 'followers':
33+
queryOrder = RankOrder.FollowersCount;
34+
propName = 'followersCount';
35+
break;
36+
case 'owned-stars':
37+
default:
38+
queryOrder = RankOrder.StarsOwned;
39+
propName = 'ownedStars';
40+
break;
41+
}
42+
43+
return [queryOrder, propName] as const;
44+
}
45+
46+
export default async function GlobalRanking({
47+
searchParams,
48+
params,
49+
}: {
50+
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
51+
params: Promise<{ type: string }>;
52+
}) {
53+
const { type } = await params;
54+
const [queryOrder, rankPropName] = getConfigByType(type);
55+
const page = Number((await searchParams)?.page) || 1;
56+
const offset = (page - 1) * ITEMS_PER_PAGE;
57+
const data = await graphqlRequest(StarsRankingDocument, { order: queryOrder, offset });
58+
59+
return (
60+
<>
61+
<table className="table-auto mx-auto my-6">
62+
<thead>
63+
<tr>
64+
<th>#</th>
65+
<th></th>
66+
<th>Login</th>
67+
<th>Location</th>
68+
<th></th>
69+
</tr>
70+
</thead>
71+
<tbody>
72+
{data.globalRanks.map((item) => {
73+
const { githubId, user } = item;
74+
return (
75+
<tr key={githubId}>
76+
<td>
77+
{item[rankPropName]}
78+
{getRankIcon(item[rankPropName], item[`${rankPropName}M`])}
79+
</td>
80+
<td>
81+
{!!user?.avatarUrl && (
82+
<Image
83+
src={user.avatarUrl}
84+
width={36}
85+
height={36}
86+
className="rounded-full m-2"
87+
alt={`${user?.login}'s avatar`}
88+
/>
89+
)}
90+
</td>
91+
<td>{user?.login}</td>
92+
<td>{user?.location}</td>
93+
<td>{user?.[rankPropName]}</td>
94+
</tr>
95+
);
96+
})}
97+
</tbody>
98+
</table>
99+
<Pagination>
100+
<PaginationContent>
101+
{page > 1 && (
102+
<PaginationItem>
103+
<PaginationPrevious href={`/by/${type}?page=${page - 1}`} />
104+
</PaginationItem>
105+
)}
106+
<PaginationItem>
107+
<PaginationNext href={`/by/${type}?page=${page + 1}`} />
108+
</PaginationItem>
109+
</PaginationContent>
110+
</Pagination>
111+
</>
112+
);
113+
}

app/by/owned-stars/page.tsx

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

app/by/owned-stars/stars-ranking.gql

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

app/globals.css

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,40 @@
11
@import 'tailwindcss';
22

3+
@plugin 'tailwindcss-animate';
4+
5+
@custom-variant dark (&:is(.dark *));
6+
37
:root {
4-
--background: #f4f4f4;
5-
--foreground: #171717;
68
--texture-url: url('/texture-light.png');
9+
--background: hsl(0 0% 100%);
10+
--foreground: hsl(240 10% 3.9%);
11+
--card: hsl(0 0% 100%);
12+
--card-foreground: hsl(240 10% 3.9%);
13+
--popover: hsl(0 0% 100%);
14+
--popover-foreground: hsl(240 10% 3.9%);
15+
--primary: hsl(240 5.9% 10%);
16+
--primary-foreground: hsl(0 0% 98%);
17+
--secondary: hsl(240 4.8% 95.9%);
18+
--secondary-foreground: hsl(240 5.9% 10%);
19+
--muted: hsl(240 4.8% 95.9%);
20+
--muted-foreground: hsl(240 3.8% 46.1%);
21+
--accent: hsl(240 4.8% 95.9%);
22+
--accent-foreground: hsl(240 5.9% 10%);
23+
--destructive: hsl(0 84.2% 60.2%);
24+
--destructive-foreground: hsl(0 0% 98%);
25+
--border: hsl(240 5.9% 90%);
26+
--input: hsl(240 5.9% 90%);
27+
--ring: hsl(240 10% 3.9%);
28+
--chart-1: hsl(12 76% 61%);
29+
--chart-2: hsl(173 58% 39%);
30+
--chart-3: hsl(197 37% 24%);
31+
--chart-4: hsl(43 74% 66%);
32+
--chart-5: hsl(27 87% 67%);
33+
--radius: 0.6rem;
734
}
835

936
@media (prefers-color-scheme: dark) {
1037
:root {
11-
--background: #303030;
12-
--foreground: #ededed;
1338
--texture-url: url('/texture-dark.png');
1439
}
1540
}
@@ -23,9 +48,70 @@
2348
}
2449

2550
body {
26-
color: var(--foreground);
27-
font-family: Arial, Helvetica, sans-serif;
28-
2951
background: var(--background) var(--texture-url) repeat;
3052
background-size: 100px 100px;
3153
}
54+
55+
.dark {
56+
--background: hsl(240 10% 3.9%);
57+
--foreground: hsl(0 0% 98%);
58+
--card: hsl(240 10% 3.9%);
59+
--card-foreground: hsl(0 0% 98%);
60+
--popover: hsl(240 10% 3.9%);
61+
--popover-foreground: hsl(0 0% 98%);
62+
--primary: hsl(0 0% 98%);
63+
--primary-foreground: hsl(240 5.9% 10%);
64+
--secondary: hsl(240 3.7% 15.9%);
65+
--secondary-foreground: hsl(0 0% 98%);
66+
--muted: hsl(240 3.7% 15.9%);
67+
--muted-foreground: hsl(240 5% 64.9%);
68+
--accent: hsl(240 3.7% 15.9%);
69+
--accent-foreground: hsl(0 0% 98%);
70+
--destructive: hsl(0 62.8% 30.6%);
71+
--destructive-foreground: hsl(0 0% 98%);
72+
--border: hsl(240 3.7% 15.9%);
73+
--input: hsl(240 3.7% 15.9%);
74+
--ring: hsl(240 4.9% 83.9%);
75+
--chart-1: hsl(220 70% 50%);
76+
--chart-2: hsl(160 60% 45%);
77+
--chart-3: hsl(30 80% 55%);
78+
--chart-4: hsl(280 65% 60%);
79+
--chart-5: hsl(340 75% 55%);
80+
}
81+
82+
@theme inline {
83+
--color-background: var(--background);
84+
--color-foreground: var(--foreground);
85+
--color-card: var(--card);
86+
--color-card-foreground: var(--card-foreground);
87+
--color-popover: var(--popover);
88+
--color-popover-foreground: var(--popover-foreground);
89+
--color-primary: var(--primary);
90+
--color-primary-foreground: var(--primary-foreground);
91+
--color-secondary: var(--secondary);
92+
--color-secondary-foreground: var(--secondary-foreground);
93+
--color-muted: var(--muted);
94+
--color-muted-foreground: var(--muted-foreground);
95+
--color-accent: var(--accent);
96+
--color-accent-foreground: var(--accent-foreground);
97+
--color-destructive: var(--destructive);
98+
--color-destructive-foreground: var(--destructive-foreground);
99+
--color-border: var(--border);
100+
--color-input: var(--input);
101+
--color-ring: var(--ring);
102+
--color-chart-1: var(--chart-1);
103+
--color-chart-2: var(--chart-2);
104+
--color-chart-3: var(--chart-3);
105+
--color-chart-4: var(--chart-4);
106+
--color-chart-5: var(--chart-5);
107+
--radius-sm: calc(var(--radius) - 4px);
108+
--radius-md: calc(var(--radius) - 2px);
109+
--radius-lg: var(--radius);
110+
--radius-xl: calc(var(--radius) + 4px);
111+
}
112+
113+
@layer base {
114+
body {
115+
@apply bg-background text-foreground;
116+
}
117+
}

app/page.tsx

Lines changed: 9 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,25 @@
11
import SigninButton from '@/components/signin-button/signin-button';
2-
import Image from 'next/image';
32

43
export default function Home() {
54
return (
65
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
76
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
8-
<Image className="dark:invert" src="/next.svg" alt="Next.js logo" width={180} height={38} priority />
9-
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
10-
<li className="mb-2">
11-
Get started by editing{' '}
12-
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded-sm font-semibold">app/page.tsx</code>.
13-
</li>
14-
<li>Save and see your changes instantly.</li>
15-
</ol>
7+
<h1 className="text-6xl">GitRanks</h1>
168

179
<div className="flex gap-4 items-center flex-col sm:flex-row">
18-
<SigninButton />
19-
<a
20-
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
21-
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
22-
target="_blank"
23-
rel="noopener noreferrer"
24-
>
25-
<Image className="dark:invert" src="/vercel.svg" alt="Vercel logomark" width={20} height={20} />
26-
Deploy now
10+
<a className="flex items-center gap-2 hover:underline hover:underline-offset-4" href="/by/owned-stars">
11+
by owned stars
2712
</a>
28-
<a
29-
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
30-
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
31-
target="_blank"
32-
rel="noopener noreferrer"
33-
>
34-
Read our docs
13+
<a className="flex items-center gap-2 hover:underline hover:underline-offset-4" href="/by/contributed-stars">
14+
by contributed stars
15+
</a>
16+
<a className="flex items-center gap-2 hover:underline hover:underline-offset-4" href="/by/followers">
17+
by followers
3518
</a>
3619
</div>
3720
</main>
3821
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
39-
<a
40-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
41-
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
42-
target="_blank"
43-
rel="noopener noreferrer"
44-
>
45-
<Image aria-hidden src="/file.svg" alt="File icon" width={16} height={16} />
46-
Learn
47-
</a>
48-
<a
49-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
50-
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
51-
target="_blank"
52-
rel="noopener noreferrer"
53-
>
54-
<Image aria-hidden src="/window.svg" alt="Window icon" width={16} height={16} />
55-
Examples
56-
</a>
57-
<a
58-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
59-
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
60-
target="_blank"
61-
rel="noopener noreferrer"
62-
>
63-
<Image aria-hidden src="/globe.svg" alt="Globe icon" width={16} height={16} />
64-
Go to nextjs.org →
65-
</a>
22+
<SigninButton />
6623
</footer>
6724
</div>
6825
);

0 commit comments

Comments
 (0)