From 465abe682023089291452e284887a3f10ea626d1 Mon Sep 17 00:00:00 2001 From: Maniska Date: Mon, 18 May 2026 12:18:09 +0530 Subject: [PATCH 1/3] feat: add back to dashboard link on public profile --- src/app/u/[username]/page.tsx | 60 ++++++++++++++++-------------- src/components/BackToDashboard.tsx | 27 ++++++++++++++ 2 files changed, 59 insertions(+), 28 deletions(-) create mode 100644 src/components/BackToDashboard.tsx diff --git a/src/app/u/[username]/page.tsx b/src/app/u/[username]/page.tsx index b7dff3a..d48dcdd 100644 --- a/src/app/u/[username]/page.tsx +++ b/src/app/u/[username]/page.tsx @@ -3,7 +3,7 @@ import BadgeSection from "@/components/BadgeSection"; import ContributionGraph from "@/components/ContributionGraph"; import StreakTracker from "@/components/StreakTracker"; import TopRepos from "@/components/TopRepos"; - +import BackToDashboard from "@/components/BackToDashboard"; interface PublicProfileData { username: string; userId: string; @@ -86,38 +86,42 @@ export default async function PublicProfilePage({ const { username } = params; const profile = await fetchPublicProfile(username); - if (!profile) { - return ( -
-
-

- Profile Not Found -

-

- This profile is not available or is private. -

- - Back to Home - -
+if (!profile) { + return ( +
+
+

+ Profile Not Found +

+

+ This profile is not available or is private. +

+ + Back to Home +
- ); - } +
+ ); +} return (
{/* Header */} -
-

- @{profile.username}'s Profile -

-

- GitHub activity and coding stats -

-
+ {/* Header */} +
+ + +

+ @{profile.username}'s Profile +

+ +

+ GitHub activity and coding stats +

+
{/* Row 1: Contribution graph + Streak */}
diff --git a/src/components/BackToDashboard.tsx b/src/components/BackToDashboard.tsx new file mode 100644 index 0000000..a8f1518 --- /dev/null +++ b/src/components/BackToDashboard.tsx @@ -0,0 +1,27 @@ +"use client"; + +import Link from "next/link"; +import { useSession } from "next-auth/react"; + +interface Props { + username: string; +} + +export default function BackToDashboard({ username }: Props) { + const { data: session } = useSession(); + + const currentUser = session?.user?.name; + + const isOwner = currentUser === username; + + if (!isOwner) return null; + + return ( + + ← Back to dashboard + + ); +} \ No newline at end of file From 36f90fcb2c02681c81d08ad56b7f8bd6efbe4ea3 Mon Sep 17 00:00:00 2001 From: Maniska Date: Mon, 18 May 2026 18:41:19 +0530 Subject: [PATCH 2/3] Add stale PR indicators with configurable thresholds --- src/app/api/metrics/prs/route.ts | 44 ++++++++++---- src/components/PRMetrics.tsx | 100 ++++++++++++++++++++++++++----- 2 files changed, 119 insertions(+), 25 deletions(-) diff --git a/src/app/api/metrics/prs/route.ts b/src/app/api/metrics/prs/route.ts index fb28df2..43345d5 100644 --- a/src/app/api/metrics/prs/route.ts +++ b/src/app/api/metrics/prs/route.ts @@ -10,16 +10,23 @@ import { GITHUB_API } from "@/lib/github"; import { supabaseAdmin } from "@/lib/supabase"; export const dynamic = "force-dynamic"; - -interface PRMetricsBase { +interface PullRequest { + title: string; + created_at: string; + html_url: string; + state: string; +}interface PRMetricsBase { open: number; merged: number; total: number; avgReviewHours: number; mergeRate: number; + prs: PullRequest[]; } async function fetchPRMetrics(token: string): Promise { + + const searchRes = await fetch( `${GITHUB_API}/search/issues?q=type:pr+author:@me&per_page=100`, { @@ -34,7 +41,13 @@ async function fetchPRMetrics(token: string): Promise { const data = (await searchRes.json()) as { total_count: number; - items: Array<{ state: string; created_at: string; closed_at: string | null }>; +items: Array<{ + title: string; + state: string; + created_at: string; + closed_at: string | null; + html_url: string; +}>; }; const open = data.items.filter((pr) => pr.state === "open").length; @@ -51,14 +64,21 @@ async function fetchPRMetrics(token: string): Promise { 0 ) / closedPRs.length : 0; - - return { - open, - merged, - total: data.total_count, - avgReviewHours: Math.round(avgReviewMs / 3600000), - mergeRate: data.total_count > 0 ? merged / data.total_count : 0, - }; + const prs = data.items.map((pr) => ({ + title: pr.title, + created_at: pr.created_at, + html_url: pr.html_url, + state: pr.state, +})); + +return { + open, + merged, + total: data.total_count, + avgReviewHours: Math.round(avgReviewMs / 3600000), + mergeRate: data.total_count > 0 ? merged / data.total_count : 0, + prs, +}; } function formatPRMetrics(metrics: PRMetricsBase) { @@ -71,6 +91,7 @@ function formatPRMetrics(metrics: PRMetricsBase) { metrics.total > 0 ? `${Math.round(metrics.mergeRate * 100)}%` : "0%", + prs: metrics.prs, }; } @@ -129,6 +150,7 @@ export async function GET(req: NextRequest) { return { open: a.open + b.open, + prs: [...a.prs, ...b.prs], merged: mergedCount, total, avgReviewHours: Math.round(avgReviewHours * 10) / 10, diff --git a/src/components/PRMetrics.tsx b/src/components/PRMetrics.tsx index 990d13f..64f1f3b 100644 --- a/src/components/PRMetrics.tsx +++ b/src/components/PRMetrics.tsx @@ -8,6 +8,13 @@ interface PRData { merged: number; avgReviewHours: number; mergeRate: string; + prs: PullRequest[]; +} +interface PullRequest { + title: string; + created_at: string; + html_url: string; + state: string; } export default function PRMetrics() { @@ -15,7 +22,7 @@ export default function PRMetrics() { const [metrics, setMetrics] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - +const [staleDays, setStaleDays] = useState(7); const fetchMetrics = useCallback(() => { setLoading(true); setError(null); @@ -39,6 +46,21 @@ export default function PRMetrics() { fetchMetrics(); }, [fetchMetrics]); + const isStale = (createdAt: string) => { + const createdDate = new Date(createdAt); + const now = new Date(); + + const diffTime = now.getTime() - createdDate.getTime(); + + const diffDays = diffTime / (1000 * 60 * 60 * 24); + + return diffDays > staleDays; +}; +const stalePRs = + metrics?.prs.filter( + (pr) => pr.state === "open" && isStale(pr.created_at) + ) || []; + const stats = metrics ? [ { label: "Open PRs", value: metrics.open }, @@ -52,6 +74,7 @@ export default function PRMetrics() {

PR Analytics

{loading ? ( +
{[1, 2, 3, 4].map((i) => (
) : ( + <> +
+
+ {stalePRs.length} PRs stale > {staleDays} days +
+ + +
+
- {stats.map((stat) => ( -
-
- {stat.value} -
-
{stat.label}
-
- ))} -
- )} + {stats.map((stat) => ( +
+
+ {stat.value} +
+ +
+ {stat.label} +
+
+ ))} +
+ +{stalePRs.length > 0 && ( +
+

+ Stale Pull Requests +

+ +
+ {stalePRs.map((pr) => ( + + + {pr.title} + + + + Stale + + + ))} +
+
+)} + +)}
); } From bdabaa3174993d4d48efb6ff25798a51e9faa0e7 Mon Sep 17 00:00:00 2001 From: Maniska Date: Tue, 19 May 2026 16:28:22 +0530 Subject: [PATCH 3/3] feat: add stale PR indicators --- src/app/api/metrics/streak/route.ts | 11 ++++++++--- src/components/PRMetrics.tsx | 9 +++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/app/api/metrics/streak/route.ts b/src/app/api/metrics/streak/route.ts index ae35df7..0bb9fa1 100644 --- a/src/app/api/metrics/streak/route.ts +++ b/src/app/api/metrics/streak/route.ts @@ -41,9 +41,14 @@ async function fetchActiveDates( } const data = (await searchRes.json()) as { - items: Array<{ commit: { author: { date: string } } }>; - }; - + items: Array<{ + commit: { + author: { + date: string; + }; + }; + }>; +}; const activeDates = new Set(); for (const item of data.items) { activeDates.add(item.commit.author.date.slice(0, 10)); diff --git a/src/components/PRMetrics.tsx b/src/components/PRMetrics.tsx index 64f1f3b..1af7234 100644 --- a/src/components/PRMetrics.tsx +++ b/src/components/PRMetrics.tsx @@ -116,14 +116,11 @@ const stalePRs = {stats.map((stat) => (
-
- {stat.value} +className="rounded-lg bg-[var(--control)] p-4 text-center min-w-0" > +
{stat.value}
-
- {stat.label} +
{stat.label}
))}