From 7a2618ceef1e356da5b6644e96c5b883fcfee347 Mon Sep 17 00:00:00 2001 From: ShaikHusnaThahera Date: Tue, 9 Jun 2026 15:18:36 +0530 Subject: [PATCH 1/5] refactor: convert dashboard to three-column layout --- package-lock.json | 12 ------------ src/app/(app)/dashboard/page.tsx | 11 +++++++---- tsconfig.json | 16 +++++++++++++--- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 815631c0..641d05ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -118,7 +118,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -4522,13 +4521,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", - "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", - "dev": true, - "license": "MIT" - }, "node_modules/@stablelib/base64": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", @@ -5103,7 +5095,6 @@ "integrity": "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.60.0", "@typescript-eslint/types": "8.60.0", @@ -5615,7 +5606,6 @@ "resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.38.0.tgz", "integrity": "sha512-wu+dZBptlLy0+MCUEoHmzrY/TnmgDey3+c7EbIGwrLqAvkP8yi5MWZHYGIFtAygmL4Bkz2TdFu+eU0vFPncIcg==", "license": "MIT", - "peer": true, "dependencies": { "uncrypto": "^0.1.3" } @@ -14629,7 +14619,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -15105,7 +15094,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/app/(app)/dashboard/page.tsx b/src/app/(app)/dashboard/page.tsx index 85645bf3..ef0c6573 100644 --- a/src/app/(app)/dashboard/page.tsx +++ b/src/app/(app)/dashboard/page.tsx @@ -61,9 +61,9 @@ export default async function DashboardPage() { {/* Main Columns */} -
+
{/* Left Column */} -
+
}> @@ -73,11 +73,14 @@ export default async function DashboardPage() {
- {/* Right Column */} -
+ {/* Center Content */} +
}> +
+ {/* Right Sidebar */} +
}> diff --git a/tsconfig.json b/tsconfig.json index a5be647d..0b01aeae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,13 +15,23 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, - "plugins": [{ "name": "next" }], + "plugins": [ + { + "name": "next" + } + ], "paths": { "@/*": ["./src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], "exclude": ["node_modules", "dist", ".next", "supabase/migrations/**/*.sql"] } From db747f39fc26c05a2f1aa2754d3a48a43198f687 Mon Sep 17 00:00:00 2001 From: ShaikHusnaThahera Date: Wed, 10 Jun 2026 08:31:11 +0530 Subject: [PATCH 2/5] feat: implement contributor dashboard with 3-column layout (#274) --- src/app/(app)/dashboard/page.tsx | 109 ++++++++++++---- .../contributor-dashboard/course-progress.tsx | 65 ++++++++++ .../contributor-dashboard/daily-challenge.tsx | 78 ++++++++++++ .../contributor-dashboard/heatmap-wrapper.tsx | 58 +++++++++ .../journey-progress.tsx | 58 +++++++++ .../contributor-dashboard/profile-sidebar.tsx | 120 ++++++++++++++++++ .../contributor-dashboard/recent-activity.tsx | 111 ++++++++++++++++ .../contributor-dashboard/right-sidebar.tsx | 113 +++++++++++++++++ ...accounts.sql => 0014_flagged_accounts.sql} | 0 ...012_issues_fts.sql => 0015_issues_fts.sql} | 0 ...l => 0016_maintainer_analytics_trends.sql} | 0 ...k.sql => 0017_recommendation_feedback.sql} | 0 12 files changed, 684 insertions(+), 28 deletions(-) create mode 100644 src/components/contributor-dashboard/course-progress.tsx create mode 100644 src/components/contributor-dashboard/daily-challenge.tsx create mode 100644 src/components/contributor-dashboard/heatmap-wrapper.tsx create mode 100644 src/components/contributor-dashboard/journey-progress.tsx create mode 100644 src/components/contributor-dashboard/profile-sidebar.tsx create mode 100644 src/components/contributor-dashboard/recent-activity.tsx create mode 100644 src/components/contributor-dashboard/right-sidebar.tsx rename supabase/migrations/{0012_flagged_accounts.sql => 0014_flagged_accounts.sql} (100%) rename supabase/migrations/{0012_issues_fts.sql => 0015_issues_fts.sql} (100%) rename supabase/migrations/{0012_maintainer_analytics_trends.sql => 0016_maintainer_analytics_trends.sql} (100%) rename supabase/migrations/{0012_recommendation_feedback.sql => 0017_recommendation_feedback.sql} (100%) diff --git a/src/app/(app)/dashboard/page.tsx b/src/app/(app)/dashboard/page.tsx index ef0c6573..6d769ffb 100644 --- a/src/app/(app)/dashboard/page.tsx +++ b/src/app/(app)/dashboard/page.tsx @@ -6,20 +6,39 @@ import LevelUpBanner from './level-up-banner'; import { redirect } from 'next/navigation'; import Link from 'next/link'; -// Component imports +// Existing dashboard components import StatsRow, { StatsSkeleton } from './stats-row'; import ActiveIssuesSection, { RecsSkeleton } from './active-issues'; import GitHubPRsWrapper, { PrsSkeleton } from './github-prs-wrapper'; import LeaderboardSnapshot, { LeaderboardSkeleton } from './leaderboard-snapshot'; import MenteesSection, { MenteesSkeleton } from './mentees-section'; +// New contributor-dashboard components +import { + ProfileSidebar, + ProfileSidebarSkeleton, +} from '@/components/contributor-dashboard/profile-sidebar'; +import JourneyProgress, { + JourneyProgressSkeleton, +} from '@/components/contributor-dashboard/journey-progress'; +import RecentActivity, { + RecentActivitySkeleton, +} from '@/components/contributor-dashboard/recent-activity'; +import HeatmapWrapper, { + HeatmapSkeleton, +} from '@/components/contributor-dashboard/heatmap-wrapper'; +import { DailyChallenge } from '@/components/contributor-dashboard/daily-challenge'; +import { CourseProgress } from '@/components/contributor-dashboard/course-progress'; +import { + RightSidebar, + RightSidebarSkeleton, +} from '@/components/contributor-dashboard/right-sidebar'; + export const dynamic = 'force-dynamic'; export default async function DashboardPage() { const sb = await getServerSupabase(); - if (!sb) { - return ; - } + if (!sb) return ; const { data: { user }, @@ -29,30 +48,32 @@ export default async function DashboardPage() { const service = getServiceSupabase(); if (!service) return ; - // Fetch only the profile info we need for the page shell header and subcomponents const { data: profile } = await service .from('profiles') .select('github_handle, xp, level, github_total_merges, github_streak, github_stats_synced_at') .eq('id', user.id) .maybeSingle(); + const xp = profile?.xp ?? 0; + const level = profile?.level ?? 0; + const githubHandle = profile?.github_handle ?? 'Contributor'; + return ( -
-
+
+
+ {/* Header */} -
+
-
+
01 / DASHBOARD
-

- Welcome back, {profile?.github_handle ?? 'Contributor'}. +

+ Welcome back, {githubHandle}.

-
- -
+
{/* Stats Row */} @@ -60,29 +81,61 @@ export default async function DashboardPage() { - {/* Main Columns */} -
- {/* Left Column */} -
+ {/* Three-column layout */} +
+ {/* ── Left Sidebar ── */} + }> + + + + {/* ── Center Feed ── */} +
+ {/* Journey progress */} + }> + + + + {/* Recent XP activity */} + }> + + + + {/* Active issues */} }> + {/* GitHub PRs */} + }> + + + + {/* Contribution heatmap */} + }> + + + + {/* Daily challenge */} + + + {/* Course progression */} + + + {/* Mentees */} }> -
+ - {/* Center Content */} -
- }> - + {/* ── Right Sidebar ── */} +
+ }> + -
- {/* Right Sidebar */} -
+ + {/* Leaderboard */} }> - +
@@ -109,7 +162,7 @@ export default async function DashboardPage() { function NotConfigured() { return ( -
+

Dashboard not configured

Auth isn't wired on this deployment yet.

diff --git a/src/components/contributor-dashboard/course-progress.tsx b/src/components/contributor-dashboard/course-progress.tsx new file mode 100644 index 00000000..025a5713 --- /dev/null +++ b/src/components/contributor-dashboard/course-progress.tsx @@ -0,0 +1,65 @@ +import Link from 'next/link'; +import { CheckCircle2, Circle, ArrowRight } from 'lucide-react'; + +// Static curriculum — replace with DB query when a `course_progress` table exists +const STEPS = [ + { id: 1, title: 'Fork & clone a repository', done: true }, + { id: 2, title: 'Make your first commit', done: true }, + { id: 3, title: 'Open a pull request', done: false }, + { id: 4, title: 'Respond to review feedback', done: false }, + { id: 5, title: 'Get your PR merged', done: false }, +]; + +export function CourseProgress() { + const completedCount = STEPS.filter((s) => s.done).length; + const nextStep = STEPS.find((s) => !s.done); + + return ( +
+
+

+ CONTRIBUTOR CURRICULUM +

+ + {completedCount}/{STEPS.length} + +
+ +
+ {STEPS.map((step) => ( +
+ {step.done ? ( + + ) : ( + + )} + + {step.title} + + {!step.done && step.id === nextStep?.id && ( + + NEXT + + )} +
+ ))} +
+ + {nextStep && ( + + CONTINUE COURSE + + )} +
+ ); +} diff --git a/src/components/contributor-dashboard/daily-challenge.tsx b/src/components/contributor-dashboard/daily-challenge.tsx new file mode 100644 index 00000000..eb810e24 --- /dev/null +++ b/src/components/contributor-dashboard/daily-challenge.tsx @@ -0,0 +1,78 @@ +'use client'; + +import { useEffect, useState } from 'react'; + +// Static challenge data — replace with a DB query when a `daily_challenges` table exists +const CHALLENGE = { + title: 'Comment on 2 open issues today', + description: 'Leave a helpful comment on any 2 open issues in the org.', + goal: 2, + current: 0, // TODO: wire to real progress when table exists + xpReward: 50, +}; + +function getSecondsUntilMidnightUTC(): number { + const now = new Date(); + const midnight = new Date( + Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1), + ); + return Math.floor((midnight.getTime() - now.getTime()) / 1000); +} + +function formatCountdown(secs: number): string { + const h = Math.floor(secs / 3600) + .toString() + .padStart(2, '0'); + const m = Math.floor((secs % 3600) / 60) + .toString() + .padStart(2, '0'); + const s = (secs % 60).toString().padStart(2, '0'); + return `${h}:${m}:${s}`; +} + +export function DailyChallenge() { + const [secs, setSecs] = useState(getSecondsUntilMidnightUTC()); + + useEffect(() => { + const id = setInterval(() => { + setSecs((prev) => (prev <= 1 ? getSecondsUntilMidnightUTC() : prev - 1)); + }, 1000); + return () => clearInterval(id); + }, []); + + const pct = Math.min(100, Math.round((CHALLENGE.current / CHALLENGE.goal) * 100)); + const done = CHALLENGE.current >= CHALLENGE.goal; + + return ( +
+
+

DAILY CHALLENGE

+ + {done ? 'COMPLETE ✓' : formatCountdown(secs)} + +
+ +
+
{CHALLENGE.title}
+
{CHALLENGE.description}
+ + {/* Progress bar */} +
+
+
+ +
+ + {CHALLENGE.current} / {CHALLENGE.goal} DONE + + +{CHALLENGE.xpReward} XP +
+
+
+ ); +} diff --git a/src/components/contributor-dashboard/heatmap-wrapper.tsx b/src/components/contributor-dashboard/heatmap-wrapper.tsx new file mode 100644 index 00000000..865f986b --- /dev/null +++ b/src/components/contributor-dashboard/heatmap-wrapper.tsx @@ -0,0 +1,58 @@ +import { getServiceSupabase } from '@/lib/supabase/service'; +import { ActivityHeatmap } from '@/components/activity-heatmap'; + +export default async function HeatmapWrapper({ userId }: { userId: string }) { + const service = getServiceSupabase(); + if (!service) return null; + + // Query merged PRs and XP events to build activity history + const [{ data: prs }, { data: xpEvents }] = await Promise.all([ + service + .from('pull_requests') + .select('merged_at') + .eq('author_user_id', userId) + .eq('state', 'merged') + .not('merged_at', 'is', null), + service.from('xp_events').select('created_at').eq('user_id', userId), + ]); + + // Count activity per day + const countMap = new Map(); + + for (const pr of prs ?? []) { + if (!pr.merged_at) continue; + const date = pr.merged_at.slice(0, 10); + countMap.set(date, (countMap.get(date) ?? 0) + 1); + } + for (const event of xpEvents ?? []) { + if (!event.created_at) continue; + const date = event.created_at.slice(0, 10); + countMap.set(date, (countMap.get(date) ?? 0) + 1); + } + + const activityHistory = Array.from(countMap.entries()).map(([date, count]) => ({ + date, + count, + })); + + return ; +} + +export function HeatmapSkeleton() { + return ( +
+
+
+
+
+
+
+ {[1, 2, 3, 4, 5].map((i) => ( +
+ ))} +
+
+
+
+ ); +} diff --git a/src/components/contributor-dashboard/journey-progress.tsx b/src/components/contributor-dashboard/journey-progress.tsx new file mode 100644 index 00000000..6d9af2b7 --- /dev/null +++ b/src/components/contributor-dashboard/journey-progress.tsx @@ -0,0 +1,58 @@ +import { getServiceSupabase } from '@/lib/supabase/service'; +import { xpToNextLevel, xpForLevel } from '@/lib/xp/curve'; + +function levelProgressPct(xp: number, level: number): number { + const floor = xpForLevel(level); + const ceiling = xpForLevel(level + 1); + if (ceiling <= floor) return 100; + const pct = ((xp - floor) / (ceiling - floor)) * 100; + return Math.max(0, Math.min(100, pct)); +} + +export default async function JourneyProgress({ xp, level }: { xp: number; level: number }) { + const { needed, next } = xpToNextLevel(xp); + const nextLevel = next ?? level + 1; + const pct = Math.round(levelProgressPct(xp, level)); + + return ( +
+
+

+ L{level} → L{nextLevel} JOURNEY +

+ {pct}% +
+ + {/* Progress bar */} +
+
+
+ +
+ {xp.toLocaleString()} XP + + {needed.toLocaleString()} XP TO L{nextLevel} + +
+
+ ); +} + +export function JourneyProgressSkeleton() { + return ( +
+
+
+
+
+
+
+
+
+
+
+ ); +} diff --git a/src/components/contributor-dashboard/profile-sidebar.tsx b/src/components/contributor-dashboard/profile-sidebar.tsx new file mode 100644 index 00000000..7755eb11 --- /dev/null +++ b/src/components/contributor-dashboard/profile-sidebar.tsx @@ -0,0 +1,120 @@ +'use client'; + +import Link from 'next/link'; +import { Github, ExternalLink } from 'lucide-react'; + +type Props = { + githubHandle: string; + xp: number; + level: number; + trustScore?: number; +}; + +const LEVEL_TITLES: Record = { + 0: 'NEWCOMER', + 1: 'CONTRIBUTOR', + 2: 'CONTRIBUTOR', + 3: 'SENIOR CONTRIBUTOR', + 4: 'MAINTAINER', + 5: 'CORE MAINTAINER', +}; + +function getLevelTitle(level: number): string { + return LEVEL_TITLES[level] ?? 'CONTRIBUTOR'; +} + +export function ProfileSidebar({ githubHandle, xp, level, trustScore = 0 }: Props) { + const initials = githubHandle.slice(0, 2).toUpperCase(); + const title = getLevelTitle(level); + + return ( + + ); +} + +export function ProfileSidebarSkeleton() { + return ( + + ); +} diff --git a/src/components/contributor-dashboard/recent-activity.tsx b/src/components/contributor-dashboard/recent-activity.tsx new file mode 100644 index 00000000..26b35296 --- /dev/null +++ b/src/components/contributor-dashboard/recent-activity.tsx @@ -0,0 +1,111 @@ +import { getServiceSupabase } from '@/lib/supabase/service'; +import { TrendingUp } from 'lucide-react'; + +type XpEvent = { + id: string; + xp_delta: number; + source: string; + created_at: string; + metadata: Record | null; +}; + +function sourceLabel(source: string, metadata: Record | null): string { + switch (source) { + case 'pr_merged': + return metadata?.repo ? `PR merged in ${metadata.repo}` : 'Pull request merged'; + case 'issue_completed': + return 'Issue completed'; + case 'review': + return 'Code review submitted'; + case 'help_review': + return 'Helped a contributor'; + case 'course_completion': + return 'Course step completed'; + default: + return source.replace(/_/g, ' '); + } +} + +function timeAgo(iso: string): string { + const diffMs = Date.now() - new Date(iso).getTime(); + const mins = Math.floor(diffMs / 60_000); + if (mins < 1) return 'just now'; + if (mins < 60) return `${mins}m ago`; + const hrs = Math.floor(mins / 60); + if (hrs < 24) return `${hrs}h ago`; + return `${Math.floor(hrs / 24)}d ago`; +} + +export default async function RecentActivity({ userId }: { userId: string }) { + const service = getServiceSupabase(); + if (!service) return null; + + const { data: events } = await service + .from('xp_events') + .select('id, xp_delta, source, created_at, metadata') + .eq('user_id', userId) + .order('created_at', { ascending: false }) + .limit(6); + + const items = (events ?? []) as XpEvent[]; + + return ( +
+
+

RECENT ACTIVITY

+ +
+ + {items.length === 0 ? ( +
+ No activity yet — claim an issue to get started. +
+ ) : ( +
+ {items.map((event) => ( +
+
+
+ {sourceLabel(event.source, event.metadata)} +
+
+ {timeAgo(event.created_at)} +
+
+ = 0 ? 'text-[#10b981]' : 'text-red-400'}`} + > + {event.xp_delta >= 0 ? '+' : ''} + {event.xp_delta} XP + +
+ ))} +
+ )} +
+ ); +} + +export function RecentActivitySkeleton() { + return ( +
+
+
+
+
+ {[1, 2, 3, 4].map((i) => ( +
+
+
+
+
+
+
+ ))} +
+
+ ); +} diff --git a/src/components/contributor-dashboard/right-sidebar.tsx b/src/components/contributor-dashboard/right-sidebar.tsx new file mode 100644 index 00000000..057f3b58 --- /dev/null +++ b/src/components/contributor-dashboard/right-sidebar.tsx @@ -0,0 +1,113 @@ +import Link from 'next/link'; +import { ArrowRight, Calendar, Megaphone } from 'lucide-react'; + +// Static data — replace with DB queries when `mentor_sessions` and `announcements` tables exist +const NEXT_SESSION = { + mentor: 'priya.codes', + date: 'TBD', + time: '—', + note: 'No session scheduled yet.', +}; + +const ANNOUNCEMENTS = [ + { + id: 1, + title: "GSSoC '26 has started!", + body: 'Welcome to the program. Check your assigned issues and start contributing.', + date: 'Jun 2026', + }, + { + id: 2, + title: 'New repos added to MergeShip', + body: 'Three new repositories are now available for contributions.', + date: 'Jun 2026', + }, +]; + +export function RightSidebar() { + return ( + + ); +} + +export function RightSidebarSkeleton() { + return ( + + ); +} diff --git a/supabase/migrations/0012_flagged_accounts.sql b/supabase/migrations/0014_flagged_accounts.sql similarity index 100% rename from supabase/migrations/0012_flagged_accounts.sql rename to supabase/migrations/0014_flagged_accounts.sql diff --git a/supabase/migrations/0012_issues_fts.sql b/supabase/migrations/0015_issues_fts.sql similarity index 100% rename from supabase/migrations/0012_issues_fts.sql rename to supabase/migrations/0015_issues_fts.sql diff --git a/supabase/migrations/0012_maintainer_analytics_trends.sql b/supabase/migrations/0016_maintainer_analytics_trends.sql similarity index 100% rename from supabase/migrations/0012_maintainer_analytics_trends.sql rename to supabase/migrations/0016_maintainer_analytics_trends.sql diff --git a/supabase/migrations/0012_recommendation_feedback.sql b/supabase/migrations/0017_recommendation_feedback.sql similarity index 100% rename from supabase/migrations/0012_recommendation_feedback.sql rename to supabase/migrations/0017_recommendation_feedback.sql From 0961628e902b298069a090b3e9c1765e24e00bc5 Mon Sep 17 00:00:00 2001 From: ShaikHusnaThahera Date: Thu, 11 Jun 2026 09:15:40 +0530 Subject: [PATCH 3/5] fix: color theme and all-time contributions heatmap --- src/app/(app)/dashboard/page.tsx | 2 +- .../contributor-dashboard/heatmap-wrapper.tsx | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/(app)/dashboard/page.tsx b/src/app/(app)/dashboard/page.tsx index 6d769ffb..3b25ce1b 100644 --- a/src/app/(app)/dashboard/page.tsx +++ b/src/app/(app)/dashboard/page.tsx @@ -162,7 +162,7 @@ export default async function DashboardPage() { function NotConfigured() { return ( -
+

Dashboard not configured

Auth isn't wired on this deployment yet.

diff --git a/src/components/contributor-dashboard/heatmap-wrapper.tsx b/src/components/contributor-dashboard/heatmap-wrapper.tsx index 865f986b..c95abe6e 100644 --- a/src/components/contributor-dashboard/heatmap-wrapper.tsx +++ b/src/components/contributor-dashboard/heatmap-wrapper.tsx @@ -35,7 +35,15 @@ export default async function HeatmapWrapper({ userId }: { userId: string }) { count, })); - return ; + const totalAllTime = activityHistory.reduce((sum, d) => sum + d.count, 0); + return ( +
+
+ ALL-TIME CONTRIBUTIONS: {totalAllTime.toLocaleString()} +
+ +
+ ); } export function HeatmapSkeleton() { From 10bc918099e4a0f0826732308d6d69a454eed6f7 Mon Sep 17 00:00:00 2001 From: ShaikHusnaThahera Date: Thu, 11 Jun 2026 22:24:21 +0530 Subject: [PATCH 4/5] fix:update colors, fix heatmap double-counting, show all-time contributions --- src/app/(app)/dashboard/page.tsx | 2 +- .../contributor-dashboard/course-progress.tsx | 8 ++--- .../contributor-dashboard/daily-challenge.tsx | 8 ++--- .../contributor-dashboard/heatmap-wrapper.tsx | 32 ++++++------------- .../journey-progress.tsx | 8 ++--- .../contributor-dashboard/profile-sidebar.tsx | 16 +++++----- .../contributor-dashboard/right-sidebar.tsx | 12 +++---- 7 files changed, 36 insertions(+), 50 deletions(-) diff --git a/src/app/(app)/dashboard/page.tsx b/src/app/(app)/dashboard/page.tsx index 3b25ce1b..65aed1e0 100644 --- a/src/app/(app)/dashboard/page.tsx +++ b/src/app/(app)/dashboard/page.tsx @@ -162,7 +162,7 @@ export default async function DashboardPage() { function NotConfigured() { return ( -
+

Dashboard not configured

Auth isn't wired on this deployment yet.

diff --git a/src/components/contributor-dashboard/course-progress.tsx b/src/components/contributor-dashboard/course-progress.tsx index 025a5713..b18c7616 100644 --- a/src/components/contributor-dashboard/course-progress.tsx +++ b/src/components/contributor-dashboard/course-progress.tsx @@ -16,7 +16,7 @@ export function CourseProgress() { return (
-
+

CONTRIBUTOR CURRICULUM

@@ -29,12 +29,12 @@ export function CourseProgress() { {STEPS.map((step) => (
{step.done ? ( - + ) : ( )} @@ -55,7 +55,7 @@ export function CourseProgress() { {nextStep && ( CONTINUE COURSE diff --git a/src/components/contributor-dashboard/daily-challenge.tsx b/src/components/contributor-dashboard/daily-challenge.tsx index eb810e24..288749c5 100644 --- a/src/components/contributor-dashboard/daily-challenge.tsx +++ b/src/components/contributor-dashboard/daily-challenge.tsx @@ -45,7 +45,7 @@ export function DailyChallenge() { return (
-
+

DAILY CHALLENGE

-
+
{CHALLENGE.title}
{CHALLENGE.description}
{/* Progress bar */} -
+
diff --git a/src/components/contributor-dashboard/heatmap-wrapper.tsx b/src/components/contributor-dashboard/heatmap-wrapper.tsx index c95abe6e..05b6e848 100644 --- a/src/components/contributor-dashboard/heatmap-wrapper.tsx +++ b/src/components/contributor-dashboard/heatmap-wrapper.tsx @@ -5,25 +5,15 @@ export default async function HeatmapWrapper({ userId }: { userId: string }) { const service = getServiceSupabase(); if (!service) return null; - // Query merged PRs and XP events to build activity history - const [{ data: prs }, { data: xpEvents }] = await Promise.all([ - service - .from('pull_requests') - .select('merged_at') - .eq('author_user_id', userId) - .eq('state', 'merged') - .not('merged_at', 'is', null), - service.from('xp_events').select('created_at').eq('user_id', userId), - ]); + // Only query xp_events for git-related activity (no double-counting) + const { data: xpEvents } = await service + .from('xp_events') + .select('created_at') + .eq('user_id', userId) + .in('source', ['recommended_merge', 'unrecommended_merge', 'help_review']); - // Count activity per day + // Count activity per day — all time, no date filter const countMap = new Map(); - - for (const pr of prs ?? []) { - if (!pr.merged_at) continue; - const date = pr.merged_at.slice(0, 10); - countMap.set(date, (countMap.get(date) ?? 0) + 1); - } for (const event of xpEvents ?? []) { if (!event.created_at) continue; const date = event.created_at.slice(0, 10); @@ -36,6 +26,7 @@ export default async function HeatmapWrapper({ userId }: { userId: string }) { })); const totalAllTime = activityHistory.reduce((sum, d) => sum + d.count, 0); + return (
@@ -48,17 +39,12 @@ export default async function HeatmapWrapper({ userId }: { userId: string }) { export function HeatmapSkeleton() { return ( -
+
-
- {[1, 2, 3, 4, 5].map((i) => ( -
- ))} -
diff --git a/src/components/contributor-dashboard/journey-progress.tsx b/src/components/contributor-dashboard/journey-progress.tsx index 6d9af2b7..c7a64dca 100644 --- a/src/components/contributor-dashboard/journey-progress.tsx +++ b/src/components/contributor-dashboard/journey-progress.tsx @@ -15,18 +15,18 @@ export default async function JourneyProgress({ xp, level }: { xp: number; level const pct = Math.round(levelProgressPct(xp, level)); return ( -
+

L{level} → L{nextLevel} JOURNEY

- {pct}% + {pct}%
{/* Progress bar */}
@@ -43,7 +43,7 @@ export default async function JourneyProgress({ xp, level }: { xp: number; level export function JourneyProgressSkeleton() { return ( -
+
diff --git a/src/components/contributor-dashboard/profile-sidebar.tsx b/src/components/contributor-dashboard/profile-sidebar.tsx index 7755eb11..443b40f2 100644 --- a/src/components/contributor-dashboard/profile-sidebar.tsx +++ b/src/components/contributor-dashboard/profile-sidebar.tsx @@ -30,9 +30,9 @@ export function ProfileSidebar({ githubHandle, xp, level, trustScore = 0 }: Prop return (