11import React , { useState , useEffect } from 'react' ;
2+ // Re-importing icons for the stats
3+ import { Users , GitFork , Star } from 'lucide-react' ;
24
35export default function ContributorsWall ( ) {
46 const [ contributors , setContributors ] = useState ( [ ] ) ;
7+ const [ repoInfo , setRepoInfo ] = useState ( null ) ; // State for repo info
58 const [ loading , setLoading ] = useState ( true ) ;
69 const [ error , setError ] = useState ( null ) ;
710
811 useEffect ( ( ) => {
9- const fetchContributors = async ( ) => {
12+ // Fetch both contributors and repo info at the same time
13+ const fetchData = async ( ) => {
1014 setLoading ( true ) ;
1115 setError ( null ) ;
1216 try {
13- const response = await fetch (
14- 'https://api.github.com/repos/commitra/react-verse/contributors?per_page=100'
15- ) ;
17+ const [ repoRes , contributorsRes ] = await Promise . all ( [
18+ fetch ( 'https://api.github.com/repos/commitra/react-verse' ) ,
19+ fetch ( 'https://api.github.com/repos/commitra/react-verse/contributors?per_page=100' )
20+ ] ) ;
1621
17- if ( ! response . ok ) {
18- // This will catch API rate-limit errors (403)
19- throw new Error ( `Failed to fetch contributors : ${ response . statusText } ` ) ;
22+ // Check both responses
23+ if ( ! repoRes . ok ) {
24+ throw new Error ( `Failed to fetch repo info : ${ repoRes . statusText } ` ) ;
2025 }
26+ if ( ! contributorsRes . ok ) {
27+ throw new Error ( `Failed to fetch contributors: ${ contributorsRes . statusText } ` ) ;
28+ }
29+
30+ const repoData = await repoRes . json ( ) ;
31+ const contributorsData = await contributorsRes . json ( ) ;
2132
22- const data = await response . json ( ) ;
23- setContributors ( data ) ;
33+ setRepoInfo ( repoData ) ; // Set the repo info
34+ setContributors ( contributorsData ) ;
2435 } catch ( err ) {
2536 setError ( err . message ) ;
2637 } finally {
2738 setLoading ( false ) ;
2839 }
2940 } ;
3041
31- fetchContributors ( ) ;
42+ fetchData ( ) ;
3243 } , [ ] ) ; // Empty dependency array ensures this runs once on mount
3344
45+ // Style object for the new stat boxes
46+ // This uses variables from your main stylesheet
47+ const statBoxStyle = {
48+ background : 'var(--bg-alt)' ,
49+ border : '1px solid var(--border)' ,
50+ padding : '0.5rem 1rem' ,
51+ borderRadius : 'var(--radius)' ,
52+ display : 'flex' ,
53+ alignItems : 'center' ,
54+ gap : '0.5rem' ,
55+ fontSize : '0.9rem' ,
56+ fontWeight : 500 ,
57+ color : 'var(--text)'
58+ } ;
59+
3460 if ( loading ) {
3561 return (
3662 < div className = "container" style = { { textAlign : 'center' , paddingTop : '5rem' } } >
37- { /* Uses the .loading class from your stylesheet */ }
3863 < h2 className = "loading" > Loading contributors...</ h2 >
3964 </ div >
4065 ) ;
@@ -43,56 +68,77 @@ export default function ContributorsWall() {
4368 if ( error ) {
4469 return (
4570 < div className = "container" style = { { textAlign : 'center' , paddingTop : '5rem' } } >
46- { /* Uses the .error class from your stylesheet */ }
4771 < h2 className = "error" > Error: { error } </ h2 >
4872 < p > You may have hit the GitHub API rate limit. Please try again later.</ p >
4973 </ div >
5074 ) ;
5175 }
5276
5377 return (
54- // Uses .container for padding and .page-transition for the animation
5578 < main className = "container page-transition" >
5679
57- { /* Uses .cards-title from your "Recipe" styles for a nice centered header */ }
5880 < h1 className = "cards-title" style = { { marginTop : '2rem' } } >
5981 React-Verse Contributors
6082 </ h1 >
6183 < p style = { { textAlign : 'center' , fontSize : '1.1rem' , opacity : 0.8 , marginTop : '-2rem' , marginBottom : '3rem' } } >
6284 Honoring the amazing developers who made this project possible.
6385 </ p >
6486
65- { /* Uses the .grid class from your stylesheet for the responsive layout */ }
87+ { /* --- NEW STATS SECTION --- */ }
88+ { repoInfo && (
89+ // Uses .flex and .wrap from your stylesheet
90+ < div
91+ className = "flex wrap"
92+ style = { {
93+ justifyContent : 'center' ,
94+ gap : '1rem' , // .gap class is 1rem
95+ marginBottom : '3rem'
96+ } }
97+ >
98+ { /* Stat Box for Stars */ }
99+ < div style = { statBoxStyle } >
100+ < Star size = { 18 } style = { { color : 'var(--primary)' } } />
101+ < span > { repoInfo . stargazers_count } Stars</ span >
102+ </ div >
103+
104+ { /* Stat Box for Forks */ }
105+ < div style = { statBoxStyle } >
106+ < GitFork size = { 18 } style = { { color : 'var(--primary)' } } />
107+ < span > { repoInfo . forks_count } Forks</ span >
108+ </ div >
109+
110+ { /* Stat Box for Contributors */ }
111+ < div style = { statBoxStyle } >
112+ < Users size = { 18 } style = { { color : 'var(--primary)' } } />
113+ < span > { contributors . length } Contributors</ span >
114+ </ div >
115+ </ div >
116+ ) }
117+ { /* --- END OF STATS SECTION --- */ }
118+
119+ { /* Uses the .grid class from your stylesheet */ }
66120 < div className = "grid" >
67121 { contributors . map ( ( contributor ) => (
68122 < a
69123 key = { contributor . id }
70124 href = { contributor . html_url }
71125 target = "_blank"
72126 rel = "noopener noreferrer"
73- style = { { textDecoration : 'none' } } // Prevents underline on the card
127+ style = { { textDecoration : 'none' } }
74128 >
75- { /* Uses the .card class from your "Recipe" styles.
76- Your CSS file already makes this:
77- - text-align: center
78- - have a background, border, and shadow
79- - animate on hover
80- */ }
129+ { /* Uses the .card class from your "Recipe" styles */ }
81130 < div className = "card" >
82- { /* This <img> tag will be styled by your ".card img" rule:
83- - 130px width/height
84- - 50% border-radius (a circle)
85- */ }
131+ { /* Styled by your ".card img" rule */ }
86132 < img
87133 src = { contributor . avatar_url }
88134 alt = { `${ contributor . login } 's avatar` }
89135 loading = "lazy"
90136 />
91137
92- { /* This <h3> will be styled by your ".card h4" or ".card h3" rule */ }
138+ { /* Styled by your ".card h4" or ".card h3" rule */ }
93139 < h4 > { contributor . login } </ h4 >
94140
95- { /* This <p> will be styled by your ".card p" rule */ }
141+ { /* Styled by your ".card p" rule */ }
96142 < p > { contributor . contributions } contributions</ p >
97143 </ div >
98144 </ a >
0 commit comments