@@ -3,7 +3,7 @@ import React, { JSX, useState } from "react";
33import { motion } from "framer-motion" ;
44import { FaStar , FaCode , FaUsers , FaGithub , FaSearch } from "react-icons/fa" ;
55import { ChevronRight , ChevronLeft } from "lucide-react" ;
6- import { useColorMode } from "@docusaurus/theme-common " ;
6+ import { useSafeColorMode } from "@site/src/utils/useSafeColorMode " ;
77import { useCommunityStatsContext } from "@site/src/lib/statsProvider" ;
88import PRListModal from "./PRListModal" ;
99import { mockContributors } from "./mockData" ;
@@ -29,6 +29,7 @@ interface Contributor {
2929 points : number ;
3030 prs : number ;
3131 prDetails ?: PRDetails [ ] ;
32+ badges ?: string [ ] ; // Array of badge image paths
3233}
3334
3435interface Stats {
@@ -37,6 +38,69 @@ interface Stats {
3738 flooredTotalPoints : number ;
3839}
3940
41+ // Badge configuration - maps badge numbers to achievement criteria
42+ const BADGE_CONFIG = [
43+ { image : "/badges/1.png" , name : "First Contribution" , criteria : ( prs : number ) => prs >= 1 } ,
44+ { image : "/badges/2.png" , name : "Bronze Contributor" , criteria : ( prs : number ) => prs >= 5 } ,
45+ { image : "/badges/3.png" , name : "Silver Contributor" , criteria : ( prs : number ) => prs >= 10 } ,
46+ { image : "/badges/4.png" , name : "Gold Contributor" , criteria : ( prs : number ) => prs >= 25 } ,
47+ { image : "/badges/5.png" , name : "Platinum Contributor" , criteria : ( prs : number ) => prs >= 50 } ,
48+ { image : "/badges/6.png" , name : "Diamond Contributor" , criteria : ( prs : number ) => prs >= 100 } ,
49+ { image : "/badges/7.png" , name : "Points Master" , criteria : ( _ : number , points : number ) => points >= 500 } ,
50+ { image : "/badges/8.png" , name : "Elite Contributor" , criteria : ( prs : number ) => prs >= 200 } ,
51+ { image : "/badges/9.png" , name : "Legendary Contributor" , criteria : ( prs : number ) => prs >= 500 } ,
52+ { image : "/badges/10.png" , name : "Hall of Fame" , criteria : ( prs : number , points : number ) => prs >= 1000 || points >= 5000 } ,
53+ ] ;
54+
55+ /**
56+ * Determines which badges a contributor should have based on their stats
57+ */
58+ function getContributorBadges ( contributor : Contributor , rank : number ) : string [ ] {
59+ const badges : string [ ] = [ ] ;
60+
61+ // Special rank-based badges
62+ if ( rank === 1 ) {
63+ badges . push ( "/badges/10.png" ) ; // Hall of Fame for #1
64+ } else if ( rank === 2 ) {
65+ badges . push ( "/badges/9.png" ) ; // Legendary for #2
66+ } else if ( rank === 3 ) {
67+ badges . push ( "/badges/8.png" ) ; // Elite for #3
68+ }
69+
70+ // Achievement-based badges
71+ BADGE_CONFIG . forEach ( ( badge ) => {
72+ if ( badge . criteria ( contributor . prs , contributor . points ) ) {
73+ // Avoid duplicates
74+ if ( ! badges . includes ( badge . image ) ) {
75+ badges . push ( badge . image ) ;
76+ }
77+ }
78+ } ) ;
79+
80+ return badges ;
81+ }
82+
83+ /**
84+ * Badge display component
85+ */
86+ function ContributorBadges ( { badges } : { badges : string [ ] } ) {
87+ if ( ! badges || badges . length === 0 ) return null ;
88+
89+ return (
90+ < div className = "contributor-badges" >
91+ { badges . map ( ( badge , index ) => (
92+ < img
93+ key = { index }
94+ src = { badge }
95+ alt = { `Badge ${ index + 1 } ` }
96+ className = "contributor-badge-icon"
97+ title = { BADGE_CONFIG . find ( b => b . image === badge ) ?. name || "Achievement Badge" }
98+ />
99+ ) ) }
100+ </ div >
101+ ) ;
102+ }
103+
40104function Badge ( {
41105 count,
42106 label,
@@ -93,9 +157,9 @@ function TopPerformerCard({
93157 rank : number ;
94158 onPRClick : ( contributor : Contributor ) => void ;
95159} ) {
96- const { colorMode } = useColorMode ( ) ;
97- const isDark = colorMode === "dark" ;
160+ const { isDark } = useSafeColorMode ( ) ;
98161 const rankClass = rank === 1 ? "top-1" : rank === 2 ? "top-2" : "top-3" ;
162+ const badges = getContributorBadges ( contributor , rank ) ;
99163
100164 return (
101165 < div className = { `top-performer-card ${ isDark ? "dark" : "light" } ` } >
@@ -116,6 +180,7 @@ function TopPerformerCard({
116180 >
117181 { contributor . username }
118182 </ a >
183+ < ContributorBadges badges = { badges } />
119184 < div className = "badges-container" >
120185 < Badge
121186 count = { contributor . prs }
@@ -146,8 +211,7 @@ export default function LeaderBoard(): JSX.Element {
146211 setTimeFilter,
147212 } = useCommunityStatsContext ( ) ;
148213
149- const { colorMode } = useColorMode ( ) ;
150- const isDark = colorMode === "dark" ;
214+ const { isDark } = useSafeColorMode ( ) ;
151215
152216 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
153217 const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
@@ -377,6 +441,7 @@ export default function LeaderBoard(): JSX.Element {
377441 />
378442 < div className = "details" >
379443 < p className = "username" > { filteredContributors [ 1 ] . username } </ p >
444+ < ContributorBadges badges = { getContributorBadges ( filteredContributors [ 1 ] , 2 ) } />
380445 < div className = "stats" >
381446 < button
382447 className = "prs"
@@ -402,6 +467,7 @@ export default function LeaderBoard(): JSX.Element {
402467 />
403468 < div className = "details" >
404469 < p className = "username" > { filteredContributors [ 0 ] . username } </ p >
470+ < ContributorBadges badges = { getContributorBadges ( filteredContributors [ 0 ] , 1 ) } />
405471 < div className = "stats" >
406472 < button
407473 className = "prs"
@@ -427,6 +493,7 @@ export default function LeaderBoard(): JSX.Element {
427493 />
428494 < div className = "details" >
429495 < p className = "username" > { filteredContributors [ 2 ] . username } </ p >
496+ < ContributorBadges badges = { getContributorBadges ( filteredContributors [ 2 ] , 3 ) } />
430497 < div className = "stats" >
431498 < button
432499 className = "prs"
@@ -574,6 +641,7 @@ export default function LeaderBoard(): JSX.Element {
574641 < div className = "contributor-cell username-cell" > User</ div >
575642 < div className = "contributor-cell prs-cell" > PRs</ div >
576643 < div className = "contributor-cell points-cell" > Points</ div >
644+ < div className = "contributor-cell badges-cell" > Badges</ div >
577645 </ div >
578646 { currentItems . map ( ( contributor , index ) => (
579647 < motion . div
@@ -629,6 +697,11 @@ export default function LeaderBoard(): JSX.Element {
629697 color = { { background : "#ede9fe" , color : "#7c3aed" } }
630698 />
631699 </ div >
700+ < div className = "contributor-cell badges-cell" >
701+ < ContributorBadges
702+ badges = { getContributorBadges ( contributor , indexOfFirst + index + 1 ) }
703+ />
704+ </ div >
632705 </ motion . div >
633706 ) ) }
634707
0 commit comments