From 3c1e5c9c484e140e0d8801ff0f8cf41ce79ab87e Mon Sep 17 00:00:00 2001 From: Aditya948351 Date: Mon, 18 May 2026 14:19:39 +0530 Subject: [PATCH] feat: implement dynamic contributors podium (#73) --- src/app/contributors/page.tsx | 120 ++++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 19 deletions(-) diff --git a/src/app/contributors/page.tsx b/src/app/contributors/page.tsx index d1ccddfe..2176481c 100644 --- a/src/app/contributors/page.tsx +++ b/src/app/contributors/page.tsx @@ -1,18 +1,83 @@ "use client"; -import { Github, Code, FileText, MessageSquare, ExternalLink } from 'lucide-react'; +import { useState, useEffect } from 'react'; +import { Github, Code, FileText, MessageSquare, ExternalLink, Loader2 } from 'lucide-react'; import Button from '@/components/ui/Button'; import styles from './Contributors.module.css'; -const contributors = [ - { name: "Aditya Patil", handle: "@Aditya948351", contributions: 500, types: ["code", "design", "community"], avatar: "AP" }, +// Type definitions +interface Contributor { + name: string; + handle: string; + contributions: number; + types: string[]; + avatar: string | null; +} + +interface TopContributor { + name: string; + handle: string; + contributions: number; + rank: number; + avatar: string | null; +} + +// Fallback lists if API fails or rate-limit is hit +const FALLBACK_TOP: TopContributor[] = [ + { name: "Aditya Patil", handle: "@Aditya948351", contributions: 500, rank: 1, avatar: null }, + { name: "schrodingerspet", handle: "@schrodingerspet", contributions: 300, rank: 2, avatar: null }, + { name: "Niteshagarwal01", handle: "@Niteshagarwal01", contributions: 150, rank: 3, avatar: null }, ]; -const topContributors = [ - { name: "Aditya Patil", handle: "@Aditya948351", contributions: 500, rank: 1, avatar: "AP" }, +const contributors: Contributor[] = [ + { name: "Aditya Patil", handle: "@Aditya948351", contributions: 500, types: ["code", "design", "community"], avatar: "AP" }, ]; export default function ContributorsPage() { + const [topContributors, setTopContributors] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + async function fetchTopContributors() { + try { + const res = await fetch('https://api.github.com/repos/devpathindcommunity-india/DevPath-Web/contributors'); + if (!res.ok) { + throw new Error(`Failed to fetch: ${res.statusText}`); + } + const data = await res.json(); + + // Sort by contributions descending + const sorted = [...data].sort((a: any, b: any) => b.contributions - a.contributions); + + // Extract top 3 + const mappedTop = sorted.slice(0, 3).map((c: any, index: number) => ({ + name: c.login, + handle: `@${c.login}`, + contributions: c.contributions, + rank: index + 1, + avatar: c.avatar_url, + })); + + setTopContributors(mappedTop); + } catch (err: any) { + console.error("Error fetching contributors for podium:", err); + setError(err.message || "Failed to load podium data"); + setTopContributors(FALLBACK_TOP); + } finally { + setLoading(false); + } + } + + fetchTopContributors(); + }, []); + + // Standard physical podium arrangement: [2nd, 1st, 3rd] + const arrangePodium = (list: TopContributor[]) => { + if (list.length < 3) return list; + return [list[1], list[0], list[2]]; + }; + return (
@@ -36,22 +101,39 @@ export default function ContributorsPage() {
-
- {topContributors.map((contributor) => ( -
-
- {contributor.rank === 1 &&
👑
} - {contributor.rank === 2 &&
🥈
} - {contributor.rank === 3 &&
🥉
} -
- {contributor.avatar} + {loading ? ( +
+ +

Loading contributors podium...

+
+ ) : ( +
+ {arrangePodium(topContributors).map((contributor) => ( +
+
+ {contributor.rank === 1 &&
👑
} + {contributor.rank === 2 &&
🥈
} + {contributor.rank === 3 &&
🥉
} +
+ {contributor.avatar ? ( + {contributor.name} + ) : ( +
+ {contributor.name.slice(0, 2).toUpperCase()} +
+ )} +
+
{contributor.name}
+
{contributor.contributions} commits
-
{contributor.name}
-
{contributor.contributions} commits
-
- ))} -
+ ))} +
+ )}
{contributors.map((contributor, index) => (