1- import React , { useState } from 'react' ;
1+ import React , { useState , useEffect } from 'react' ;
22import { toast } from 'react-toastify' ;
33import Cookies from 'js-cookie' ;
44import { logStore } from '~/lib/stores/logs' ;
55
6+ interface GitHubUserResponse {
7+ login : string ;
8+ id : number ;
9+ [ key : string ] : any ; // for other properties we don't explicitly need
10+ }
11+
612export default function ConnectionsTab ( ) {
713 const [ githubUsername , setGithubUsername ] = useState ( Cookies . get ( 'githubUsername' ) || '' ) ;
814 const [ githubToken , setGithubToken ] = useState ( Cookies . get ( 'githubToken' ) || '' ) ;
15+ const [ isConnected , setIsConnected ] = useState ( false ) ;
16+ const [ isVerifying , setIsVerifying ] = useState ( false ) ;
17+
18+ useEffect ( ( ) => {
19+ // Check if credentials exist and verify them
20+ if ( githubUsername && githubToken ) {
21+ verifyGitHubCredentials ( ) ;
22+ }
23+ } , [ ] ) ;
924
10- const handleSaveConnection = ( ) => {
11- Cookies . set ( 'githubUsername' , githubUsername ) ;
12- Cookies . set ( 'githubToken' , githubToken ) ;
13- logStore . logSystem ( 'GitHub connection settings updated' , {
14- username : githubUsername ,
15- hasToken : ! ! githubToken ,
16- } ) ;
17- toast . success ( 'GitHub credentials saved successfully!' ) ;
18- Cookies . set ( 'git:github.com' , JSON . stringify ( { username : githubToken , password : 'x-oauth-basic' } ) ) ;
25+ const verifyGitHubCredentials = async ( ) => {
26+ setIsVerifying ( true ) ;
27+ try {
28+ const response = await fetch ( 'https://api.github.com/user' , {
29+ headers : {
30+ Authorization : `Bearer ${ githubToken } ` ,
31+ } ,
32+ } ) ;
33+
34+ if ( response . ok ) {
35+ const data = ( await response . json ( ) ) as GitHubUserResponse ;
36+ if ( data . login === githubUsername ) {
37+ setIsConnected ( true ) ;
38+ return true ;
39+ }
40+ }
41+ setIsConnected ( false ) ;
42+ return false ;
43+ } catch ( error ) {
44+ console . error ( 'Error verifying GitHub credentials:' , error ) ;
45+ setIsConnected ( false ) ;
46+ return false ;
47+ } finally {
48+ setIsVerifying ( false ) ;
49+ }
50+ } ;
51+
52+ const handleSaveConnection = async ( ) => {
53+ if ( ! githubUsername || ! githubToken ) {
54+ toast . error ( 'Please provide both GitHub username and token' ) ;
55+ return ;
56+ }
57+
58+ setIsVerifying ( true ) ;
59+ const isValid = await verifyGitHubCredentials ( ) ;
60+
61+ if ( isValid ) {
62+ Cookies . set ( 'githubUsername' , githubUsername ) ;
63+ Cookies . set ( 'githubToken' , githubToken ) ;
64+ logStore . logSystem ( 'GitHub connection settings updated' , {
65+ username : githubUsername ,
66+ hasToken : ! ! githubToken ,
67+ } ) ;
68+ toast . success ( 'GitHub credentials verified and saved successfully!' ) ;
69+ Cookies . set ( 'git:github.com' , JSON . stringify ( { username : githubToken , password : 'x-oauth-basic' } ) ) ;
70+ setIsConnected ( true ) ;
71+ } else {
72+ toast . error ( 'Invalid GitHub credentials. Please check your username and token.' ) ;
73+ }
74+ } ;
75+
76+ const handleDisconnect = ( ) => {
77+ Cookies . remove ( 'githubUsername' ) ;
78+ Cookies . remove ( 'githubToken' ) ;
79+ Cookies . remove ( 'git:github.com' ) ;
80+ setGithubUsername ( '' ) ;
81+ setGithubToken ( '' ) ;
82+ setIsConnected ( false ) ;
83+ logStore . logSystem ( 'GitHub connection removed' ) ;
84+ toast . success ( 'GitHub connection removed successfully!' ) ;
1985 } ;
2086
2187 return (
@@ -28,7 +94,8 @@ export default function ConnectionsTab() {
2894 type = "text"
2995 value = { githubUsername }
3096 onChange = { ( e ) => setGithubUsername ( e . target . value ) }
31- className = "w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"
97+ disabled = { isVerifying }
98+ className = "w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor disabled:opacity-50"
3299 />
33100 </ div >
34101 < div className = "flex-1" >
@@ -37,17 +104,41 @@ export default function ConnectionsTab() {
37104 type = "password"
38105 value = { githubToken }
39106 onChange = { ( e ) => setGithubToken ( e . target . value ) }
40- className = "w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"
107+ disabled = { isVerifying }
108+ className = "w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor disabled:opacity-50"
41109 />
42110 </ div >
43111 </ div >
44- < div className = "flex mb-4" >
45- < button
46- onClick = { handleSaveConnection }
47- className = "bg-bolt-elements-button-primary-background rounded-lg px-4 py-2 mr-2 transition-colors duration-200 hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-button-primary-text"
48- >
49- Save Connection
50- </ button >
112+ < div className = "flex mb-4 items-center" >
113+ { ! isConnected ? (
114+ < button
115+ onClick = { handleSaveConnection }
116+ disabled = { isVerifying || ! githubUsername || ! githubToken }
117+ className = "bg-bolt-elements-button-primary-background rounded-lg px-4 py-2 mr-2 transition-colors duration-200 hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-button-primary-text disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
118+ >
119+ { isVerifying ? (
120+ < >
121+ < div className = "i-ph:spinner animate-spin mr-2" />
122+ Verifying...
123+ </ >
124+ ) : (
125+ 'Connect'
126+ ) }
127+ </ button >
128+ ) : (
129+ < button
130+ onClick = { handleDisconnect }
131+ className = "bg-bolt-elements-button-danger-background rounded-lg px-4 py-2 mr-2 transition-colors duration-200 hover:bg-bolt-elements-button-danger-backgroundHover text-bolt-elements-button-danger-text"
132+ >
133+ Disconnect
134+ </ button >
135+ ) }
136+ { isConnected && (
137+ < span className = "text-sm text-green-600 flex items-center" >
138+ < div className = "i-ph:check-circle mr-1" />
139+ Connected to GitHub
140+ </ span >
141+ ) }
51142 </ div >
52143 </ div >
53144 ) ;
0 commit comments