@@ -131,6 +131,112 @@ export default function EventLeaderboardIndiPage() {
131131 }
132132 } , [ router , searchParams ] ) ;
133133
134+ const exportForCert = ( ) => {
135+ if ( ! filteredParticipants || ! eventMetadata ) return ;
136+
137+ // Filter top 5 participants based on overallTotal (descending)
138+ // Assuming filteredParticipants are already sorted by overallTotal,
139+ // otherwise we would need to sort first:
140+ // const sorted = [...filteredParticipants].sort((a, b) => (b.overallTotal || 0) - (a.overallTotal || 0));
141+ const topParticipants = filteredParticipants . slice ( 0 , 5 ) ;
142+
143+ if ( topParticipants . length === 0 ) return ;
144+
145+ const flatList = topParticipants . map ( ( p , index ) => {
146+ // Determine effective student name and related details if substituted
147+ const isSubstituted = p . substitute && p . substitute [ eventMetadata . name ] ;
148+ const studentName = isSubstituted
149+ ? p . substitute [ eventMetadata . name ] . newStudentName
150+ : p . studentFullName ;
151+
152+ const studentGender = isSubstituted
153+ ? p . substitute [ eventMetadata . name ] . newStudentGender
154+ : p . gender ;
155+
156+ const studentDOB = isSubstituted
157+ ? p . substitute [ eventMetadata . name ] . newStudentDOB
158+ : p . dateOfBirth ;
159+
160+ const studentGroup = isSubstituted
161+ ? p . substitute [ eventMetadata . name ] . newStudentGroup
162+ : p . studentGroup ;
163+
164+ return {
165+ eventName : eventName ,
166+ Rank : index + 1 ,
167+ ...p ,
168+ // Override with substituted values where applicable for the certificate
169+ studentFullName : studentName ,
170+ gender : studentGender ,
171+ dateOfBirth : studentDOB ,
172+ studentGroup : studentGroup ,
173+ // Format the total score
174+ OverallTotal : parseFloat ( p . overallTotal ?? 0 ) . toFixed ( 2 ) ,
175+ } ;
176+ } ) ;
177+
178+ const allKeys = new Set ( ) ;
179+ flatList . forEach ( ( item ) => {
180+ Object . keys ( item ) . forEach ( ( key ) => {
181+ const val = item [ key ] ;
182+ // Skip complex objects/arrays except for specific ones if needed.
183+ // We generally want scalar values.
184+ if (
185+ val !== null &&
186+ typeof val === 'object' &&
187+ ! Array . isArray ( val ) &&
188+ key !== 'Rank' // Rank is scalar but good to be explicit
189+ ) {
190+ return ;
191+ }
192+ allKeys . add ( key ) ;
193+ } ) ;
194+ } ) ;
195+
196+ const headers = Array . from ( allKeys ) . sort ( ) ;
197+ // Prioritize common certificate fields
198+ const prioritized = [
199+ 'eventName' ,
200+ 'Rank' ,
201+ 'studentFullName' ,
202+ 'studentId' ,
203+ 'district' ,
204+ 'samithiName' ,
205+ 'OverallTotal' ,
206+ 'studentGroup' ,
207+ ] ;
208+ const sortedHeaders = [
209+ ...prioritized . filter ( ( h ) => headers . includes ( h ) ) ,
210+ ...headers . filter ( ( h ) => ! prioritized . includes ( h ) ) ,
211+ ] ;
212+
213+ const escapeCSV = ( value ) => {
214+ if ( value == null ) return '' ;
215+ if ( Array . isArray ( value ) ) return `"${ value . join ( '; ' ) } "` ;
216+ const str = String ( value ) ;
217+ if ( str . includes ( ',' ) || str . includes ( '\n' ) || str . includes ( '"' ) ) {
218+ return `"${ str . replace ( / " / g, '""' ) } "` ;
219+ }
220+ return str ;
221+ } ;
222+
223+ const csv = [
224+ sortedHeaders . map ( escapeCSV ) . join ( ',' ) ,
225+ ...flatList . map ( ( row ) =>
226+ sortedHeaders . map ( ( header ) => escapeCSV ( row [ header ] ) ) . join ( ',' ) ,
227+ ) ,
228+ ] . join ( '\r\n' ) ;
229+
230+ const BOM = '\uFEFF' ;
231+ const blob = new Blob ( [ BOM + csv ] , { type : 'text/csv;charset=utf-8;' } ) ;
232+ const url = URL . createObjectURL ( blob ) ;
233+ const link = document . createElement ( 'a' ) ;
234+ link . href = url ;
235+ link . download = `${ eventName } _top5_cert_export.csv` ;
236+ link . click ( ) ;
237+ URL . revokeObjectURL ( url ) ;
238+ } ;
239+
134240 const exportToCSV = ( ) => {
135241 if ( ! filteredParticipants || ! eventMetadata || orderedJudges . length === 0 )
136242 return ;
@@ -345,12 +451,20 @@ export default function EventLeaderboardIndiPage() {
345451 < div className = "rounded-2xl p-4 my-4 bg-white border overflow-x-auto" >
346452 < div className = "w-full flex justify-between" >
347453 < h1 className = "text-2xl font-bold" > Leaderboard</ h1 >
348- < button
349- onClick = { exportToCSV }
350- className = "px-4 py-1 text-md bg-blue-500 text-white rounded-lg hover:bg-blue-600"
351- >
352- Export
353- </ button >
454+ < div className = "flex gap-2" >
455+ < button
456+ onClick = { exportForCert }
457+ className = "px-4 py-1 text-md bg-green-500 text-white rounded-lg hover:bg-green-600"
458+ >
459+ Export for Cert
460+ </ button >
461+ < button
462+ onClick = { exportToCSV }
463+ className = "px-4 py-1 text-md bg-blue-500 text-white rounded-lg hover:bg-blue-600"
464+ >
465+ Export
466+ </ button >
467+ </ div >
354468 </ div >
355469 < table className = "table-auto w-full mt-4" >
356470 < thead >
0 commit comments