diff --git a/app/components/settings/connections/ConnectionsTab.tsx b/app/components/settings/connections/ConnectionsTab.tsx index 6046526f..79c26e86 100644 --- a/app/components/settings/connections/ConnectionsTab.tsx +++ b/app/components/settings/connections/ConnectionsTab.tsx @@ -13,6 +13,8 @@ interface GitHubUserResponse { public_repos: number; followers: number; following: number; + created_at: string; + public_gists: number; } interface GitHubRepoInfo { @@ -24,12 +26,36 @@ interface GitHubRepoInfo { forks_count: number; default_branch: string; updated_at: string; + languages_url: string; +} + +interface GitHubOrganization { + login: string; + avatar_url: string; + html_url: string; +} + +interface GitHubEvent { + id: string; + type: string; + repo: { + name: string; + }; + created_at: string; +} + +interface GitHubLanguageStats { + [language: string]: number; } interface GitHubStats { repos: GitHubRepoInfo[]; totalStars: number; totalForks: number; + organizations: GitHubOrganization[]; + recentActivity: GitHubEvent[]; + languages: GitHubLanguageStats; + totalGists: number; } interface GitHubConnection { @@ -88,9 +114,54 @@ export default function ConnectionsTab() { const repos = (await reposResponse.json()) as GitHubRepoInfo[]; + // Fetch organizations + const orgsResponse = await fetch('https://api.github.com/user/orgs', { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (!orgsResponse.ok) { + throw new Error('Failed to fetch organizations'); + } + + const organizations = (await orgsResponse.json()) as GitHubOrganization[]; + + // Fetch recent activity + const eventsResponse = await fetch('https://api.github.com/users/' + connection.user?.login + '/events/public', { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (!eventsResponse.ok) { + throw new Error('Failed to fetch events'); + } + + const recentActivity = ((await eventsResponse.json()) as GitHubEvent[]).slice(0, 5); + + // Fetch languages for each repository + const languagePromises = repos.map((repo) => + fetch(repo.languages_url, { + headers: { + Authorization: `Bearer ${token}`, + }, + }).then((res) => res.json() as Promise>), + ); + + const repoLanguages = await Promise.all(languagePromises); + const languages: GitHubLanguageStats = {}; + + repoLanguages.forEach((repoLang) => { + Object.entries(repoLang).forEach(([lang, bytes]) => { + languages[lang] = (languages[lang] || 0) + bytes; + }); + }); + // Calculate total stats const totalStars = repos.reduce((acc, repo) => acc + repo.stargazers_count, 0); const totalForks = repos.reduce((acc, repo) => acc + repo.forks_count, 0); + const totalGists = connection.user?.public_gists || 0; setConnection((prev) => ({ ...prev, @@ -98,6 +169,10 @@ export default function ConnectionsTab() { repos, totalStars, totalForks, + organizations, + recentActivity, + languages, + totalGists, }, })); } catch (error) { @@ -360,6 +435,102 @@ export default function ConnectionsTab() { + {/* Organizations Section */} + {connection.stats.organizations.length > 0 && ( +
+

Organizations

+
+ {connection.stats.organizations.map((org) => ( + + {org.login} + {org.login} + + ))} +
+
+ )} + + {/* Languages Section */} +
+

Top Languages

+
+ {Object.entries(connection.stats.languages) + .sort(([, a], [, b]) => b - a) + .slice(0, 5) + .map(([language]) => ( + + {language} + + ))} +
+
+ + {/* Recent Activity Section */} +
+

Recent Activity

+
+ {connection.stats.recentActivity.map((event) => ( +
+
+
+ {event.type.replace('Event', '')} + on + + {event.repo.name} + +
+
+ {new Date(event.created_at).toLocaleDateString()} at{' '} + {new Date(event.created_at).toLocaleTimeString()} +
+
+ ))} +
+
+ + {/* Additional Stats */} +
+
+
Member Since
+
+ {new Date(connection.user.created_at).toLocaleDateString()} +
+
+
+
Public Gists
+
+ {connection.stats.totalGists} +
+
+
+
Organizations
+
+ {connection.stats.organizations.length} +
+
+
+
Languages
+
+ {Object.keys(connection.stats.languages).length} +
+
+
+ + {/* Existing repositories section */}

Recent Repositories

{connection.stats.repos.map((repo) => ( diff --git a/app/components/settings/connections/types/GitHub.ts b/app/components/settings/connections/types/GitHub.ts index d260042d..f2f1af6b 100644 --- a/app/components/settings/connections/types/GitHub.ts +++ b/app/components/settings/connections/types/GitHub.ts @@ -7,6 +7,9 @@ export interface GitHubUserResponse { public_repos: number; followers: number; following: number; + public_gists: number; + created_at: string; + updated_at: string; } export interface GitHubRepoInfo { @@ -18,12 +21,45 @@ export interface GitHubRepoInfo { forks_count: number; default_branch: string; updated_at: string; + language: string; + languages_url: string; +} + +export interface GitHubOrganization { + login: string; + avatar_url: string; + description: string; + html_url: string; +} + +export interface GitHubEvent { + id: string; + type: string; + created_at: string; + repo: { + name: string; + url: string; + }; + payload: { + action?: string; + ref?: string; + ref_type?: string; + description?: string; + }; +} + +export interface GitHubLanguageStats { + [key: string]: number; } export interface GitHubStats { repos: GitHubRepoInfo[]; totalStars: number; totalForks: number; + organizations: GitHubOrganization[]; + recentActivity: GitHubEvent[]; + languages: GitHubLanguageStats; + totalGists: number; } export interface GitHubConnection { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b95822b..a9cc1969 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9935,7 +9935,7 @@ snapshots: '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) '@vanilla-extract/babel-plugin-debug-ids': 1.2.0 '@vanilla-extract/css': 1.17.0 - esbuild: 0.17.6 + esbuild: 0.17.19 eval: 0.1.8 find-up: 5.0.0 javascript-stringify: 2.1.0