diff --git a/src/pages/dashboard/dashboard.css b/src/pages/dashboard/dashboard.css index c0db1112..7c64bb39 100644 --- a/src/pages/dashboard/dashboard.css +++ b/src/pages/dashboard/dashboard.css @@ -584,6 +584,426 @@ text-decoration: none; } +/* Leaderboard Page Styles */ +.leaderboard-page-container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem 0; +} + +.leaderboard-page-header { + text-align: center; + margin-bottom: 3rem; +} + +.leaderboard-page-title { + font-size: 3rem; + font-weight: 700; + margin-bottom: 1rem; + color: var(--ifm-color-emphasis-900); +} + +.leaderboard-page-title .highlight { + background: linear-gradient(135deg, #ffd700, #ff8c00); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.leaderboard-page-subtitle { + font-size: 1.2rem; + color: var(--ifm-color-emphasis-700); + margin-bottom: 2rem; +} + +.refresh-section { + margin-top: 2rem; +} + +.refresh-button { + background: var(--ifm-color-primary); + color: white; + border: none; + padding: 0.75rem 2rem; + border-radius: 50px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + font-size: 1rem; +} + +.refresh-button:hover:not(:disabled) { + background: var(--ifm-color-primary-darker); + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); +} + +.refresh-button:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* Loading and Error States */ +.loading-container { + text-align: center; + padding: 4rem 2rem; + background: var(--ifm-card-background-color); + border-radius: 1rem; + margin: 2rem 0; + border: 1px solid var(--ifm-color-border); +} + +.loading-spinner-large { + font-size: 3rem; + margin-bottom: 1rem; + animation: spin 2s linear infinite; +} + +.error-container { + text-align: center; + padding: 3rem 2rem; + background: var(--ifm-alert-background-color); + border: 1px solid var(--ifm-alert-border-color); + border-radius: 1rem; + margin: 2rem 0; +} + +.error-container h3 { + color: var(--ifm-color-danger); + margin-bottom: 1rem; +} + +.error-container p { + color: var(--ifm-font-color-base); + margin-bottom: 2rem; +} + +.error-help { + color: var(--ifm-font-color-base); +} + +.retry-button { + background: var(--ifm-color-danger); + color: white; + border: none; + padding: 0.75rem 2rem; + border-radius: 0.5rem; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + margin-top: 1rem; +} + +.retry-button:hover { + background: var(--ifm-color-danger-dark); + transform: translateY(-2px); +} + +/* Leaderboard Stats */ +.leaderboard-stats { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 2rem; + margin-bottom: 3rem; + padding: 2rem; + background: var(--ifm-card-background-color); + border-radius: 1rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); +} + +.leaderboard-stats .stat-item { + text-align: center; + padding: 1rem; +} + +.leaderboard-stats .stat-number { + display: block; + font-size: 2.5rem; + font-weight: 700; + color: var(--ifm-color-primary); + margin-bottom: 0.5rem; +} + +.leaderboard-stats .stat-label { + font-size: 0.9rem; + color: var(--ifm-color-emphasis-600); + font-weight: 500; +} + +/* Leaderboard Grid */ +.leaderboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 2rem; +} + +.leaderboard-item { + background: var(--ifm-card-background-color); + border-radius: 1.5rem; + padding: 2rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + border: 1px solid var(--ifm-color-border); + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.leaderboard-item::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: var(--ifm-color-primary); +} + +.leaderboard-item.rank-1::before { + background: linear-gradient(135deg, #ffd700, #ffb347); +} + +.leaderboard-item.rank-2::before { + background: linear-gradient(135deg, #c0c0c0, #a8a8a8); +} + +.leaderboard-item.rank-3::before { + background: linear-gradient(135deg, #cd7f32, #b8860b); +} + +/* Rank Section */ +.rank-section { + text-align: center; + margin-bottom: 1.5rem; +} + +.rank-badge { + display: inline-block; + font-size: 1.2rem; + font-weight: 700; + padding: 0.75rem 1.5rem; + border-radius: 50px; + color: white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + min-width: 80px; +} + +.rank-badge.rank-1 { + background: linear-gradient(135deg, #ffd700, #ffb347); + box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3); +} + +.rank-badge.rank-2 { + background: linear-gradient(135deg, #c0c0c0, #a8a8a8); + box-shadow: 0 4px 15px rgba(192, 192, 192, 0.3); +} + +.rank-badge.rank-3 { + background: linear-gradient(135deg, #cd7f32, #b8860b); + box-shadow: 0 4px 15px rgba(205, 127, 50, 0.3); +} + +.rank-badge.rank-other { + background: linear-gradient(135deg, var(--ifm-color-primary), var(--ifm-color-primary-darker)); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); +} + +/* Avatar Section */ +.avatar-section { + text-align: center; + margin-bottom: 1.5rem; +} + +.user-avatar { + width: 80px; + height: 80px; + border-radius: 50%; + border: 4px solid var(--ifm-color-primary-lightest); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + object-fit: cover; +} + +/* User Info */ +.user-info { + text-align: center; + margin-bottom: 1.5rem; +} + +.user-name { + font-size: 1.4rem; + font-weight: 600; + color: var(--ifm-color-emphasis-900); + margin-bottom: 0.5rem; +} + +.user-username { + font-size: 0.9rem; + color: var(--ifm-color-emphasis-600); + margin-bottom: 1rem; + font-style: italic; +} + +/* Score Display */ +.score-display { + margin-bottom: 1.5rem; +} + +.score-number { + font-size: 2.5rem; + font-weight: 700; + color: var(--ifm-color-primary); + display: block; +} + +.score-label { + font-size: 0.9rem; + color: var(--ifm-color-emphasis-600); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* User Stats */ +.user-stats { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-bottom: 1.5rem; +} + +.user-stats .stat { + text-align: center; + padding: 0.75rem; + background: var(--ifm-color-background); + border-radius: 0.5rem; + border: 1px solid var(--ifm-color-border); +} + +.stat-value { + display: block; + font-size: 1.5rem; + font-weight: 600; + color: var(--ifm-color-primary); + margin-bottom: 0.25rem; +} + +.stat-text { + font-size: 0.8rem; + color: var(--ifm-color-emphasis-600); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Achievements */ +.achievements { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + justify-content: center; + margin-bottom: 1.5rem; +} + +.achievement-tag { + font-size: 0.75rem; + padding: 0.25rem 0.75rem; + background: var(--ifm-color-primary-lightest); + color: var(--ifm-color-primary-darker); + border-radius: 50px; + font-weight: 500; + border: 1px solid var(--ifm-color-primary-light); + white-space: nowrap; +} + +/* Actions Section */ +.actions-section { + text-align: center; +} + +.github-link { + display: inline-flex; + align-items: center; + gap: 0.5rem; + background: var(--ifm-color-primary); + color: white; + text-decoration: none; + padding: 0.75rem 1.5rem; + border-radius: 50px; + font-weight: 600; + transition: all 0.3s ease; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); +} + +.github-link:hover { + background: var(--ifm-color-primary-darker); + transform: translateY(-1px); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); + text-decoration: none; + color: white; +} + +/* Empty State */ +.empty-state { + text-align: center; + padding: 4rem 2rem; + background: var(--ifm-card-background-color); + border-radius: 1rem; + border: 1px solid var(--ifm-color-border); + color: var(--ifm-color-emphasis-700); +} + +.empty-state h3 { + margin-bottom: 1rem; + color: var(--ifm-color-emphasis-800); +} + +.empty-state p { + font-size: 1.1rem; + margin-bottom: 0; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .leaderboard-page-title { + font-size: 2rem; + } + + .leaderboard-grid { + grid-template-columns: 1fr; + gap: 1.5rem; + } + + .leaderboard-item { + padding: 1.5rem; + } + + .leaderboard-stats { + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + padding: 1.5rem; + } + + .user-avatar { + width: 60px; + height: 60px; + } + + .score-number { + font-size: 2rem; + } +} + +@media (max-width: 480px) { + .leaderboard-page-container { + padding: 1rem 0; + } + + .leaderboard-stats { + grid-template-columns: 1fr; + } + + .user-stats { + grid-template-columns: 1fr; + } +} + /* Error Message */ .error-message { text-align: center; @@ -677,4 +1097,370 @@ .dashboard-cta { padding: 2rem 1rem; } +} + +/* GSSoC-Style Enhancements */ +.leaderboard-page-container { + background: var(--ifm-background-color); + min-height: calc(100vh - 60px); +} + +.leaderboard-page-title { + background: linear-gradient(135deg, var(--ifm-color-primary) 0%, var(--ifm-color-primary-darker) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +/* Enhanced Leaderboard Item for GSSoC Style */ +.leaderboard-item { + background: var(--ifm-card-background-color); + border: 1px solid var(--ifm-color-border); + box-shadow: + 0 8px 32px rgba(0, 0, 0, 0.1), + 0 1px 1px rgba(0, 0, 0, 0.15); + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); +} + +[data-theme='dark'] .leaderboard-item { + box-shadow: + 0 8px 32px rgba(255, 255, 255, 0.05), + 0 1px 1px rgba(255, 255, 255, 0.1); +} + +.leaderboard-item:hover { + transform: translateY(-8px); + box-shadow: + 0 20px 40px rgba(0, 0, 0, 0.15), + 0 2px 4px rgba(0, 0, 0, 0.1); +} + +[data-theme='dark'] .leaderboard-item:hover { + box-shadow: + 0 20px 40px rgba(255, 255, 255, 0.1), + 0 2px 4px rgba(255, 255, 255, 0.05); +} + +/* Gradient rank badges like GSSoC */ +.rank-badge.rank-1 { + background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 50%, #fecfef 100%); + animation: pulse-gold 2s ease-in-out infinite alternate; + color: #fff; +} + +.rank-badge.rank-2 { + background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); + animation: pulse-silver 2s ease-in-out infinite alternate; + color: #333; +} + +.rank-badge.rank-3 { + background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%); + animation: pulse-bronze 2s ease-in-out infinite alternate; + color: #fff; +} + +@keyframes pulse-gold { + 0% { box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3); } + 100% { box-shadow: 0 6px 25px rgba(255, 215, 0, 0.5); } +} + +@keyframes pulse-silver { + 0% { box-shadow: 0 4px 15px rgba(192, 192, 192, 0.3); } + 100% { box-shadow: 0 6px 25px rgba(192, 192, 192, 0.5); } +} + +@keyframes pulse-bronze { + 0% { box-shadow: 0 4px 15px rgba(205, 127, 50, 0.3); } + 100% { box-shadow: 0 6px 25px rgba(205, 127, 50, 0.5); } +} + +/* Special badges for GSSoC features */ +.achievement-tag { + font-size: 0.7rem; + padding: 0.3rem 0.8rem; + border-radius: 20px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + transition: all 0.3s ease; +} + +/* Postman badge styling */ +.achievement-tag:has-text("📮") { + background: linear-gradient(135deg, #ff6b35 0%, #f7931e 100%); + color: white; + border: none; +} + +/* Web3 badge styling */ +.achievement-tag:has-text("🌐") { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border: none; +} + +/* Enhanced score display */ +.score-display { + position: relative; + background: linear-gradient(135deg, var(--ifm-color-primary) 0%, var(--ifm-color-primary-darker) 100%); + color: white; + padding: 1.5rem; + border-radius: 1rem; + margin-bottom: 1.5rem; + text-align: center; +} + +.score-number { + font-size: 3rem; + font-weight: 800; + color: white; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3)); +} + +.score-label { + color: rgba(255, 255, 255, 0.9); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; +} + +/* Enhanced user avatar with ranking glow */ +.user-avatar { + transition: all 0.3s ease; + filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.15)); +} + +[data-theme='dark'] .user-avatar { + filter: drop-shadow(0 4px 12px rgba(255, 255, 255, 0.1)); +} + +.leaderboard-item:nth-child(1) .user-avatar { + border-color: #ffd700; + box-shadow: 0 0 20px rgba(255, 215, 0, 0.4); +} + +.leaderboard-item:nth-child(2) .user-avatar { + border-color: #c0c0c0; + box-shadow: 0 0 20px rgba(192, 192, 192, 0.4); +} + +.leaderboard-item:nth-child(3) .user-avatar { + border-color: #cd7f32; + box-shadow: 0 0 20px rgba(205, 127, 50, 0.4); +} + +/* Enhanced stats display */ +.user-stats .stat { + background: var(--ifm-color-background); + border: 1px solid var(--ifm-color-border); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06); + transition: all 0.3s ease; +} + +[data-theme='dark'] .user-stats .stat { + box-shadow: inset 0 2px 4px rgba(255, 255, 255, 0.03); +} + +.user-stats .stat:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} + +[data-theme='dark'] .user-stats .stat:hover { + box-shadow: 0 4px 12px rgba(255, 255, 255, 0.05); +} + +/* Enhanced GitHub link */ +.github-link { + background: var(--ifm-color-emphasis-800); + color: var(--ifm-font-color-base-inverse); + padding: 0.75rem 1.5rem; + border-radius: 50px; + text-decoration: none; + transition: all 0.3s ease; + font-weight: 600; + gap: 0.5rem; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); +} + +[data-theme='dark'] .github-link { + background: var(--ifm-color-emphasis-200); + color: var(--ifm-color-emphasis-900); + box-shadow: 0 4px 15px rgba(255, 255, 255, 0.1); +} + +.github-link:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3); + color: var(--ifm-font-color-base-inverse); + text-decoration: none; +} + +[data-theme='dark'] .github-link:hover { + box-shadow: 0 8px 25px rgba(255, 255, 255, 0.2); + color: var(--ifm-color-emphasis-900); +} + +/* Top 3 special styling */ +.leaderboard-item:nth-child(1) { + background: var(--ifm-card-background-color); + border: 2px solid #ffd700; + position: relative; + overflow: hidden; +} + +.leaderboard-item:nth-child(1)::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, transparent 50%); + pointer-events: none; +} + +.leaderboard-item:nth-child(2) { + background: var(--ifm-card-background-color); + border: 2px solid #c0c0c0; + position: relative; + overflow: hidden; +} + +.leaderboard-item:nth-child(2)::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(192, 192, 192, 0.1) 0%, transparent 50%); + pointer-events: none; +} + +.leaderboard-item:nth-child(3) { + background: var(--ifm-card-background-color); + border: 2px solid #cd7f32; + position: relative; + overflow: hidden; +} + +.leaderboard-item:nth-child(3)::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(205, 127, 50, 0.1) 0%, transparent 50%); + pointer-events: none; +} + +/* Crown icons for top 3 */ +.leaderboard-item:nth-child(1)::after { + content: '👑'; + position: absolute; + top: -10px; + right: 20px; + font-size: 2rem; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2)); +} + +.leaderboard-item:nth-child(2)::after { + content: '🥈'; + position: absolute; + top: -10px; + right: 20px; + font-size: 2rem; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2)); +} + +.leaderboard-item:nth-child(3)::after { + content: '🥉'; + position: absolute; + top: -10px; + right: 20px; + font-size: 2rem; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2)); +} + +/* Leaderboard stats enhancement */ +.leaderboard-stats { + background: linear-gradient(135deg, var(--ifm-color-primary) 0%, var(--ifm-color-primary-darker) 100%); + color: white; + margin-bottom: 3rem; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); +} + +[data-theme='dark'] .leaderboard-stats { + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +} + +.leaderboard-stats .stat-number { + color: white; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); +} + +.leaderboard-stats .stat-label { + color: rgba(255, 255, 255, 0.9); + font-weight: 600; +} + +/* Loading animation enhancement */ +.loading-spinner-large { + background: linear-gradient(135deg, var(--ifm-color-primary), var(--ifm-color-primary-darker)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Empty state enhancement */ +.empty-state { + text-align: center; + padding: 4rem 2rem; + background: var(--ifm-card-background-color); + border-radius: 2rem; + margin: 2rem 0; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + border: 1px solid var(--ifm-color-border); +} + +[data-theme='dark'] .empty-state { + box-shadow: 0 8px 32px rgba(255, 255, 255, 0.05); +} + +/* Streak display */ +.streak-display { + position: absolute; + top: 1rem; + left: 1rem; + background: linear-gradient(135deg, #ff8a80, #ff5722); + color: white; + padding: 0.5rem 1rem; + border-radius: 50px; + font-size: 0.8rem; + font-weight: 600; + box-shadow: 0 4px 12px rgba(255, 87, 34, 0.3); + z-index: 10; +} + +.streak-display::before { + content: '🔥 '; +} + +/* Achievement tag enhancements for theme compatibility */ +.achievement-tag { + background: var(--ifm-color-primary-lightest); + color: var(--ifm-color-primary-darker); + border: 1px solid var(--ifm-color-primary-light); + transition: all 0.3s ease; +} + +[data-theme='dark'] .achievement-tag { + background: var(--ifm-color-primary-darkest); + color: var(--ifm-color-primary-lighter); + border: 1px solid var(--ifm-color-primary-dark); } \ No newline at end of file diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index 822050ad..6ce7d7ba 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -11,11 +11,16 @@ import "./dashboard.css"; interface LeaderboardEntry { rank: number; name: string; + username?: string; avatar: string; contributions: number; repositories: number; achievements: string[]; github_url: string; + score?: number; + streak?: number; + postManTag?: boolean; + web3hack?: boolean; } interface DashboardStats { @@ -26,26 +31,203 @@ interface DashboardStats { topContributors: LeaderboardEntry[]; } +// Helper function to parse CSV data from Google Sheets +const parseCSVToJSON = (csvText: string): any[] => { + const lines = csvText.trim().split('\n'); + if (lines.length < 2) return []; + + // Get headers from first line (remove quotes) + const headers = lines[0].split(',').map(header => header.replace(/"/g, '').trim()); + console.log('📋 CSV Headers found:', headers); + + // Parse data rows + const data: any[] = []; + + for (let i = 1; i < lines.length; i++) { + const values = lines[i].split(',').map(value => value.replace(/"/g, '').trim()); + const row: any = {}; + + headers.forEach((header, index) => { + if (values[index]) { + row[header] = values[index]; + } + }); + + // Only add rows that have meaningful data + if (row[headers[0]] && row[headers[0]] !== '') { + data.push(row); + } + } + + console.log('📊 Parsed CSV data:', data); + return data; +}; + const DashboardContent: React.FC = () => { const location = useLocation(); const history = useHistory(); - const [activeTab, setActiveTab] = useState<'home' | 'discuss'>('home'); + const [activeTab, setActiveTab] = useState<'home' | 'discuss' | 'leaderboard'>('home'); const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); + const [leaderboardData, setLeaderboardData] = useState([]); + const [isLoadingLeaderboard, setIsLoadingLeaderboard] = useState(false); + const [leaderboardError, setLeaderboardError] = useState(null); useEffect(() => { // Set active tab based on URL hash if (location.hash === '#discuss') { setActiveTab('discuss'); + } else if (location.hash === '#leaderboard') { + setActiveTab('leaderboard'); } else { setActiveTab('home'); } }, [location]); - const handleTabChange = (tab: 'home' | 'discuss') => { + // Fetch leaderboard data when leaderboard tab is active + useEffect(() => { + if (activeTab === 'leaderboard') { + fetchLeaderboardData(); + } + }, [activeTab]); + + const fetchLeaderboardData = async () => { + setIsLoadingLeaderboard(true); + setLeaderboardError(null); + + try { + console.log('🔄 Fetching leaderboard data from API...'); + + const response = await fetch('https://gssoc24-leaderboard-backend-production-dfe3.up.railway.app/OSLeaderboard'); + + if (!response.ok) { + throw new Error(`API request failed: ${response.status}`); + } + + const data = await response.json(); + console.log('📊 API Response:', data); + + if (!data.leaderboard || !Array.isArray(data.leaderboard)) { + throw new Error('Invalid API response format'); + } + + // Transform API data to match our LeaderboardEntry interface + const transformedData: LeaderboardEntry[] = data.leaderboard + .filter(item => item.login && item.score !== undefined) // Filter out entries without login or score + .map((item, index) => { + const score = item.score || 0; + const prCount = item.pr_urls ? item.pr_urls.length : 0; + const achievements = generateAchievements(score, prCount); + + // Add badges for special tags + if (item.postManTag) achievements.push("📮 Postman Badge"); + if (item.web3hack) achievements.push("🌐 Web3 Hacker"); + + return { + rank: index + 1, + name: item.login, // Using login as name since that's what's available + username: item.login, + avatar: item.avatar_url || `https://avatars.githubusercontent.com/u/${Math.floor(Math.random() * 100000)}?v=4`, + contributions: prCount, + repositories: Math.floor(prCount / 3) || 1, // Estimate repos based on PRs + score, + achievements, + github_url: item.url || `https://github.com/${item.login}`, + streak: item.streak || 0, + postManTag: item.postManTag || false, + web3hack: item.web3hack || false, + }; + }) + .sort((a, b) => b.score - a.score) // Sort by score descending + .map((item, index) => ({ ...item, rank: index + 1 })); // Update ranks after sorting + + console.log('✅ Successfully processed leaderboard data:', transformedData); + setLeaderboardData(transformedData); + + } catch (error) { + console.error('❌ Error fetching leaderboard data:', error); + setLeaderboardError(error.message); + + // Fallback demo data with similar structure + console.log('📝 Loading demo data as fallback...'); + const demoData: LeaderboardEntry[] = [ + { + rank: 1, + name: "ShivanshPlays", + username: "ShivanshPlays", + avatar: "https://avatars.githubusercontent.com/u/112249407?v=4", + contributions: 158, + repositories: 25, + score: 7900, + achievements: ["🏆 Top Contributor", "📮 Postman Badge", "🌐 Web3 Hacker"], + github_url: "https://github.com/ShivanshPlays", + streak: 9, + postManTag: true, + web3hack: true, + }, + { + rank: 2, + name: "IkkiOcean", + username: "IkkiOcean", + avatar: "https://avatars.githubusercontent.com/u/76002919?v=4", + contributions: 145, + repositories: 22, + score: 7850, + achievements: ["🚀 Rising Star", "📮 Postman Badge", "🌐 Web3 Hacker"], + github_url: "https://github.com/IkkiOcean", + streak: 8, + postManTag: true, + web3hack: true, + }, + { + rank: 3, + name: "Community Member", + username: "member3", + avatar: "https://avatars.githubusercontent.com/u/79542825?v=4", + contributions: 120, + repositories: 18, + score: 6500, + achievements: ["💪 Power User", "⭐ Star Contributor"], + github_url: "https://github.com/member3", + streak: 5, + } + ]; + setLeaderboardData(demoData); + } finally { + setIsLoadingLeaderboard(false); + } + }; + + const generateAchievements = (score: number, contributions: number): string[] => { + const achievements: string[] = []; + + // Score-based achievements (GSSoC style) + if (score >= 5000) achievements.push("🏆 Elite Contributor"); + if (score >= 3000) achievements.push("⭐ Master Contributor"); + if (score >= 1000) achievements.push("🚀 Advanced Contributor"); + if (score >= 500) achievements.push("💪 Active Contributor"); + if (score >= 100) achievements.push("🌟 Rising Star"); + + // PR count-based achievements + if (contributions >= 100) achievements.push("� Century Club"); + if (contributions >= 50) achievements.push("🎯 Half Century"); + if (contributions >= 25) achievements.push("⚡ Quick Contributor"); + if (contributions >= 10) achievements.push("🔥 Consistent"); + + // Special milestone achievements + if (score >= 7000) achievements.push("👑 Legend"); + if (contributions >= 150) achievements.push("🎖️ PR Master"); + + return achievements.slice(0, 3); // Limit to 3 achievements for UI + }; + + const handleTabChange = (tab: 'home' | 'discuss' | 'leaderboard') => { setActiveTab(tab); if (tab === 'discuss') { history.push('#discuss'); window.scrollTo(0, 0); + } else if (tab === 'leaderboard') { + history.push('#leaderboard'); + window.scrollTo(0, 0); } else { history.push('#'); } @@ -158,7 +340,6 @@ const DashboardContent: React.FC = () => { value={valueText} autoAnimationStart={true} duration={1} - className="dashboard-slot-counter-value" /> )} @@ -252,6 +433,13 @@ const DashboardContent: React.FC = () => { 💬 Discuss +
  • handleTabChange('leaderboard')} + > + 🏆 + Leaderboard +
  • - ) : ( + ) : activeTab === 'discuss' ? (

    Community Discussions

    Join the conversation, ask questions, and share your thoughts with the RecodeHive community.

    @@ -406,6 +594,187 @@ const DashboardContent: React.FC = () => { />
    + ) : ( + /* Leaderboard Tab */ +
    + +

    + 🏆 Community Leaderboard +

    +

    + Live rankings from GSSoC '24 API • Updated automatically +

    +
    + +
    +
    + + {/* Loading State */} + {isLoadingLeaderboard && ( + +
    +

    Loading leaderboard data from GSSoC API...

    +
    + )} + + {/* Error State */} + {leaderboardError && !isLoadingLeaderboard && ( + +

    ⚠️ API Connection Issue

    +

    {leaderboardError}

    +
    +

    This could be due to:

    +
      +
    • API server is temporarily down
    • +
    • Network connectivity issues
    • +
    • API rate limiting
    • +
    +

    Please try refreshing in a moment!

    +
    + +
    + )} + + {/* Leaderboard Data */} + {!isLoadingLeaderboard && !leaderboardError && leaderboardData.length > 0 && ( + +
    +
    + {leaderboardData.length} + Participants +
    +
    + {leaderboardData[0]?.score || 0} + Top Score +
    +
    + + {Math.round(leaderboardData.reduce((acc, user) => acc + (user.score || 0), 0) / leaderboardData.length)} + + Avg Score +
    +
    + +
    + {leaderboardData.map((entry, index) => ( + + {/* Streak Display */} + {entry.streak && entry.streak > 1 && ( +
    + {entry.streak} Day Streak +
    + )} + +
    +
    + #{entry.rank} +
    +
    + +
    + {entry.name} +
    + +
    +

    {entry.name}

    + {entry.username && entry.username !== entry.name && ( +

    @{entry.username}

    + )} + +
    + {entry.score || 0} + points +
    + +
    +
    + {entry.contributions} + PRs +
    +
    + {entry.repositories} + Repos +
    +
    + + {entry.achievements.length > 0 && ( +
    + {entry.achievements.map((achievement, i) => ( + + {achievement} + + ))} +
    + )} +
    + + +
    + ))} +
    +
    + )} + + {/* Empty State */} + {!isLoadingLeaderboard && !leaderboardError && leaderboardData.length === 0 && ( + +

    📊 No data available

    +

    The leaderboard is empty. Check back later!

    +
    + )} +
    )}