1
- import React , { useState } from 'react' ;
1
+ import React , { useState , useEffect } from 'react' ;
2
2
import { toast } from 'react-toastify' ;
3
3
import Cookies from 'js-cookie' ;
4
4
import { logStore } from '~/lib/stores/logs' ;
5
5
6
+ interface GitHubUserResponse {
7
+ login : string ;
8
+ id : number ;
9
+ [ key : string ] : any ; // for other properties we don't explicitly need
10
+ }
11
+
6
12
export default function ConnectionsTab ( ) {
7
13
const [ githubUsername , setGithubUsername ] = useState ( Cookies . get ( 'githubUsername' ) || '' ) ;
8
14
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 ) ;
9
50
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!' ) ;
19
91
} ;
20
92
21
93
return (
@@ -28,7 +100,8 @@ export default function ConnectionsTab() {
28
100
type = "text"
29
101
value = { githubUsername }
30
102
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"
32
105
/>
33
106
</ div >
34
107
< div className = "flex-1" >
@@ -37,17 +110,41 @@ export default function ConnectionsTab() {
37
110
type = "password"
38
111
value = { githubToken }
39
112
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"
41
115
/>
42
116
</ div >
43
117
</ 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
+ ) }
51
148
</ div >
52
149
</ div >
53
150
) ;
0 commit comments