From 1ffe964bf3078b0a732411997be9dfbdea6b3b6f Mon Sep 17 00:00:00 2001 From: Anushka Srivastava Date: Tue, 2 Sep 2025 13:38:29 +0530 Subject: [PATCH 01/21] fix: add GitHub API version header, PAT usage, router cleanup; contributors + profile working --- src/App.tsx | 53 ++-- src/Routes/Router.tsx | 30 ++- src/lib/githubFetch.ts | 18 ++ .../ContributorProfile/ContributorProfile.tsx | 64 +++-- src/pages/Contributors/Contributors.tsx | 230 ++++++++---------- 5 files changed, 219 insertions(+), 176 deletions(-) create mode 100644 src/lib/githubFetch.ts diff --git a/src/App.tsx b/src/App.tsx index b00eba8..8a46000 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,29 @@ +import { createBrowserRouter, RouterProvider, Outlet } from "react-router-dom"; import Navbar from "./components/Navbar"; import Footer from "./components/Footer"; import ScrollProgressBar from "./components/ScrollProgressBar"; import { Toaster } from "react-hot-toast"; -import Router from "./Routes/Router"; import ThemeWrapper from "./context/ThemeContext"; -function App() { +import Tracker from "./pages/Tracker/Tracker.tsx"; +import About from "./pages/About/About"; +import Contact from "./pages/Contact/Contact"; +import Contributors from "./pages/Contributors/Contributors"; +import Signup from "./pages/Signup/Signup.tsx"; +import Login from "./pages/Login/Login.tsx"; +import ContributorProfile from "./pages/ContributorProfile/ContributorProfile.tsx"; +import Home from "./pages/Home/Home.tsx"; + +function RootLayout() { return (
- -
- +
-
-
@@ -41,4 +40,28 @@ function App() { ); } -export default App; +const router = createBrowserRouter([ + { + path: "/", + element: , + children: [ + { index: true, element: }, + { path: "track", element: }, + { path: "signup", element: }, + { path: "login", element: }, + { path: "about", element: }, + { path: "contact", element: }, + { path: "contributors", element: }, + { path: "contributor/:username", element: }, + ], + }, +]); + +export default function AppRouter() { + return ( + + ); +} diff --git a/src/Routes/Router.tsx b/src/Routes/Router.tsx index 40a7861..83aeb23 100644 --- a/src/Routes/Router.tsx +++ b/src/Routes/Router.tsx @@ -1,4 +1,4 @@ -import { Routes, Route } from "react-router-dom"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; import Tracker from "../pages/Tracker/Tracker.tsx"; import About from "../pages/About/About"; import Contact from "../pages/Contact/Contact"; @@ -8,19 +8,17 @@ import Login from "../pages/Login/Login.tsx"; import ContributorProfile from "../pages/ContributorProfile/ContributorProfile.tsx"; import Home from "../pages/Home/Home.tsx"; -const Router = () => { - return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - ); -}; +const router = createBrowserRouter([ + { path: "/", element: }, + { path: "/track", element: }, + { path: "/signup", element: }, + { path: "/login", element: }, + { path: "/about", element: }, + { path: "/contact", element: }, + { path: "/contributors", element: }, + { path: "/contributor/:username", element: }, +]); -export default Router; +export default function AppRouter() { + return ; +} diff --git a/src/lib/githubFetch.ts b/src/lib/githubFetch.ts new file mode 100644 index 0000000..4c01dbe --- /dev/null +++ b/src/lib/githubFetch.ts @@ -0,0 +1,18 @@ +// src/lib/githubFetch.ts +export const GITHUB_API_VERSION = "2022-11-28"; + +export async function ghFetch( + path: string, + token: string, + init: RequestInit = {} +) { + const base = "https://api.github.com"; + const headers = new Headers(init.headers || {}); + if (token) { + headers.set("Authorization", `Bearer ${token}`); // or `token ${token}` + } + headers.set("Accept", "application/vnd.github+json"); + headers.set("X-GitHub-Api-Version", GITHUB_API_VERSION); + + return fetch(`${base}${path}`, { ...init, headers }); +} \ No newline at end of file diff --git a/src/pages/ContributorProfile/ContributorProfile.tsx b/src/pages/ContributorProfile/ContributorProfile.tsx index b4ab931..8f29111 100644 --- a/src/pages/ContributorProfile/ContributorProfile.tsx +++ b/src/pages/ContributorProfile/ContributorProfile.tsx @@ -1,16 +1,25 @@ +import { ghFetch as gh } from "../../lib/githubFetch"; import { useParams } from "react-router-dom"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; +type Profile = { + login: string; + avatar_url: string; + bio?: string | null; + html_url: string; +}; + type PR = { + id: number; title: string; html_url: string; repository_url: string; }; export default function ContributorProfile() { - const { username } = useParams(); - const [profile, setProfile] = useState(null); + const { username } = useParams<{ username: string }>(); + const [profile, setProfile] = useState(null); const [prs, setPRs] = useState([]); const [loading, setLoading] = useState(true); @@ -18,18 +27,28 @@ export default function ContributorProfile() { async function fetchData() { if (!username) return; + const token = + localStorage.getItem("gh_pat") || + localStorage.getItem("github_pat") || + localStorage.getItem("token") || + ""; + try { - const userRes = await fetch(`https://api.github.com/users/${username}`); - const userData = await userRes.json(); + // fetch user profile + const userRes = await gh(`/users/${encodeURIComponent(username)}`, token); + const userData = (await userRes.json()) as Profile; setProfile(userData); - const prsRes = await fetch( - `https://api.github.com/search/issues?q=author:${username}+type:pr` + // fetch PRs authored by the user (latest first) + const q = `author:${encodeURIComponent(username)}+type:pr`; + const prsRes = await gh( + `/search/issues?q=${q}&per_page=100&sort=updated&order=desc`, + token ); const prsData = await prsRes.json(); - setPRs(prsData.items); - } catch (error) { - toast.error("Failed to fetch user data."); + setPRs(Array.isArray(prsData.items) ? (prsData.items as PR[]) : []); + } catch (error: any) { + toast.error(`Failed to fetch user data. ${error?.message ?? ""}`); } finally { setLoading(false); } @@ -39,8 +58,12 @@ export default function ContributorProfile() { }, [username]); const handleCopyLink = () => { - navigator.clipboard.writeText(window.location.href); - toast.success("đź”— Shareable link copied to clipboard!"); + try { + navigator.clipboard.writeText(window.location.href); + toast.success("đź”— Shareable link copied to clipboard!"); + } catch { + toast.error("Could not copy link"); + } }; if (loading) return
Loading...
; @@ -55,11 +78,20 @@ export default function ContributorProfile() {
Avatar -

{profile.login}

-

{profile.bio}

+

+ + {profile.login} + +

+ {profile.bio &&

{profile.bio}

} - - - - ))} - - + return ( +
+

Contributors

+ {loading &&
Loading…
} + {error && ( +
{error}
+ )} +
); -}; - -export default ContributorsPage; +} From 1985c85ed14730c09b10a35bf17843569e5a45ef Mon Sep 17 00:00:00 2001 From: Anushka Srivastava Date: Tue, 2 Sep 2025 14:01:40 +0530 Subject: [PATCH 02/21] perf(router): lazy-load routes + suspense fallback --- src/App.tsx | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 8a46000..4485005 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,18 +1,19 @@ import { createBrowserRouter, RouterProvider, Outlet } from "react-router-dom"; +import { lazy, Suspense } from "react"; import Navbar from "./components/Navbar"; import Footer from "./components/Footer"; import ScrollProgressBar from "./components/ScrollProgressBar"; import { Toaster } from "react-hot-toast"; import ThemeWrapper from "./context/ThemeContext"; -import Tracker from "./pages/Tracker/Tracker.tsx"; -import About from "./pages/About/About"; -import Contact from "./pages/Contact/Contact"; -import Contributors from "./pages/Contributors/Contributors"; -import Signup from "./pages/Signup/Signup.tsx"; -import Login from "./pages/Login/Login.tsx"; -import ContributorProfile from "./pages/ContributorProfile/ContributorProfile.tsx"; -import Home from "./pages/Home/Home.tsx"; +const Home = lazy(() => import("./pages/Home/Home.tsx")); +const Tracker = lazy(() => import("./pages/Tracker/Tracker.tsx")); +const About = lazy(() => import("./pages/About/About")); +const Contact = lazy(() => import("./pages/Contact/Contact")); +const Contributors = lazy(() => import("./pages/Contributors/Contributors")); +const Signup = lazy(() => import("./pages/Signup/Signup.tsx")); +const Login = lazy(() => import("./pages/Login/Login.tsx")); +const ContributorProfile = lazy(() => import("./pages/ContributorProfile/ContributorProfile.tsx")); function RootLayout() { return ( @@ -21,7 +22,9 @@ function RootLayout() {
- + Loading…
}> + +