Skip to content

Commit 1bc72a8

Browse files
Temp Config Leaderboard (#41)
1 parent a6f5f27 commit 1bc72a8

File tree

6 files changed

+177
-0
lines changed

6 files changed

+177
-0
lines changed

app/(jumbocup)/jumbocup/page.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* page.tsx
2+
*
3+
* Page for viewing the current Jumbocup leaderboard
4+
*
5+
* Created by Holden Kittelberger on 11/29/2025
6+
*
7+
*/
8+
9+
import Leaderboard from "@/components/leaderboard";
10+
11+
export default function JumboCupPage() {
12+
return (
13+
<div className="flex flex-col items-center justify-center mt-8">
14+
<Leaderboard />
15+
</div>
16+
);
17+
}

app/(jumbocup)/layout.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import "../globals.css";
2+
3+
export const metadata = {
4+
title: 'Next.js',
5+
description: 'Generated by Next.js',
6+
}
7+
8+
export default function RootLayout({
9+
children,
10+
}: {
11+
children: React.ReactNode
12+
}) {
13+
return (
14+
<html lang="en" className="scroll-smooth">
15+
<body className="antialiased bg-gray-950">
16+
<div className="max-w-screen-lg mx-auto px-8 sm:px-12 pt-4 sm:pt-8 pb-24">
17+
{/* Gradient background */}
18+
<div className="absolute inset-0 h-[85vh] bg-gradient-to-b from-brand/25 to-gray-950 -z-10" />
19+
<main>{children}</main>
20+
</div>
21+
</body>
22+
</html>
23+
)
24+
}

components/leaderboard.tsx

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
interface Team {
2+
id: number;
3+
name: string;
4+
score: number;
5+
}
6+
7+
// Sample data - replace with your actual data source
8+
// const teams: Team[] = [
9+
// { id: 1, name: "Beantown Baby Diaper Bank", score: 323 },
10+
// { id: 2, name: "Boston Community Pediatrics", score: 287 },
11+
// { id: 3, name: "Commonwealth Kitchen", score: 283 },
12+
// { id: 4, name: "Lexington Zero Waste", score: 243 },
13+
// { id: 5, name: "MHD", score: 219 },
14+
// { id: 6, name: "The Loop Lab", score: 178 },
15+
// { id: 7, name: "Food Link", score: 137 },
16+
// { id: 8, name: "School on Wheels", score: 100 },
17+
// { id: 9, name: "Boston's Higher Ground", score: 92 },
18+
// { id: 10, name: "Food for Free", score: 82 },
19+
// { id: 11, name: "Artists for Humanity", score: 70 },
20+
// { id: 12, name: "Breaktime", score: 46 },
21+
// ];
22+
23+
const teams: Team[] = [
24+
{ id: 1, name: "Artists for Humanity", score: 0 },
25+
{ id: 2, name: "Beantown Baby Diaper Bank", score: 0 },
26+
{ id: 3, name: "Boston Community Pediatrics", score: 0 },
27+
{ id: 4, name: "Boston's Higher Ground", score: 0 },
28+
{ id: 5, name: "Breaktime", score: 0 },
29+
{ id: 6, name: "Commonwealth Kitchen", score: 0 },
30+
{ id: 7, name: "Food for Free", score: 0 },
31+
{ id: 8, name: "Food Link", score: 0 },
32+
{ id: 9, name: "Lexington Zero Waste", score: 0 },
33+
{ id: 10, name: "MHD", score: 0 },
34+
{ id: 11, name: "School on Wheels", score: 0 },
35+
{ id: 12, name: "The Loop Lab", score: 0 },
36+
];
37+
38+
export default function Leaderboard() {
39+
// Sort teams by score in descending order, then alphabetically by name for ties
40+
const sortedTeams = [...teams].sort((a, b) => {
41+
if (b.score !== a.score) {
42+
return b.score - a.score;
43+
}
44+
return a.name.localeCompare(b.name);
45+
});
46+
47+
// Calculate ranks handling ties
48+
const teamsWithRanks = sortedTeams.map((team, index) => {
49+
let rank = 1;
50+
for (let i = 0; i < index; i++) {
51+
if (sortedTeams[i].score !== team.score) {
52+
rank = i + 2;
53+
break;
54+
}
55+
}
56+
return { ...team, rank };
57+
});
58+
59+
// Get top 3 for podium and remaining for rows
60+
const podiumTeams = teamsWithRanks.slice(0, 3);
61+
const remainingTeams = teamsWithRanks.slice(3);
62+
63+
return (
64+
<div className="flex flex-col items-center justify-center mt-8 max-w-4xl mx-auto px-4">
65+
{/* Title */}
66+
<h2 className="text-3xl font-bold text-white mb-8 text-center">
67+
JumboCup Spring 2026 Leaderboard
68+
</h2>
69+
70+
{/* Podium Section */}
71+
<div className="w-full mb-12">
72+
<div className="flex items-end justify-center gap-2 mb-6 max-w-3xl mx-auto">
73+
{/* 2nd Place */}
74+
<div className="flex flex-col items-center w-1/3">
75+
<div className="bg-gray-600 text-white px-2 py-2 rounded-t-lg mb-2 w-full text-center">
76+
<div className="font-semibold text-xs leading-tight break-words">{podiumTeams[1]?.name}</div>
77+
<div className="text-lg font-bold mt-1">{podiumTeams[1]?.score}</div>
78+
</div>
79+
<div className="bg-gray-400 w-full h-20 rounded-t-lg flex items-center justify-center">
80+
<span className="text-2xl font-bold text-gray-800">2</span>
81+
</div>
82+
</div>
83+
84+
{/* 1st Place */}
85+
<div className="flex flex-col items-center w-1/3">
86+
<div className="bg-yellow-500 text-gray-900 px-2 py-2 rounded-t-lg mb-2 w-full text-center">
87+
<div className="font-semibold text-xs leading-tight break-words">{podiumTeams[0]?.name}</div>
88+
<div className="text-lg font-bold mt-1">{podiumTeams[0]?.score}</div>
89+
</div>
90+
<div className="bg-yellow-400 w-full h-28 rounded-t-lg flex items-center justify-center">
91+
<span className="text-3xl font-bold text-gray-800">1</span>
92+
</div>
93+
</div>
94+
95+
{/* 3rd Place */}
96+
<div className="flex flex-col items-center w-1/3">
97+
<div className="bg-orange-600 text-white px-2 py-2 rounded-t-lg mb-2 w-full text-center">
98+
<div className="font-semibold text-xs leading-tight break-words">{podiumTeams[2]?.name}</div>
99+
<div className="text-lg font-bold mt-1">{podiumTeams[2]?.score}</div>
100+
</div>
101+
<div className="bg-orange-400 w-full h-16 rounded-t-lg flex items-center justify-center">
102+
<span className="text-2xl font-bold text-gray-800">3</span>
103+
</div>
104+
</div>
105+
</div>
106+
</div>
107+
108+
{/* Remaining Teams */}
109+
<div className="w-full space-y-2">
110+
{remainingTeams.map((team) => {
111+
return (
112+
<div
113+
key={team.id}
114+
className="flex items-center justify-between bg-gray-800 hover:bg-gray-700 transition-colors duration-200 px-6 py-4 rounded-lg border border-gray-700"
115+
>
116+
<div className="flex items-center gap-4">
117+
<div className="w-8 h-8 bg-gray-600 rounded-full flex items-center justify-center">
118+
<span className="font-bold text-white text-sm">{team.rank}</span>
119+
</div>
120+
<span className="font-semibold text-white text-lg">{team.name}</span>
121+
</div>
122+
<div className="text-brand text-xl font-bold">
123+
{team.score.toLocaleString()}
124+
</div>
125+
</div>
126+
);
127+
})}
128+
</div>
129+
130+
{/* Footer note */}
131+
<p className="text-subtext text-sm mt-6 text-center">
132+
Updated in real-time • Last update: {new Date().toLocaleDateString()}
133+
</p>
134+
</div>
135+
);
136+
}

0 commit comments

Comments
 (0)