* 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>
227 lines
6.3 KiB
TypeScript
227 lines
6.3 KiB
TypeScript
import { atom, computed } from 'nanostores';
|
|
import Cookies from 'js-cookie';
|
|
import { logStore } from '~/lib/stores/logs';
|
|
import { gitHubApiService } from '~/lib/services/githubApiService';
|
|
import { calculateStatsSummary } from '~/utils/githubStats';
|
|
import type { GitHubConnection } from '~/types/GitHub';
|
|
|
|
// Auto-connect using environment variable
|
|
const envToken = import.meta.env?.VITE_GITHUB_ACCESS_TOKEN;
|
|
const envTokenType = import.meta.env?.VITE_GITHUB_TOKEN_TYPE;
|
|
|
|
const githubConnectionAtom = atom<GitHubConnection>({
|
|
user: null,
|
|
token: envToken || '',
|
|
tokenType:
|
|
envTokenType === 'classic' || envTokenType === 'fine-grained'
|
|
? (envTokenType as 'classic' | 'fine-grained')
|
|
: 'classic',
|
|
});
|
|
|
|
// Initialize connection from localStorage on startup
|
|
function initializeConnection() {
|
|
try {
|
|
const savedConnection = localStorage.getItem('github_connection');
|
|
|
|
if (savedConnection) {
|
|
const parsed = JSON.parse(savedConnection);
|
|
|
|
// Ensure tokenType is set
|
|
if (!parsed.tokenType) {
|
|
parsed.tokenType = 'classic';
|
|
}
|
|
|
|
// Only set if we have a valid user
|
|
if (parsed.user) {
|
|
githubConnectionAtom.set(parsed);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error initializing GitHub connection:', error);
|
|
localStorage.removeItem('github_connection');
|
|
}
|
|
}
|
|
|
|
// Initialize on module load (client-side only)
|
|
if (typeof window !== 'undefined') {
|
|
initializeConnection();
|
|
}
|
|
|
|
// Computed store for checking if connected
|
|
export const isGitHubConnected = computed(githubConnectionAtom, (connection) => !!connection.user);
|
|
|
|
// Computed store for GitHub stats summary
|
|
export const githubStatsSummary = computed(githubConnectionAtom, (connection) => {
|
|
if (!connection.stats) {
|
|
return null;
|
|
}
|
|
|
|
return calculateStatsSummary(connection.stats);
|
|
});
|
|
|
|
// Connection status atoms
|
|
export const isGitHubConnecting = atom(false);
|
|
export const isGitHubLoadingStats = atom(false);
|
|
|
|
// GitHub connection store methods
|
|
export const githubConnectionStore = {
|
|
// Get current connection
|
|
get: () => githubConnectionAtom.get(),
|
|
|
|
// Connect to GitHub
|
|
async connect(token: string, tokenType: 'classic' | 'fine-grained' = 'classic'): Promise<void> {
|
|
if (isGitHubConnecting.get()) {
|
|
throw new Error('Connection already in progress');
|
|
}
|
|
|
|
isGitHubConnecting.set(true);
|
|
|
|
try {
|
|
// Fetch user data
|
|
const { user, rateLimit } = await gitHubApiService.fetchUser(token, tokenType);
|
|
|
|
// Create connection object
|
|
const connection: GitHubConnection = {
|
|
user,
|
|
token,
|
|
tokenType,
|
|
rateLimit,
|
|
};
|
|
|
|
// Set cookies for client-side access
|
|
Cookies.set('githubUsername', user.login);
|
|
Cookies.set('githubToken', token);
|
|
Cookies.set('git:github.com', JSON.stringify({ username: token, password: 'x-oauth-basic' }));
|
|
|
|
// Store connection details in localStorage
|
|
localStorage.setItem('github_connection', JSON.stringify(connection));
|
|
|
|
// Update atom
|
|
githubConnectionAtom.set(connection);
|
|
|
|
logStore.logInfo('Connected to GitHub', {
|
|
type: 'system',
|
|
message: `Connected to GitHub as ${user.login}`,
|
|
});
|
|
|
|
// Fetch stats in background
|
|
this.fetchStats().catch((error) => {
|
|
console.error('Failed to fetch initial GitHub stats:', error);
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to connect to GitHub:', error);
|
|
logStore.logError(`GitHub authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`, {
|
|
type: 'system',
|
|
message: 'GitHub authentication failed',
|
|
});
|
|
throw error;
|
|
} finally {
|
|
isGitHubConnecting.set(false);
|
|
}
|
|
},
|
|
|
|
// Disconnect from GitHub
|
|
disconnect(): void {
|
|
// Clear atoms
|
|
githubConnectionAtom.set({
|
|
user: null,
|
|
token: '',
|
|
tokenType: 'classic',
|
|
});
|
|
|
|
// Clear localStorage
|
|
localStorage.removeItem('github_connection');
|
|
|
|
// Clear cookies
|
|
Cookies.remove('githubUsername');
|
|
Cookies.remove('githubToken');
|
|
Cookies.remove('git:github.com');
|
|
|
|
// Clear API service cache
|
|
gitHubApiService.clearCache();
|
|
|
|
logStore.logInfo('Disconnected from GitHub', {
|
|
type: 'system',
|
|
message: 'Disconnected from GitHub',
|
|
});
|
|
},
|
|
|
|
// Fetch GitHub stats
|
|
async fetchStats(): Promise<void> {
|
|
const connection = githubConnectionAtom.get();
|
|
|
|
if (!connection.user || !connection.token) {
|
|
throw new Error('Not connected to GitHub');
|
|
}
|
|
|
|
if (isGitHubLoadingStats.get()) {
|
|
return; // Already loading
|
|
}
|
|
|
|
isGitHubLoadingStats.set(true);
|
|
|
|
try {
|
|
const stats = await gitHubApiService.fetchStats(connection.token, connection.tokenType);
|
|
|
|
// Update connection with stats
|
|
const updatedConnection: GitHubConnection = {
|
|
...connection,
|
|
stats,
|
|
};
|
|
|
|
// Update localStorage
|
|
localStorage.setItem('github_connection', JSON.stringify(updatedConnection));
|
|
|
|
// Update atom
|
|
githubConnectionAtom.set(updatedConnection);
|
|
|
|
logStore.logInfo('GitHub stats refreshed', {
|
|
type: 'system',
|
|
message: 'Successfully refreshed GitHub statistics',
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to fetch GitHub stats:', error);
|
|
|
|
// If the error is due to expired token, disconnect
|
|
if (error instanceof Error && error.message.includes('401')) {
|
|
logStore.logError('GitHub token has expired', {
|
|
type: 'system',
|
|
message: 'GitHub token has expired. Please reconnect your account.',
|
|
});
|
|
this.disconnect();
|
|
}
|
|
|
|
throw error;
|
|
} finally {
|
|
isGitHubLoadingStats.set(false);
|
|
}
|
|
},
|
|
|
|
// Update token type
|
|
updateTokenType(tokenType: 'classic' | 'fine-grained'): void {
|
|
const connection = githubConnectionAtom.get();
|
|
const updatedConnection = {
|
|
...connection,
|
|
tokenType,
|
|
};
|
|
|
|
githubConnectionAtom.set(updatedConnection);
|
|
localStorage.setItem('github_connection', JSON.stringify(updatedConnection));
|
|
},
|
|
|
|
// Clear stats cache
|
|
clearCache(): void {
|
|
const connection = githubConnectionAtom.get();
|
|
|
|
if (connection.token) {
|
|
gitHubApiService.clearUserCache(connection.token);
|
|
}
|
|
},
|
|
|
|
// Subscribe to connection changes
|
|
subscribe: githubConnectionAtom.subscribe.bind(githubConnectionAtom),
|
|
};
|
|
|
|
// Export the atom for direct access
|
|
export { githubConnectionAtom };
|