1+ // src/components/dashboard/LeaderBoard/ContributorCard.tsx
2+ import React , { useRef , useState } from "react" ;
3+ import { toPng } from "html-to-image" ;
4+ import { FaGithub , FaDownload , FaTrophy } from "react-icons/fa" ;
5+
6+ interface ContributorCardProps {
7+ username : string ;
8+ avatar : string ;
9+ prs : number ;
10+ points : number ;
11+ rank : number ;
12+ badges : string [ ] ;
13+ isDark : boolean ;
14+ }
15+
16+ export default function ContributorCard ( {
17+ username,
18+ avatar,
19+ prs,
20+ points,
21+ rank,
22+ badges,
23+ isDark,
24+ } : ContributorCardProps ) {
25+ const [ open , setOpen ] = useState ( false ) ;
26+ const cardRef = useRef < HTMLDivElement > ( null ) ;
27+
28+ const handleDownload = async ( ) => {
29+ if ( ! cardRef . current ) return ;
30+ const dataUrl = await toPng ( cardRef . current , { cacheBust : true } ) ;
31+ const link = document . createElement ( "a" ) ;
32+ link . download = `${ username } -recodehive-card.png` ;
33+ link . href = dataUrl ;
34+ link . click ( ) ;
35+ } ;
36+
37+ return (
38+ < >
39+ { /* Trigger button — sits right of "Contribute on GitHub" */ }
40+ < button
41+ className = "cta-button"
42+ onClick = { ( ) => setOpen ( true ) }
43+ style = { { marginLeft : 12 } }
44+ aria-label = "Generate your achievement card"
45+ >
46+ < FaTrophy style = { { marginRight : 8 } } />
47+ My Achievement Card
48+ </ button >
49+
50+ { /* Modal overlay */ }
51+ { open && (
52+ < div
53+ className = "card-modal-overlay"
54+ onClick = { ( ) => setOpen ( false ) }
55+ >
56+ < div
57+ className = "card-modal-inner"
58+ onClick = { ( e ) => e . stopPropagation ( ) }
59+ >
60+ { /* The actual downloadable card */ }
61+ < div
62+ ref = { cardRef }
63+ className = { `achievement-card ${ isDark ? "dark" : "light" } ` }
64+ >
65+ < div className = "achievement-card-header" >
66+ < img src = "/img/logo.png" alt = "RecodeHive" className = "card-logo" />
67+ < span className = "card-brand" > recodehive</ span >
68+ </ div >
69+ < img src = { avatar } alt = { username } className = "card-avatar" />
70+ < h2 className = "card-username" > { username } </ h2 >
71+ < div className = "card-rank" > #{ rank } on Leaderboard</ div >
72+ < div className = "card-stats" >
73+ < div className = "card-stat" >
74+ < span className = "card-stat-value" > { prs } </ span >
75+ < span className = "card-stat-label" > PRs Merged</ span >
76+ </ div >
77+ < div className = "card-stat" >
78+ < span className = "card-stat-value" > { points } </ span >
79+ < span className = "card-stat-label" > Points</ span >
80+ </ div >
81+ </ div >
82+ { badges . length > 0 && (
83+ < div className = "card-badges" >
84+ { badges . slice ( 0 , 5 ) . map ( ( badge , i ) => (
85+ < img key = { i } src = { badge } alt = { `badge-${ i } ` } className = "card-badge-icon" />
86+ ) ) }
87+ </ div >
88+ ) }
89+ < div className = "card-footer" >
90+ < FaGithub /> github.com/recodehive
91+ </ div >
92+ </ div >
93+
94+ { /* Action buttons */ }
95+ < div className = "card-modal-actions" >
96+ < button className = "cta-button" onClick = { handleDownload } >
97+ < FaDownload style = { { marginRight : 8 } } />
98+ Download PNG
99+ </ button >
100+ < button
101+ className = "cta-button"
102+ style = { { background : "#6b7280" , marginLeft : 8 } }
103+ onClick = { ( ) => setOpen ( false ) }
104+ >
105+ Close
106+ </ button >
107+ </ div >
108+ </ div >
109+ </ div >
110+ ) }
111+ </ >
112+ ) ;
113+ }
0 commit comments