import React, { useState, useEffect } from 'react'; import { logStore } from '~/lib/stores/logs'; import { classNames } from '~/utils/classNames'; import { motion } from 'framer-motion'; import { toast } from 'react-toastify'; interface GitHubUserResponse { login: string; avatar_url: string; html_url: string; name: string; bio: string; public_repos: number; followers: number; following: number; } interface GitHubRepoInfo { name: string; full_name: string; html_url: string; description: string; stargazers_count: number; forks_count: number; default_branch: string; updated_at: string; } interface GitHubStats { repos: GitHubRepoInfo[]; totalStars: number; totalForks: number; } interface GitHubConnection { user: GitHubUserResponse | null; token: string; tokenType: 'classic' | 'fine-grained'; stats?: GitHubStats; } export default function ConnectionsTab() { const [connection, setConnection] = useState({ user: null, token: '', tokenType: 'classic', }); const [isLoading, setIsLoading] = useState(true); const [isConnecting, setIsConnecting] = useState(false); const [isFetchingStats, setIsFetchingStats] = useState(false); // Load saved connection on mount useEffect(() => { const savedConnection = localStorage.getItem('github_connection'); if (savedConnection) { const parsed = JSON.parse(savedConnection); // Ensure backward compatibility with existing connections if (!parsed.tokenType) { parsed.tokenType = 'classic'; } setConnection(parsed); if (parsed.user && parsed.token) { fetchGitHubStats(parsed.token); } } setIsLoading(false); }, []); const fetchGitHubStats = async (token: string) => { try { setIsFetchingStats(true); // Fetch repositories const reposResponse = await fetch('https://api.github.com/user/repos?sort=updated&per_page=10', { headers: { Authorization: `Bearer ${token}`, }, }); if (!reposResponse.ok) { throw new Error('Failed to fetch repositories'); } const repos = (await reposResponse.json()) as GitHubRepoInfo[]; // 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); setConnection((prev) => ({ ...prev, stats: { repos, totalStars, totalForks, }, })); } catch (error) { logStore.logError('Failed to fetch GitHub stats', { error }); toast.error('Failed to fetch GitHub statistics'); } finally { setIsFetchingStats(false); } }; const fetchGithubUser = async (token: string) => { try { setIsConnecting(true); const response = await fetch('https://api.github.com/user', { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new Error('Invalid token or unauthorized'); } const data = (await response.json()) as GitHubUserResponse; const newConnection: GitHubConnection = { user: data, token, tokenType: connection.tokenType, }; // Save connection localStorage.setItem('github_connection', JSON.stringify(newConnection)); setConnection(newConnection); // Fetch additional stats await fetchGitHubStats(token); toast.success('Successfully connected to GitHub'); } catch (error) { logStore.logError('Failed to authenticate with GitHub', { error }); toast.error('Failed to connect to GitHub'); setConnection({ user: null, token: '', tokenType: 'classic' }); } finally { setIsConnecting(false); } }; const handleConnect = async (event: React.FormEvent) => { event.preventDefault(); await fetchGithubUser(connection.token); }; const handleDisconnect = () => { localStorage.removeItem('github_connection'); setConnection({ user: null, token: '', tokenType: 'classic' }); toast.success('Disconnected from GitHub'); }; if (isLoading) { return ; } return (
{/* Header */}

Connection Settings

Manage your external service connections and integrations

{/* GitHub Connection */}

GitHub Connection

setConnection((prev) => ({ ...prev, token: e.target.value }))} disabled={isConnecting || !!connection.user} placeholder={`Enter your GitHub ${connection.tokenType === 'classic' ? 'personal access token' : 'fine-grained token'}`} className={classNames( 'w-full px-3 py-2 rounded-lg text-sm', 'bg-[#F8F8F8] dark:bg-[#1A1A1A]', 'border border-[#E5E5E5] dark:border-[#333333]', 'text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary', 'focus:outline-none focus:ring-1 focus:ring-purple-500', 'disabled:opacity-50', )} />
{!connection.user ? ( ) : ( )} {connection.user && (
Connected to GitHub )}
{connection.user && (
{connection.user.login}

{connection.user.name}

@{connection.user.login}

{isFetchingStats ? (
Fetching GitHub stats...
) : ( connection.stats && (

Public Repos

{connection.user.public_repos}

Total Stars

{connection.stats.totalStars}

Total Forks

{connection.stats.totalForks}

) )}
)}
); } function LoadingSpinner() { return (
Loading...
); }