Skip to content

Commit 2542300

Browse files
Merge pull request #132 from itschetna/feature/shareable-pr-link
Feature: Add shareable link to user’s PRs
2 parents 6ede2b4 + 25cd0e6 commit 2542300

File tree

2 files changed

+55
-25
lines changed

2 files changed

+55
-25
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@
2828
"@eslint/js": "^9.13.0",
2929
"@types/jasmine": "^5.1.8",
3030
"@types/node": "^22.10.1",
31-
"@types/react": "^18.3.12",
32-
"@types/react-dom": "^18.3.1",
31+
"@types/react": "^18.3.23",
32+
"@types/react-dom": "^18.3.7",
3333
"@types/react-redux": "^7.1.34",
34+
"@types/react-router-dom": "^5.3.3",
3435
"@vitejs/plugin-react-swc": "^3.5.0",
3536
"autoprefixer": "^10.4.20",
3637
"bcryptjs": "^3.0.2",
Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useParams } from "react-router-dom";
22
import { useEffect, useState } from "react";
3+
import toast from "react-hot-toast";
34

45
type 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

Comments
 (0)