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