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+ } , [ ] ) ;
24+
25+ const verifyGitHubCredentials = async ( ) => {
26+ setIsVerifying ( true ) ;
27+
28+ try {
29+ const response = await fetch ( 'https://api.github.com/user' , {
30+ headers : {
31+ Authorization : `Bearer ${ githubToken } ` ,
32+ } ,
33+ } ) ;
34+
35+ if ( response . ok ) {
36+ const data = ( await response . json ( ) ) as GitHubUserResponse ;
37+
38+ if ( data . login === githubUsername ) {
39+ setIsConnected ( true ) ;
40+ return true ;
41+ }
42+ }
43+
44+ setIsConnected ( false ) ;
45+
46+ return false ;
47+ } catch ( error ) {
48+ console . error ( 'Error verifying GitHub credentials:' , error ) ;
49+ setIsConnected ( false ) ;
950
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' } ) ) ;
51+ return false ;
52+ } finally {
53+ setIsVerifying ( false ) ;
54+ }
55+ } ;
56+
57+ const handleSaveConnection = async ( ) => {
58+ if ( ! githubUsername || ! githubToken ) {
59+ toast . error ( 'Please provide both GitHub username and token' ) ;
60+ return ;
61+ }
62+
63+ setIsVerifying ( true ) ;
64+
65+ const isValid = await verifyGitHubCredentials ( ) ;
66+
67+ if ( isValid ) {
68+ Cookies . set ( 'githubUsername' , githubUsername ) ;
69+ Cookies . set ( 'githubToken' , githubToken ) ;
70+ logStore . logSystem ( 'GitHub connection settings updated' , {
71+ username : githubUsername ,
72+ hasToken : ! ! githubToken ,
73+ } ) ;
74+ toast . success ( 'GitHub credentials verified and saved successfully!' ) ;
75+ Cookies . set ( 'git:github.com' , JSON . stringify ( { username : githubToken , password : 'x-oauth-basic' } ) ) ;
76+ setIsConnected ( true ) ;
77+ } else {
78+ toast . error ( 'Invalid GitHub credentials. Please check your username and token.' ) ;
79+ }
80+ } ;
81+
82+ const handleDisconnect = ( ) => {
83+ Cookies . remove ( 'githubUsername' ) ;
84+ Cookies . remove ( 'githubToken' ) ;
85+ Cookies . remove ( 'git:github.com' ) ;
86+ setGithubUsername ( '' ) ;
87+ setGithubToken ( '' ) ;
88+ setIsConnected ( false ) ;
89+ logStore . logSystem ( 'GitHub connection removed' ) ;
90+ toast . success ( 'GitHub connection removed successfully!' ) ;
1991 } ;
2092
2193 return (
@@ -28,7 +100,8 @@ export default function ConnectionsTab() {
28100 type = "text"
29101 value = { githubUsername }
30102 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"
103+ disabled = { isVerifying }
104+ 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"
32105 />
33106 </ div >
34107 < div className = "flex-1" >
@@ -37,17 +110,41 @@ export default function ConnectionsTab() {
37110 type = "password"
38111 value = { githubToken }
39112 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"
113+ disabled = { isVerifying }
114+ 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"
41115 />
42116 </ div >
43117 </ 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 >
118+ < div className = "flex mb-4 items-center" >
119+ { ! isConnected ? (
120+ < button
121+ onClick = { handleSaveConnection }
122+ disabled = { isVerifying || ! githubUsername || ! githubToken }
123+ 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"
124+ >
125+ { isVerifying ? (
126+ < >
127+ < div className = "i-ph:spinner animate-spin mr-2" />
128+ Verifying...
129+ </ >
130+ ) : (
131+ 'Connect'
132+ ) }
133+ </ button >
134+ ) : (
135+ < button
136+ onClick = { handleDisconnect }
137+ 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"
138+ >
139+ Disconnect
140+ </ button >
141+ ) }
142+ { isConnected && (
143+ < span className = "text-sm text-green-600 flex items-center" >
144+ < div className = "i-ph:check-circle mr-1" />
145+ Connected to GitHub
146+ </ span >
147+ ) }
51148 </ div >
52149 </ div >
53150 ) ;
0 commit comments