Files
bolt-diy/app/utils/githubStats.ts
Stijnus 3ea96506ea feat: gitLab Integration Implementation / github refactor / overal improvements (#1963)
* Add GitLab integration components

Introduced PushToGitLabDialog and GitlabConnection components to handle GitLab project connections and push functionality. Includes user authentication, project handling, and UI for seamless integration with GitLab.

* Add components for GitLab connection and push dialog

Introduce `GitlabConnection` and `PushToGitLabDialog` components to handle GitLab integration. These components allow users to connect their GitLab account, manage recent projects, and push code to a GitLab repository with detailed configurations and feedback.

* Fix GitLab personal access tokens link to use correct URL

* Update GitHub push call to use new pushToRepository method

* Enhance GitLab integration with performance improvements

- Add comprehensive caching system for repositories and user data
- Implement pagination and search/filter functionality with debouncing
- Add skeleton loaders and improved loading states
- Implement retry logic for API calls with exponential backoff
- Add background refresh capabilities
- Improve error handling and user feedback
- Optimize API calls to reduce loading times

* feat: implement GitLab integration with connection management and repository handling

- Add GitLab connection UI components
- Implement GitLab API service for repository operations
- Add GitLab connection store for state management
- Update existing connection components (Vercel, Netlify)
- Add repository listing and statistics display
- Refactor GitLab components into organized folder structure

* fix: resolve GitLab deployment issues and improve user experience

- Fix DialogTitle accessibility warnings for screen readers
- Remove CORS-problematic attributes from avatar images to prevent loading errors
- Enhance GitLab API error handling with detailed error messages
- Fix project creation settings to prevent initial commit conflicts
- Add automatic GitLab connection state initialization on app startup
- Improve deployment dialog UI with better error handling and user feedback
- Add GitLab deployment source type to action runner system
- Clean up deprecated push dialog files and consolidate deployment components

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: implement GitHub clone repository dialog functionality

This commit fixes the missing GitHub repository selection dialog in the "Clone a repo" feature
by implementing the same elegant interface pattern used by GitLab.

Key Changes:
- Added onCloneRepository prop support to GitHubConnection component
- Updated RepositoryCard to generate proper GitHub clone URLs (https://github.com/{full_name}.git)
- Implemented full GitHub repository selection dialog in GitCloneButton.tsx
- Added proper dialog close handling after successful clone operations
- Maintained existing GitHub connection settings page functionality

Technical Details:
- Follows same component patterns as GitLab implementation
- Uses proper TypeScript interfaces for clone URL handling
- Includes professional dialog styling with loading states
- Supports repository search, pagination, and authentication flow

The GitHub clone experience now matches GitLab's functionality, providing users with
a unified and intuitive repository selection interface across both providers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Clean up unused connection components

- Remove ConnectionForm.tsx (unused GitHub form component)
- Remove CreateBranchDialog.tsx (unused branch creation dialog)
- Remove RepositoryDialogContext.tsx (unused context provider)
- Remove empty components/ directory

These files were not referenced anywhere in the codebase and were leftover from development.

* Remove environment variables info section from ConnectionsTab

- Remove collapsible environment variables section
- Clean up unused state and imports
- Simplify the connections tab UI

* Reorganize connections folder structure

- Create netlify/ folder and move NetlifyConnection.tsx
- Create vercel/ folder and move VercelConnection.tsx
- Add index.ts files for both netlify and vercel folders
- Update imports in ConnectionsTab.tsx to use new folder structure
- All connection components now follow consistent folder organization

---------

Co-authored-by: Hayat Bourgi <hayat.bourgi@montyholding.com>
Co-authored-by: Hayat55 <53140162+Hayat55@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-05 14:01:33 +02:00

168 lines
5.0 KiB
TypeScript

import type { GitHubStats, GitHubLanguageStats } from '~/types/GitHub';
export interface GitHubStatsSummary {
totalRepositories: number;
totalStars: number;
totalForks: number;
publicRepositories: number;
privateRepositories: number;
followers: number;
publicGists: number;
topLanguages: Array<{ name: string; count: number; percentage: number }>;
recentActivityCount: number;
lastUpdated?: string;
}
export function calculateStatsSummary(stats: GitHubStats): GitHubStatsSummary {
// Calculate total repositories
const totalRepositories = stats.repos?.length || stats.publicRepos || 0;
// Calculate language statistics
const topLanguages = calculateTopLanguages(stats.languages || {});
return {
totalRepositories,
totalStars: stats.totalStars || stats.stars || 0,
totalForks: stats.totalForks || stats.forks || 0,
publicRepositories: stats.publicRepos || 0,
privateRepositories: stats.privateRepos || 0,
followers: stats.followers || 0,
publicGists: stats.totalGists || stats.publicGists || 0,
topLanguages,
recentActivityCount: stats.recentActivity?.length || 0,
lastUpdated: stats.lastUpdated,
};
}
export function calculateTopLanguages(languages: GitHubLanguageStats): Array<{
name: string;
count: number;
percentage: number;
}> {
if (!languages || Object.keys(languages).length === 0) {
return [];
}
const totalCount = Object.values(languages).reduce((sum, count) => sum + count, 0);
if (totalCount === 0) {
return [];
}
return Object.entries(languages)
.map(([name, count]) => ({
name,
count,
percentage: Math.round((count / totalCount) * 100),
}))
.sort((a, b) => b.count - a.count)
.slice(0, 10); // Top 10 languages
}
export function formatRepositoryStats(stats: GitHubStats) {
const repositories = stats.repos || [];
// Sort repositories by stars (descending)
const topStarredRepos = repositories
.filter((repo) => repo.stargazers_count > 0)
.sort((a, b) => b.stargazers_count - a.stargazers_count)
.slice(0, 5);
// Sort repositories by forks (descending)
const topForkedRepos = repositories
.filter((repo) => repo.forks_count > 0)
.sort((a, b) => b.forks_count - a.forks_count)
.slice(0, 5);
// Recent repositories (by update date)
const recentRepos = repositories
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
.slice(0, 10);
return {
total: repositories.length,
topStarredRepos,
topForkedRepos,
recentRepos,
totalStars: repositories.reduce((sum, repo) => sum + repo.stargazers_count, 0),
totalForks: repositories.reduce((sum, repo) => sum + repo.forks_count, 0),
};
}
export function formatActivitySummary(stats: GitHubStats) {
const activity = stats.recentActivity || [];
// Group activities by type
const activityByType = activity.reduce(
(acc, event) => {
acc[event.type] = (acc[event.type] || 0) + 1;
return acc;
},
{} as Record<string, number>,
);
// Format activity types for display
const formattedActivity = Object.entries(activityByType)
.map(([type, count]) => ({
type: formatActivityType(type),
count,
}))
.sort((a, b) => b.count - a.count);
return {
total: activity.length,
byType: formattedActivity,
recent: activity.slice(0, 5),
};
}
function formatActivityType(type: string): string {
const typeMap: Record<string, string> = {
PushEvent: 'Pushes',
CreateEvent: 'Created',
DeleteEvent: 'Deleted',
ForkEvent: 'Forks',
WatchEvent: 'Stars',
IssuesEvent: 'Issues',
PullRequestEvent: 'Pull Requests',
ReleaseEvent: 'Releases',
PublicEvent: 'Made Public',
};
return typeMap[type] || type.replace('Event', '');
}
export function calculateGrowthMetrics(currentStats: GitHubStats, previousStats?: GitHubStats) {
if (!previousStats) {
return null;
}
const starsDiff = (currentStats.totalStars || 0) - (previousStats.totalStars || 0);
const forksDiff = (currentStats.totalForks || 0) - (previousStats.totalForks || 0);
const followersDiff = (currentStats.followers || 0) - (previousStats.followers || 0);
const reposDiff = (currentStats.repos?.length || 0) - (previousStats.repos?.length || 0);
return {
stars: {
current: currentStats.totalStars || 0,
change: starsDiff,
percentage: previousStats.totalStars ? Math.round((starsDiff / previousStats.totalStars) * 100) : 0,
},
forks: {
current: currentStats.totalForks || 0,
change: forksDiff,
percentage: previousStats.totalForks ? Math.round((forksDiff / previousStats.totalForks) * 100) : 0,
},
followers: {
current: currentStats.followers || 0,
change: followersDiff,
percentage: previousStats.followers ? Math.round((followersDiff / previousStats.followers) * 100) : 0,
},
repositories: {
current: currentStats.repos?.length || 0,
change: reposDiff,
percentage: previousStats.repos?.length ? Math.round((reposDiff / previousStats.repos.length) * 100) : 0,
},
};
}