11import React , { useState , useEffect } from 'react' ;
2- import { Users , GitFork , Star , ExternalLink } from 'lucide-react' ;
2+ // Re-importing icons for the stats
3+ import { Users , GitFork , Star } from 'lucide-react' ;
34
45export default function ContributorsWall ( ) {
56 const [ contributors , setContributors ] = useState ( [ ] ) ;
7+ const [ repoInfo , setRepoInfo ] = useState ( null ) ; // State for repo info
68 const [ loading , setLoading ] = useState ( true ) ;
79 const [ error , setError ] = useState ( null ) ;
8- const [ repoInfo , setRepoInfo ] = useState ( null ) ;
910
1011 useEffect ( ( ) => {
11- fetchContributors ( ) ;
12- fetchRepoInfo ( ) ;
13- } , [ ] ) ;
12+ // Fetch both contributors and repo info at the same time
13+ const fetchData = async ( ) => {
14+ setLoading ( true ) ;
15+ setError ( null ) ;
16+ try {
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+ ] ) ;
1421
15- const fetchRepoInfo = async ( ) => {
16- try {
17- const response = await fetch ( 'https://api.github.com/repos/commitra/react-verse' ) ;
18- const data = await response . json ( ) ;
19- setRepoInfo ( data ) ;
20- } catch ( err ) {
21- console . error ( 'Error fetching repo info:' , err ) ;
22- }
23- } ;
22+ // Check both responses
23+ if ( ! repoRes . ok ) {
24+ throw new Error ( `Failed to fetch repo info: ${ repoRes . statusText } ` ) ;
25+ }
26+ if ( ! contributorsRes . ok ) {
27+ throw new Error ( `Failed to fetch contributors: ${ contributorsRes . statusText } ` ) ;
28+ }
2429
25- const fetchContributors = async ( ) => {
26- try {
27- setLoading ( true ) ;
28- const response = await fetch (
29- 'https://api.github.com/repos/commitra/react-verse/contributors?per_page=100'
30- ) ;
31-
32- if ( ! response . ok ) {
33- throw new Error ( 'Failed to fetch contributors' ) ;
30+ const repoData = await repoRes . json ( ) ;
31+ const contributorsData = await contributorsRes . json ( ) ;
32+
33+ setRepoInfo ( repoData ) ; // Set the repo info
34+ setContributors ( contributorsData ) ;
35+ } catch ( err ) {
36+ setError ( err . message ) ;
37+ } finally {
38+ setLoading ( false ) ;
3439 }
35-
36- const data = await response . json ( ) ;
37- setContributors ( data ) ;
38- setLoading ( false ) ;
39- } catch ( err ) {
40- setError ( err . message ) ;
41- setLoading ( false ) ;
42- }
40+ } ;
41+
42+ fetchData ( ) ;
43+ } , [ ] ) ; // Empty dependency array ensures this runs once on mount
44+
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)'
4358 } ;
4459
4560 if ( loading ) {
4661 return (
47- < div className = "min-h-screen bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 flex items-center justify-center" >
48- < div className = "text-center" >
49- < div className = "animate-spin rounded-full h-16 w-16 border-t-4 border-b-4 border-purple-400 mx-auto mb-4" > </ div >
50- < p className = "text-white text-xl" > Loading contributors...</ p >
51- </ div >
62+ < div className = "container" style = { { textAlign : 'center' , paddingTop : '5rem' } } >
63+ < h2 className = "loading" > Loading contributors...</ h2 >
5264 </ div >
5365 ) ;
5466 }
5567
5668 if ( error ) {
5769 return (
58- < div className = "min-h-screen bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 flex items-center justify-center" >
59- < div className = "bg-red-500 bg-opacity-20 border border-red-400 rounded-lg p-6 max-w-md" >
60- < p className = "text-red-200 text-center" > Error: { error } </ p >
61- </ div >
70+ < div className = "container" style = { { textAlign : 'center' , paddingTop : '5rem' } } >
71+ < h2 className = "error" > Error: { error } </ h2 >
72+ < p > You may have hit the GitHub API rate limit. Please try again later.</ p >
6273 </ div >
6374 ) ;
6475 }
6576
6677 return (
67- < div className = "min-h-screen bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 py-12 px-4" >
68- < div className = "max-w-7xl mx-auto" >
69- { /* Header */ }
70- < div className = "text-center mb-12" >
71- < div className = "inline-block bg-purple-500 bg-opacity-20 rounded-full px-6 py-2 mb-4" >
72- < Users className = "inline-block mr-2 text-purple-300" size = { 24 } />
73- < span className = "text-purple-200 font-semibold" > Wall of Contributors</ span >
78+ < main className = "container page-transition" >
79+
80+ < h1 className = "cards-title" style = { { marginTop : '2rem' } } >
81+ React-Verse Contributors
82+ </ h1 >
83+ < p style = { { textAlign : 'center' , fontSize : '1.1rem' , opacity : 0.8 , marginTop : '-2rem' , marginBottom : '3rem' } } >
84+ Honoring the amazing developers who made this project possible.
85+ </ p >
86+
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 >
74102 </ div >
75- < h1 className = "text-5xl font-bold text-white mb-4" >
76- React-Verse Contributors
77- </ h1 >
78- < p className = "text-blue-200 text-lg mb-6" >
79- Honoring the amazing developers who made this project possible
80- </ p >
81103
82- { repoInfo && (
83- < div className = "flex justify-center gap-6 text-white" >
84- < div className = "flex items-center gap-2 bg-white bg-opacity-10 px-4 py-2 rounded-full" >
85- < Star size = { 20 } className = "text-yellow-400" />
86- < span > { repoInfo . stargazers_count } Stars</ span >
87- </ div >
88- < div className = "flex items-center gap-2 bg-white bg-opacity-10 px-4 py-2 rounded-full" >
89- < GitFork size = { 20 } className = "text-blue-400" />
90- < span > { repoInfo . forks_count } Forks</ span >
91- </ div >
92- < div className = "flex items-center gap-2 bg-white bg-opacity-10 px-4 py-2 rounded-full" >
93- < Users size = { 20 } className = "text-green-400" />
94- < span > { contributors . length } Contributors</ span >
95- </ div >
96- </ div >
97- ) }
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 >
98115 </ div >
116+ ) }
117+ { /* --- END OF STATS SECTION --- */ }
99118
100- { /* Repository Link */ }
101- < div className = "text-center mb-12" >
119+ { /* Uses the .grid class from your stylesheet */ }
120+ < div className = "grid" >
121+ { contributors . map ( ( contributor ) => (
102122 < a
103- href = "https://github.com/commitra/react-verse"
123+ key = { contributor . id }
124+ href = { contributor . html_url }
104125 target = "_blank"
105126 rel = "noopener noreferrer"
106- className = "inline-flex items-center gap-2 bg-white bg-opacity-10 hover:bg-opacity-20 text-white px-6 py-3 rounded-lg transition-all duration-300 hover:scale-105"
127+ style = { { textDecoration : 'none' } }
107128 >
108- View Repository
109- < ExternalLink size = { 18 } />
129+ { /* Uses the .card class from your "Recipe" styles */ }
130+ < div className = "card" >
131+ { /* Styled by your ".card img" rule */ }
132+ < img
133+ src = { contributor . avatar_url }
134+ alt = { `${ contributor . login } 's avatar` }
135+ loading = "lazy"
136+ />
137+
138+ { /* Styled by your ".card h4" or ".card h3" rule */ }
139+ < h4 > { contributor . login } </ h4 >
140+
141+ { /* Styled by your ".card p" rule */ }
142+ < p > { contributor . contributions } contributions</ p >
143+ </ div >
110144 </ a >
111- </ div >
112-
113- { /* Contributors Grid */ }
114- < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-6" >
115- { contributors . map ( ( contributor , index ) => (
116- < a
117- key = { contributor . id }
118- href = { contributor . html_url }
119- target = "_blank"
120- rel = "noopener noreferrer"
121- className = "group"
122- >
123- < div className = "bg-white bg-opacity-10 backdrop-blur-lg rounded-xl p-4 hover:bg-opacity-20 transition-all duration-300 hover:scale-105 hover:shadow-2xl border border-white border-opacity-20" >
124- < div className = "relative mb-3" >
125- < img
126- src = { contributor . avatar_url }
127- alt = { contributor . login }
128- className = "w-full aspect-square rounded-full border-4 border-purple-400 group-hover:border-pink-400 transition-colors duration-300"
129- />
130- < div className = "absolute -bottom-2 -right-2 bg-gradient-to-r from-purple-500 to-pink-500 text-white text-xs font-bold rounded-full w-8 h-8 flex items-center justify-center border-2 border-white" >
131- #{ index + 1 }
132- </ div >
133- </ div >
134- < h3 className = "text-white font-semibold text-sm text-center truncate mb-1" >
135- { contributor . login }
136- </ h3 >
137- < p className = "text-blue-200 text-xs text-center" >
138- { contributor . contributions } contributions
139- </ p >
140- </ div >
141- </ a >
142- ) ) }
143- </ div >
144-
145- { /* Footer */ }
146- < div className = "text-center mt-12" >
147- < p className = "text-blue-300 text-sm" >
148- Thank you to all our contributors for making React-Verse awesome! 🎉
149- </ p >
150- </ div >
145+ ) ) }
151146 </ div >
152- </ div >
147+ </ main >
153148 ) ;
154149}
0 commit comments