@@ -2,9 +2,23 @@ import { GitHubOrgList } from '../components/github-org-list';
22import { getCache , setCache } from '@/lib/cache' ;
33import { Suspense } from 'react' ;
44import { StatsCards } from '@/components/stats-cards' ;
5- import { GitHubRepo } from './types/github' ;
5+ import { GitHubRepo , LanguageLinesStats } from './types/github' ;
66import { Metadata } from 'next' ;
77import defaultMetadata from './metadata' ;
8+ import { ThemeToggle } from '@/components/theme-toggle' ;
9+
10+ const LANGUAGE_COLORS : Record < string , string > = {
11+ TypeScript : "#3178c6" ,
12+ JavaScript : "#f1e05a" ,
13+ Python : "#3572A5" ,
14+ HTML : "#e34c26" ,
15+ Vue : "#41b883" ,
16+ Astro : "#f0f0f0" ,
17+ PHP : "#4F5D95" ,
18+ Java : "#b07219" ,
19+ "C#" : "#178600" ,
20+ Other : "#6e7681"
21+ } ;
822
923// Add configuration type
1024interface GitHubConfig {
@@ -165,9 +179,9 @@ export default async function Home() {
165179 // For users, we need to fetch all repos (including private ones if token has access)
166180 const reposUrl = isUser
167181 ? userData . login === org
168- ? `${ config . baseUrl } /user/repos?per_page=100&type =owner&sort=updated` // For authenticated user
182+ ? `${ config . baseUrl } /user/repos?per_page=100&affiliation =owner&sort=updated` // For authenticated user
169183 : `${ config . baseUrl } /users/${ org } /repos?per_page=100&type=owner&sort=updated` // For other users
170- : `${ config . baseUrl } /orgs/${ org } /repos?per_page=100` ;
184+ : `${ config . baseUrl } /orgs/${ org } /repos?per_page=100&type=all ` ;
171185
172186 const [ orgResponse , reposResponse ] = await Promise . all ( [
173187 isUser
@@ -181,6 +195,12 @@ export default async function Home() {
181195 reposResponse . json ( )
182196 ] ) ;
183197
198+ // Add error handling for reposData
199+ if ( ! Array . isArray ( reposData ) ) {
200+ console . error ( `Invalid repos data for ${ org } :` , reposData ) ;
201+ return { ...orgData , repos : [ ] } ;
202+ }
203+
184204 console . log ( `Fetched ${ reposData . length } repositories for ${ isUser ? 'user' : 'org' } ${ org } ` ) ;
185205
186206 // Enhance each repository with additional details
@@ -194,12 +214,17 @@ export default async function Home() {
194214 return cachedRepoData ;
195215 }
196216
197- const [ contributorsResponse , commitsResponse ] = await Promise . all ( [
217+ const [ contributorsResponse , commitsResponse , languagesResponse ] = await Promise . all ( [
198218 fetch ( repo . contributors_url , { headers : config . headers } ) ,
199- fetch ( `${ repo . url } /commits?per_page=1` , { headers : config . headers } )
219+ fetch ( `${ repo . url } /commits?per_page=1` , { headers : config . headers } ) ,
220+ fetch ( repo . languages_url , { headers : config . headers } )
221+ ] ) ;
222+
223+ const [ contributorsData , languagesData ] = await Promise . all ( [
224+ contributorsResponse . json ( ) ,
225+ languagesResponse . json ( )
200226 ] ) ;
201227
202- const contributorsData = await contributorsResponse . json ( ) ;
203228 const commitsLinkHeader = commitsResponse . headers . get ( "Link" ) ;
204229 let commitsCount = null ;
205230
@@ -214,6 +239,7 @@ export default async function Home() {
214239 ...repo ,
215240 contributors : Array . isArray ( contributorsData ) ? contributorsData . slice ( 0 , 5 ) : [ ] ,
216241 commits_count : commitsCount ,
242+ languages_stats : languagesData
217243 } ;
218244
219245 // Cache individual repo data
@@ -266,46 +292,124 @@ export default async function Home() {
266292 const totalPrivateRepos = orgsData . reduce ( ( sum , org ) => sum + ( org . total_private_repos || 0 ) , 0 ) ;
267293 const totalRepos = totalPublicRepos + totalPrivateRepos ;
268294
269- // Calculate total stats
270- const totalStats = {
271- totalRepos,
272- publicRepos : totalPublicRepos ,
273- privateRepos : totalPrivateRepos ,
274- totalStars : orgsData . reduce ( ( sum , org ) =>
275- sum + org . repos . reduce ( ( repoSum : number , repo : GitHubRepo ) => repoSum + repo . stargazers_count , 0 ) , 0
276- ) ,
277- totalForks : orgsData . reduce ( ( sum , org ) =>
278- sum + org . repos . reduce ( ( repoSum : number , repo : GitHubRepo ) => repoSum + repo . forks_count , 0 ) , 0
279- ) ,
280- organizations : orgsData . map ( org => ( {
281- login : org . login ,
282- avatar_url : org . avatar_url ,
283- name : org . name || org . login
284- } ) ) ,
285- contributors : Array . from ( new Set (
286- orgsData . flatMap ( org =>
287- org . repos . flatMap ( ( repo : GitHubRepo ) => repo . contributors )
288- ) . map ( c => JSON . stringify ( { login : c . login , avatar_url : c . avatar_url } ) )
289- ) ) . map ( str => JSON . parse ( str ) ) ,
290- totalContributors : new Set (
291- orgsData . flatMap ( ( org : any ) =>
292- org . repos . flatMap ( ( repo : GitHubRepo ) => repo . contributors . map ( c => c . login ) )
295+ // Calculate total lines stats
296+ const languageBytesStats = orgsData . reduce ( ( stats : Record < string , number > , org ) => {
297+ org . repos . forEach ( ( repo : GitHubRepo ) => {
298+ if ( repo . languages_stats ) {
299+ Object . entries ( repo . languages_stats ) . forEach ( ( [ lang , bytes ] ) => {
300+ stats [ lang ] = ( stats [ lang ] || 0 ) + bytes ;
301+ } ) ;
302+ }
303+ } ) ;
304+ return stats ;
305+ } , { } ) ;
306+
307+ const totalBytes = Object . values ( languageBytesStats ) . reduce ( ( sum , bytes ) => sum + bytes , 0 ) ;
308+ const BYTES_PER_LINE = 120 ; // Average line length estimation
309+ const LINES_PER_HOUR = 50 / 8 ; // 50 lines per day (8 hours)
310+ const totalLines = Math . round ( totalBytes / BYTES_PER_LINE ) ;
311+ const totalHours = Math . round ( totalLines / LINES_PER_HOUR ) ;
312+
313+ function createLanguageStats (
314+ name : string ,
315+ bytes : number ,
316+ percentage : number ,
317+ color ?: string
318+ ) : LanguageLinesStats {
319+ return {
320+ name,
321+ bytes,
322+ lines : Math . round ( bytes / BYTES_PER_LINE ) ,
323+ percentage,
324+ color
325+ } ;
326+ }
327+
328+ // Get top 5 languages by bytes and combine rest into "Others"
329+ const topLanguagesByBytes = Object . entries ( languageBytesStats )
330+ . sort ( ( [ , a ] , [ , b ] ) => b - a )
331+ . reduce < LanguageLinesStats [ ] > ( ( acc , [ name , bytes ] , index ) => {
332+ if ( index < 5 ) {
333+ acc . push ( createLanguageStats ( name , bytes , ( bytes / totalBytes ) * 100 , LANGUAGE_COLORS [ name ] || LANGUAGE_COLORS . Other ) ) ;
334+ } else if ( index === 5 ) {
335+ const otherBytes = Object . entries ( languageBytesStats ) . slice ( 5 ) . reduce ( ( sum , [ , bytes ] ) => sum + bytes , 0 ) ;
336+ acc . push ( createLanguageStats ( 'Others' , otherBytes , ( otherBytes / totalBytes ) * 100 , LANGUAGE_COLORS . Other ) ) ;
337+ }
338+ return acc ;
339+ } , [ ] ) ;
340+
341+ // Calculate total stats
342+ const totalStats = {
343+ totalRepos,
344+ publicRepos : totalPublicRepos ,
345+ privateRepos : totalPrivateRepos ,
346+ totalStars : orgsData . reduce ( ( sum , org ) =>
347+ sum + org . repos . reduce ( ( repoSum : number , repo : GitHubRepo ) => repoSum + repo . stargazers_count , 0 ) , 0
348+ ) ,
349+ totalForks : orgsData . reduce ( ( sum , org ) =>
350+ sum + org . repos . reduce ( ( repoSum : number , repo : GitHubRepo ) => repoSum + repo . forks_count , 0 ) , 0
351+ ) ,
352+ totalIssues : orgsData . reduce ( ( sum , org ) =>
353+ sum + org . repos . reduce ( ( repoSum : number , repo : GitHubRepo ) => repoSum + repo . open_issues || 0 , 0 ) , 0
354+ ) ,
355+ organizations : orgsData . map ( org => ( {
356+ login : org . login ,
357+ avatar_url : org . avatar_url ,
358+ name : org . name || org . login
359+ } ) ) ,
360+ contributors : Array . from ( new Set (
361+ orgsData . flatMap ( org =>
362+ org . repos . flatMap ( ( repo : GitHubRepo ) => repo . contributors )
363+ ) . map ( c => JSON . stringify ( { login : c . login , avatar_url : c . avatar_url } ) )
364+ ) ) . map ( str => JSON . parse ( str ) ) ,
365+ totalContributors : new Set (
366+ orgsData . flatMap ( ( org : any ) =>
367+ org . repos . flatMap ( ( repo : GitHubRepo ) => repo . contributors . map ( c => c . login ) )
368+ )
369+ ) . size ,
370+ totalCommits : orgsData . reduce ( ( sum , org ) =>
371+ sum + org . repos . reduce ( ( repoSum : number , repo : GitHubRepo ) => repoSum + ( repo . commits_count || 0 ) , 0 ) , 0
372+ ) ,
373+ lastUpdated : new Date ( ) . toISOString ( ) ,
374+ languages : Object . entries (
375+ orgsData . reduce ( ( langs : Record < string , number > , org ) => {
376+ org . repos . forEach ( ( repo : GitHubRepo ) => {
377+ if ( repo . language ) {
378+ langs [ repo . language ] = ( langs [ repo . language ] || 0 ) + 1 ;
379+ }
380+ } ) ;
381+ return langs ;
382+ } , { } )
293383 )
294- ) . size ,
295- totalCommits : orgsData . reduce ( ( sum , org ) =>
296- sum + org . repos . reduce ( ( repoSum : number , repo : GitHubRepo ) => repoSum + ( repo . commits_count || 0 ) , 0 ) , 0
297- ) ,
298- lastUpdated : new Date ( ) . toISOString ( ) ,
299- } ;
384+ . map ( ( [ name , count ] ) => ( {
385+ name,
386+ count,
387+ lines : Math . round ( count / BYTES_PER_LINE ) ,
388+ percentage : ( count / totalRepos ) * 100
389+ } ) )
390+ . sort ( ( a , b ) => b . count - a . count )
391+ . slice ( 0 , 5 ) ,
392+ languageBytes : {
393+ languages : topLanguagesByBytes ,
394+ totalBytes
395+ } ,
396+ developmentStats : {
397+ totalLines,
398+ totalHours
399+ }
400+ } ;
300401
301402 return (
302403 < main className = "container mx-auto p-4" >
303- < h1 className = "text-4xl font-bold mb-8" >
304- { process . env . NEXT_PUBLIC_APP_NAME }
305- < small className = "block font-thin text-base text-gray-700" >
306- { process . env . NEXT_PUBLIC_APP_DESCRIPTION }
307- </ small >
308- </ h1 >
404+ < div className = "flex justify-between items-center mb-8" >
405+ < h1 className = "text-4xl font-bold" >
406+ { process . env . NEXT_PUBLIC_APP_NAME }
407+ < small className = "block font-thin text-base text-gray-700 dark:text-gray-300" >
408+ { process . env . NEXT_PUBLIC_APP_DESCRIPTION }
409+ </ small >
410+ </ h1 >
411+ < ThemeToggle />
412+ </ div >
309413 < StatsCards stats = { totalStats } />
310414 < Suspense fallback = { < GitHubOrgList orgsData = { [ ] } loading = { true } /> } >
311415 < GitHubOrgList orgsData = { sortedOrgsData } />
0 commit comments