Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
"devDependencies": {
"@eslint/js": "^9.13.0",
"@types/node": "^22.10.1",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
"@types/react-redux": "^7.1.34",
"@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.13.0",
Expand Down
75 changes: 52 additions & 23 deletions src/pages/UserProfile/UserProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";

type PR = {
title: string;
Expand All @@ -17,41 +18,69 @@ export default function UserProfile() {
async function fetchData() {
if (!username) return;

const userRes = await fetch(`https://api.github.com/users/${username}`);
const userData = await userRes.json();
setProfile(userData);
try {
const userRes = await fetch(`https://api.github.com/users/${username}`);
const userData = await userRes.json();
setProfile(userData);

const prsRes = await fetch(`https://api.github.com/search/issues?q=author:${username}+type:pr`);
const prsData = await prsRes.json();
setPRs(prsData.items);
setLoading(false);
const prsRes = await fetch(`https://api.github.com/search/issues?q=author:${username}+type:pr`);
const prsData = await prsRes.json();
setPRs(prsData.items);
} catch (error) {
toast.error("Failed to fetch user data.");
} finally {
setLoading(false);
}
}

fetchData();
}, [username]);

const handleCopyLink = () => {
navigator.clipboard.writeText(window.location.href);
toast.success("🔗 Shareable link copied to clipboard!");
};

if (loading) return <div className="text-center mt-10">Loading...</div>;

if (!profile) return <div className="text-center mt-10 text-red-600">User not found.</div>;

return (
<div className="max-w-3xl mx-auto mt-10 p-4 bg-white shadow-xl rounded-xl">
{profile && (
<div className="text-center">
<img src={profile.avatar_url} className="w-24 h-24 mx-auto rounded-full" />
<h2 className="text-2xl font-bold mt-2">{profile.login}</h2>
<p className="text-gray-600">{profile.bio}</p>
</div>
)}
<div className="text-center">
<img src={profile.avatar_url} alt="Avatar" className="w-24 h-24 mx-auto rounded-full" />
<h2 className="text-2xl font-bold mt-2">{profile.login}</h2>
<p className="text-gray-600">{profile.bio}</p>
<button
onClick={handleCopyLink}
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition"
>
Copy Shareable Link
</button>
</div>

<h3 className="text-xl font-semibold mt-6 mb-2">Pull Requests</h3>
<ul className="list-disc ml-6 space-y-2">
{prs.map((pr, i) => (
<li key={i}>
<a href={pr.html_url} target="_blank" className="text-blue-600 hover:underline">
{pr.title}
</a>
</li>
))}
</ul>
{prs.length > 0 ? (
<ul className="list-disc ml-6 space-y-2">
{prs.map((pr, i) => {
const repoName = pr.repository_url.split("/").slice(-2).join("/");
return (
<li key={i}>
<a
href={pr.html_url}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:underline"
>
[{repoName}] {pr.title}
</a>
</li>
);
})}
</ul>
) : (
<p className="text-gray-600">No pull requests found for this user.</p>
)}
</div>
);
}