11import { useParams } from "react-router-dom" ;
22import { useEffect , useState } from "react" ;
3+ import toast from "react-hot-toast" ;
34
45type PR = {
56 title : string ;
@@ -17,41 +18,69 @@ export default function UserProfile() {
1718 async function fetchData ( ) {
1819 if ( ! username ) return ;
1920
20- const userRes = await fetch ( `https://api.github.com/users/${ username } ` ) ;
21- const userData = await userRes . json ( ) ;
22- setProfile ( userData ) ;
21+ try {
22+ const userRes = await fetch ( `https://api.github.com/users/${ username } ` ) ;
23+ const userData = await userRes . json ( ) ;
24+ setProfile ( userData ) ;
2325
24- const prsRes = await fetch ( `https://api.github.com/search/issues?q=author:${ username } +type:pr` ) ;
25- const prsData = await prsRes . json ( ) ;
26- setPRs ( prsData . items ) ;
27- setLoading ( false ) ;
26+ const prsRes = await fetch ( `https://api.github.com/search/issues?q=author:${ username } +type:pr` ) ;
27+ const prsData = await prsRes . json ( ) ;
28+ setPRs ( prsData . items ) ;
29+ } catch ( error ) {
30+ toast . error ( "Failed to fetch user data." ) ;
31+ } finally {
32+ setLoading ( false ) ;
33+ }
2834 }
2935
3036 fetchData ( ) ;
3137 } , [ username ] ) ;
3238
39+ const handleCopyLink = ( ) => {
40+ navigator . clipboard . writeText ( window . location . href ) ;
41+ toast . success ( "🔗 Shareable link copied to clipboard!" ) ;
42+ } ;
43+
3344 if ( loading ) return < div className = "text-center mt-10" > Loading...</ div > ;
3445
46+ if ( ! profile ) return < div className = "text-center mt-10 text-red-600" > User not found.</ div > ;
47+
3548 return (
3649 < div className = "max-w-3xl mx-auto mt-10 p-4 bg-white shadow-xl rounded-xl" >
37- { profile && (
38- < div className = "text-center" >
39- < img src = { profile . avatar_url } className = "w-24 h-24 mx-auto rounded-full" />
40- < h2 className = "text-2xl font-bold mt-2" > { profile . login } </ h2 >
41- < p className = "text-gray-600" > { profile . bio } </ p >
42- </ div >
43- ) }
50+ < div className = "text-center" >
51+ < img src = { profile . avatar_url } alt = "Avatar" className = "w-24 h-24 mx-auto rounded-full" />
52+ < h2 className = "text-2xl font-bold mt-2" > { profile . login } </ h2 >
53+ < p className = "text-gray-600" > { profile . bio } </ p >
54+ < button
55+ onClick = { handleCopyLink }
56+ className = "mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition"
57+ >
58+ Copy Shareable Link
59+ </ button >
60+ </ div >
4461
4562 < h3 className = "text-xl font-semibold mt-6 mb-2" > Pull Requests</ h3 >
46- < ul className = "list-disc ml-6 space-y-2" >
47- { prs . map ( ( pr , i ) => (
48- < li key = { i } >
49- < a href = { pr . html_url } target = "_blank" className = "text-blue-600 hover:underline" >
50- { pr . title }
51- </ a >
52- </ li >
53- ) ) }
54- </ ul >
63+ { prs . length > 0 ? (
64+ < ul className = "list-disc ml-6 space-y-2" >
65+ { prs . map ( ( pr , i ) => {
66+ const repoName = pr . repository_url . split ( "/" ) . slice ( - 2 ) . join ( "/" ) ;
67+ return (
68+ < li key = { i } >
69+ < a
70+ href = { pr . html_url }
71+ target = "_blank"
72+ rel = "noopener noreferrer"
73+ className = "text-blue-600 hover:underline"
74+ >
75+ [{ repoName } ] { pr . title }
76+ </ a >
77+ </ li >
78+ ) ;
79+ } ) }
80+ </ ul >
81+ ) : (
82+ < p className = "text-gray-600" > No pull requests found for this user.</ p >
83+ ) }
5584 </ div >
5685 ) ;
5786}
0 commit comments