+
{user.githubStats.repos || 0}
- Repositories
+ Repositories
{user.githubStats.totalStars || 0}
- Total Stars
+ Total Stars
{user.githubStats.followers || 0}
- Followers
+ Followers
{user.githubStats.following || 0}
- Following
+ Following
+
+
+
+ {(user.githubStats.linesContributed ?? GIT_FALLBACK_STATS[(user.githubStats.username || '').toLowerCase()]?.additions ?? 0).toLocaleString()}
+ Lines Contributed
+
+
+
+ {user.githubStats.contributions ?? GIT_FALLBACK_STATS[(user.githubStats.username || '').toLowerCase()]?.commits ?? 0}
+ Commits Contributed
diff --git a/src/components/profile/UserProfile.tsx b/src/components/profile/UserProfile.tsx
index 9a6845dd..81389daf 100644
--- a/src/components/profile/UserProfile.tsx
+++ b/src/components/profile/UserProfile.tsx
@@ -4,7 +4,7 @@ import { useState, useEffect } from 'react';
import { useAuth } from '@/context/AuthContext';
import { useRouter } from 'next/navigation';
import { teamMembers } from '@/data/team';
-import { Trophy, Flame, Star, Target, MapPin, Link as LinkIcon, Calendar, Phone, LogOut, Camera, Save, X, Github, Instagram, Linkedin, CheckCircle, Share2, Shield, Copy, Check, Plus, Edit3, Users, Globe, BookOpen, GitMerge } from 'lucide-react';
+import { Trophy, Flame, Star, Target, MapPin, Link as LinkIcon, Calendar, Phone, LogOut, Camera, Save, X, Github, Instagram, Linkedin, CheckCircle, Share2, Shield, Copy, Check, Plus, Edit3, Users, Globe, BookOpen, GitMerge, Code2 } from 'lucide-react';
import styles from './Profile.module.css';
import 'github-markdown-css/github-markdown.css';
import ProjectUploadModal from '@/components/projects/ProjectUploadModal';
@@ -20,7 +20,16 @@ import { collection, query, where, orderBy, getDocs, doc, updateDoc, arrayUnion,
import { db } from '@/lib/firebase';
import { calculateLevel } from '@/lib/points';
import { getEmbedUrl } from '@/lib/utils';
-
+import { GIT_FALLBACK_STATS } from '@/lib/github';
+
+/**
+ * UserProfile component renders the main dashboard profile page for authenticated developers.
+ * It manages:
+ * - Local layout state for editing avatars and bios.
+ * - Loading and sorting uploaded developer projects from Firestore.
+ * - Real-time calculations of gamification Dev Points, progress levels, and achievements.
+ * - Rendering animated progress rings and privacy toggle modals.
+ */
export default function UserProfile() {
const { user, logout, updateUserProfile } = useAuth();
const router = useRouter();
@@ -37,6 +46,10 @@ export default function UserProfile() {
const levelInfo = calculateLevel(user?.points || 0);
const targetProgress = levelInfo.progress;
+ /**
+ * Triggers animated loading effect for the custom circular progress ring
+ * once the component is mounted.
+ */
useEffect(() => {
if (user) {
const timer = setTimeout(() => {
@@ -511,26 +524,36 @@ export default function UserProfile() {
GitHub Activity
-
+
{user.githubStats.repos || 0}
- Repositories
+ Repositories
{user.githubStats.totalStars || 0}
- Total Stars
+ Total Stars
{user.githubStats.followers || 0}
- Followers
+ Followers
{user.githubStats.following || 0}
- Following
+ Following
+
+
+
+ {(user.githubStats.linesContributed ?? GIT_FALLBACK_STATS[(user.githubStats.username || '').toLowerCase()]?.additions ?? 0).toLocaleString()}
+ Lines Contributed
+
+
+
+ {user.githubStats.contributions ?? GIT_FALLBACK_STATS[(user.githubStats.username || '').toLowerCase()]?.commits ?? 0}
+ Commits Contributed
diff --git a/src/components/resources/QuizComponent.tsx b/src/components/resources/QuizComponent.tsx
index 8fef9fe5..8b7130df 100644
--- a/src/components/resources/QuizComponent.tsx
+++ b/src/components/resources/QuizComponent.tsx
@@ -2,6 +2,10 @@ import { useState } from "react";
import styles from "./QuizComponent.module.css";
import { useGamification } from "../../context/GamificationContext";
+/**
+ * Static list of quiz questions used for developer resource testing.
+ * Each question contains a prompt, list of choice options, and the correct answer.
+ */
const questions = [
{
question: "What is React?",
@@ -15,6 +19,11 @@ const questions = [
},
];
+/**
+ * QuizComponent renders an interactive quiz interface.
+ * On completion, it calculates the user's score and awards gamification XP
+ * through the global GamificationContext depending on performance.
+ */
export default function QuizComponent() {
const { addXp } = useGamification();
diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx
index a07d4c54..1e977a87 100644
--- a/src/context/AuthContext.tsx
+++ b/src/context/AuthContext.tsx
@@ -58,6 +58,9 @@ interface User {
company?: string;
location?: string;
createdAt?: string;
+ linesAdded?: number;
+ linesRemoved?: number;
+ linesContributed?: number;
};
followers?: string[]; // Array of user UIDs
following?: string[]; // Array of user UIDs
diff --git a/src/lib/github.ts b/src/lib/github.ts
index b3498c0b..e2e743e7 100644
--- a/src/lib/github.ts
+++ b/src/lib/github.ts
@@ -31,3 +31,100 @@ export const fetchUserActivity = async (username: string, token: string) => {
if (!res.ok) throw new Error('Failed to fetch user activity');
return res.json();
};
+
+// Fallback contribution stats calculated from local repository Git log analysis
+export const GIT_FALLBACK_STATS: Record
= {
+ 'aditya948351': { additions: 49917, deletions: 18400, commits: 78 },
+ 'devpathindcommunity-india': { additions: 49917, deletions: 18400, commits: 78 },
+ 'devpathind-community': { additions: 49917, deletions: 18400, commits: 78 },
+ 'schrodingerspet': { additions: 16582, deletions: 1172, commits: 16 },
+ 'bhavyaxtech': { additions: 1175, deletions: 424, commits: 2 },
+ 'niteshagarwal01': { additions: 525, deletions: 226, commits: 6 },
+ 'deepiga0706': { additions: 271, deletions: 0, commits: 1 },
+ 'nishantdakua': { additions: 8, deletions: 8, commits: 1 },
+ 'swathi-chippa': { additions: 1, deletions: 11, commits: 1 },
+ 'mzl2233': { additions: 0, deletions: 2, commits: 1 }
+};
+
+export const fetchRepoContributorStats = async (token?: string) => {
+ try {
+ const headers: Record = {
+ 'Accept': 'application/vnd.github.v3+json',
+ 'User-Agent': 'DevPath-Website'
+ };
+ if (token) {
+ headers['Authorization'] = `Bearer ${token}`;
+ }
+
+ let res = await fetch('https://api.github.com/repos/devpathindcommunity-india/DevPath-Web/stats/contributors', { headers });
+
+ // Handle 202 status code by doing a brief retry loop
+ let retries = 0;
+ while (res.status === 202 && retries < 3) {
+ await new Promise(resolve => setTimeout(resolve, 1500));
+ res = await fetch('https://api.github.com/repos/devpathindcommunity-india/DevPath-Web/stats/contributors', { headers });
+ retries++;
+ }
+
+ if (!res.ok) {
+ return null;
+ }
+
+ const data = await res.json();
+ return Array.isArray(data) ? data : null;
+ } catch (e) {
+ console.error('Error fetching repo contributor stats:', e);
+ return null;
+ }
+};
+
+export const calculateUserLinesContributed = (stats: any[] | null, username: string) => {
+ let additions = 0;
+ let deletions = 0;
+ let commits = 0;
+
+ const lowerUsername = username.toLowerCase();
+ const isAdityaOrCommunity = lowerUsername === 'aditya948351' ||
+ lowerUsername === 'devpathindcommunity-india' ||
+ lowerUsername === 'devpathind-community';
+
+ if (stats && Array.isArray(stats)) {
+ for (const contributor of stats) {
+ if (!contributor || !contributor.author) continue;
+ const login = contributor.author.login.toLowerCase();
+ let match = false;
+
+ if (isAdityaOrCommunity) {
+ // Aditya controls Aditya948351 and the community accounts
+ match = login === 'aditya948351' ||
+ login === 'devpathindcommunity-india' ||
+ login === 'devpathind-community';
+ } else {
+ match = login === lowerUsername;
+ }
+
+ if (match) {
+ commits += contributor.total || 0;
+ if (contributor.weeks) {
+ for (const week of contributor.weeks) {
+ additions += week.a || 0;
+ deletions += week.d || 0;
+ }
+ }
+ }
+ }
+ }
+
+ // Use fallback if stats returned empty or user has 0 additions but is in our fallback list
+ if (additions === 0) {
+ const fallback = GIT_FALLBACK_STATS[lowerUsername];
+ if (fallback) {
+ additions = fallback.additions;
+ deletions = fallback.deletions;
+ commits = fallback.commits;
+ }
+ }
+
+ return { additions, deletions, commits };
+};
+