1- import React , { useEffect , useState } from ' react' ;
2- import { Helmet } from ' react-helmet-async' ;
3- import { Link } from ' react-router-dom' ; // Import Link
1+ import React , { useEffect , useState } from " react" ;
2+ import { Helmet } from " react-helmet-async" ;
3+ import { Link } from " react-router-dom" ; // Import Link
44import { Button } from "@/components/ui/button" ; // Keep Button import for potential future use or consistency
55
66interface PointsDetails {
@@ -16,30 +16,52 @@ interface LeaderboardEntry {
1616 total_points : number ;
1717}
1818
19- const Modal : React . FC < { isOpen : boolean ; onClose : ( ) => void ; data : PointsDetails [ ] ; name : string } > = ( { isOpen, onClose, data, name } ) => {
19+ const Modal : React . FC < {
20+ isOpen : boolean ;
21+ onClose : ( ) => void ;
22+ data : PointsDetails [ ] ;
23+ name : string ;
24+ } > = ( { isOpen, onClose, data, name } ) => {
2025 if ( ! isOpen ) return null ;
2126
2227 return (
23- < div className = "fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50" onClick = { onClose } >
24- < div className = "bg-zinc-900 p-6 rounded-lg max-w-2xl w-full max-h-[80vh] overflow-y-auto text-white border border-zinc-700" onClick = { ( e ) => e . stopPropagation ( ) } >
28+ < div
29+ className = "fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50"
30+ onClick = { onClose }
31+ >
32+ < div
33+ className = "bg-zinc-900 p-6 rounded-lg max-w-2xl w-full max-h-[80vh] overflow-y-auto text-white border border-zinc-700"
34+ onClick = { ( e ) => e . stopPropagation ( ) }
35+ >
2536 < h2 className = "text-2xl font-bold mb-4" > Points Breakdown for { name } </ h2 >
2637 < div className = "overflow-x-auto" >
2738 < table className = "w-full" >
2839 < thead >
2940 < tr className = "bg-zinc-800" >
3041 < th className = "p-2 text-left border border-zinc-700" > Event</ th >
3142 < th className = "p-2 text-left border border-zinc-700" > Points</ th >
32- < th className = "p-2 text-left border border-zinc-700" > Awarded By</ th >
43+ < th className = "p-2 text-left border border-zinc-700" >
44+ Awarded By
45+ </ th >
3346 < th className = "p-2 text-left border border-zinc-700" > Date</ th >
3447 </ tr >
3548 </ thead >
3649 < tbody >
3750 { data . map ( ( detail , index ) => (
38- < tr key = { index } className = "hover:bg-soda-blue/5 transition-colors" >
51+ < tr
52+ key = { index }
53+ className = "hover:bg-soda-blue/5 transition-colors"
54+ >
3955 < td className = "p-2 border border-zinc-700" > { detail . event } </ td >
40- < td className = "p-2 border border-zinc-700" > { detail . points } </ td >
41- < td className = "p-2 border border-zinc-700" > { detail . awarded_by } </ td >
42- < td className = "p-2 border border-zinc-700" > { new Date ( detail . timestamp ) . toLocaleDateString ( ) } </ td >
56+ < td className = "p-2 border border-zinc-700" >
57+ { detail . points }
58+ </ td >
59+ < td className = "p-2 border border-zinc-700" >
60+ { detail . awarded_by }
61+ </ td >
62+ < td className = "p-2 border border-zinc-700" >
63+ { new Date ( detail . timestamp ) . toLocaleDateString ( ) }
64+ </ td >
4365 </ tr >
4466 ) ) }
4567 </ tbody >
@@ -58,21 +80,27 @@ const Modal: React.FC<{ isOpen: boolean; onClose: () => void; data: PointsDetail
5880} ;
5981
6082const Leaderboard : React . FC = ( ) => {
61- const [ leaderboardData , setLeaderboardData ] = useState < LeaderboardEntry [ ] > ( [ ] ) ;
83+ const [ leaderboardData , setLeaderboardData ] = useState < LeaderboardEntry [ ] > (
84+ [ ] ,
85+ ) ;
6286 const [ loading , setLoading ] = useState ( true ) ;
6387 const [ error , setError ] = useState < any | null > ( null ) ;
6488 const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
6589 const [ itemsPerPage , setItemsPerPage ] = useState ( 10 ) ;
66- const [ searchTerm , setSearchTerm ] = useState ( '' ) ;
90+ const [ searchTerm , setSearchTerm ] = useState ( "" ) ;
6791 const [ modalOpen , setModalOpen ] = useState ( false ) ;
68- const [ selectedEntry , setSelectedEntry ] = useState < LeaderboardEntry | null > ( null ) ;
92+ const [ selectedEntry , setSelectedEntry ] = useState < LeaderboardEntry | null > (
93+ null ,
94+ ) ;
6995
7096 useEffect ( ( ) => {
7197 const fetchData = async ( ) => {
7298 try {
73- const response = await fetch ( 'https://api.thesoda.io/leaderboard' ) ;
99+ const response = await fetch (
100+ "https://api.thesoda.io/api/points/soda/leaderboard" ,
101+ ) ;
74102 if ( ! response . ok ) {
75- throw new Error ( ' Network response was not ok' ) ;
103+ throw new Error ( " Network response was not ok" ) ;
76104 }
77105 const data : LeaderboardEntry [ ] = await response . json ( ) ;
78106 setLeaderboardData ( data ) ;
@@ -87,63 +115,94 @@ const Leaderboard: React.FC = () => {
87115 } , [ ] ) ;
88116
89117 const filteredData = leaderboardData . filter ( ( entry ) =>
90- entry . name . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )
118+ entry . name . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ,
91119 ) ;
92120
93121 const totalPages = Math . ceil ( filteredData . length / itemsPerPage ) ;
94122 const startIndex = ( currentPage - 1 ) * itemsPerPage ;
95- const currentItems = filteredData . slice ( startIndex , startIndex + itemsPerPage ) ;
123+ const currentItems = filteredData . slice (
124+ startIndex ,
125+ startIndex + itemsPerPage ,
126+ ) ;
96127
97128 const handlePrevious = ( ) => setCurrentPage ( ( prev ) => Math . max ( prev - 1 , 1 ) ) ;
98- const handleNext = ( ) => setCurrentPage ( ( prev ) => Math . min ( prev + 1 , totalPages ) ) ;
129+ const handleNext = ( ) =>
130+ setCurrentPage ( ( prev ) => Math . min ( prev + 1 , totalPages ) ) ;
99131
100132 return (
101133 < div className = "flex flex-col items-center w-full max-w-5xl mx-auto p-4 sm:p-6 my-8 sm:my-16 md:my-24 lg:my-36 shadow-md rounded-lg bg-zinc-900/80 text-white border border-zinc-800" >
102134 < Helmet >
103135 < title > Leaderboard</ title >
104- < meta name = "description" content = "Check out the top performers in SoDA's leaderboard and see who's leading the rankings!" />
105- < meta name = "keywords" content = "leaderboard, asu soda, asu, software developers association, arizona state university, benifits, computer science" />
136+ < meta
137+ name = "description"
138+ content = "Check out the top performers in SoDA's leaderboard and see who's leading the rankings!"
139+ />
140+ < meta
141+ name = "keywords"
142+ content = "leaderboard, asu soda, asu, software developers association, arizona state university, benifits, computer science"
143+ />
106144 </ Helmet >
107145
108- < h1 className = "text-2xl sm:text-3xl font-bold mb-4 sm:mb-6" > Leaderboard</ h1 >
146+ < h1 className = "text-2xl sm:text-3xl font-bold mb-4 sm:mb-6" >
147+ Leaderboard
148+ </ h1 >
109149
110150 < div className = "flex flex-col sm:flex-row justify-between items-center mb-2 w-full px-2 rounded-lg" >
111151 < input
112- type = "text"
113- placeholder = "Search by name..."
114- value = { searchTerm }
115- onChange = { ( e ) => setSearchTerm ( e . target . value ) }
116- className = "border border-zinc-700 bg-zinc-800 py-1.5 px-2 rounded w-full sm:w-64 text-white placeholder-zinc-400 focus:outline-none focus:border-soda-blue/50"
152+ type = "text"
153+ placeholder = "Search by name..."
154+ value = { searchTerm }
155+ onChange = { ( e ) => setSearchTerm ( e . target . value ) }
156+ className = "border border-zinc-700 bg-zinc-800 py-1.5 px-2 rounded w-full sm:w-64 text-white placeholder-zinc-400 focus:outline-none focus:border-soda-blue/50"
117157 />
118- < p className = "text-zinc-500 mt-2 sm:mt-0" > Click any row for point details</ p >
158+ < p className = "text-zinc-500 mt-2 sm:mt-0" >
159+ Click any row for point details
160+ </ p >
119161 </ div >
120162
121163 { /* Description of the leaderboard */ }
122- < p className = "text-zinc-300 text-center px-2 mb-4" > { /* Removed my-4, added mb-4 for spacing below */ }
123- This leaderboard tracks points earned through the{ ' ' }
124- < Link to = "/distinguishedMembers" className = "text-soda-blue hover:underline" >
164+ < p className = "text-zinc-300 text-center px-2 mb-4" >
165+ { " " }
166+ { /* Removed my-4, added mb-4 for spacing below */ }
167+ This leaderboard tracks points earned through the{ " " }
168+ < Link
169+ to = "/distinguishedMembers"
170+ className = "text-soda-blue hover:underline"
171+ >
125172 Distinguished Members Program
126173 </ Link >
127- , recognizing active participation in SoDA meetings, workshops, and community engagement.
174+ , recognizing active participation in SoDA meetings, workshops, and
175+ community engagement.
128176 </ p >
129177
130178 < div className = "w-full overflow-x-auto" >
131179 < table className = "table-auto border-collapse border border-zinc-700 w-full text-center" >
132180 < thead >
133181 < tr className = "bg-zinc-800" >
134- < th className = "w-1/2 border border-zinc-700 py-2 px-4 text-base sm:text-lg font-semibold text-center" > Name</ th >
135- < th className = "w-1/2 border border-zinc-700 py-2 px-4 text-base sm:text-lg font-semibold text-center" > Points Earned</ th >
182+ < th className = "w-1/2 border border-zinc-700 py-2 px-4 text-base sm:text-lg font-semibold text-center" >
183+ Name
184+ </ th >
185+ < th className = "w-1/2 border border-zinc-700 py-2 px-4 text-base sm:text-lg font-semibold text-center" >
186+ Points Earned
187+ </ th >
136188 </ tr >
137189 </ thead >
138190 < tbody >
139191 { currentItems . map ( ( entry , index ) => (
140192 < tr
141193 key = { index }
142- onClick = { ( ) => { setSelectedEntry ( entry ) ; setModalOpen ( true ) ; } }
194+ onClick = { ( ) => {
195+ setSelectedEntry ( entry ) ;
196+ setModalOpen ( true ) ;
197+ } }
143198 className = "hover:bg-soda-blue/5 transition-colors cursor-pointer"
144199 >
145- < td className = "border border-zinc-700 py-2 px-4 text-center" > { entry . name } </ td >
146- < td className = "border border-zinc-700 py-2 px-4 text-center" > { entry . total_points } </ td >
200+ < td className = "border border-zinc-700 py-2 px-4 text-center" >
201+ { entry . name }
202+ </ td >
203+ < td className = "border border-zinc-700 py-2 px-4 text-center" >
204+ { entry . total_points }
205+ </ td >
147206 </ tr >
148207 ) ) }
149208 </ tbody >
@@ -159,7 +218,9 @@ const Leaderboard: React.FC = () => {
159218 >
160219 Previous
161220 </ button >
162- < span className = "text-base sm:text-lg" > Page { currentPage } of { totalPages } </ span >
221+ < span className = "text-base sm:text-lg" >
222+ Page { currentPage } of { totalPages }
223+ </ span >
163224 { /* Updated Next Button Hover Styling */ }
164225 < button
165226 onClick = { handleNext }
@@ -172,9 +233,12 @@ const Leaderboard: React.FC = () => {
172233
173234 < Modal
174235 isOpen = { modalOpen }
175- onClose = { ( ) => { setModalOpen ( false ) ; setSelectedEntry ( null ) ; } }
236+ onClose = { ( ) => {
237+ setModalOpen ( false ) ;
238+ setSelectedEntry ( null ) ;
239+ } }
176240 data = { selectedEntry ?. points_details || [ ] }
177- name = { selectedEntry ?. name || '' }
241+ name = { selectedEntry ?. name || "" }
178242 />
179243 </ div >
180244 ) ;
0 commit comments