@@ -277,6 +277,7 @@ export function CredentialsManager() {
277277 oauthConnections . map ( ( service ) => ( {
278278 value : service . providerId ,
279279 label : service . name ,
280+ icon : getServiceConfigByProviderId ( service . providerId ) ?. icon ,
280281 } ) ) ,
281282 [ oauthConnections ]
282283 )
@@ -786,20 +787,22 @@ export function CredentialsManager() {
786787
787788 try {
788789 if ( credentialToDelete . type === 'oauth' ) {
789- if ( credentialToDelete . accountId && credentialToDelete . providerId ) {
790- await disconnectOAuthService . mutateAsync ( {
791- provider : credentialToDelete . providerId . split ( '-' ) [ 0 ] || credentialToDelete . providerId ,
792- providerId : credentialToDelete . providerId ,
793- serviceId : credentialToDelete . providerId ,
794- accountId : credentialToDelete . accountId ,
795- } )
796- await refetchCredentials ( )
797- window . dispatchEvent (
798- new CustomEvent ( 'oauth-credentials-updated' , {
799- detail : { providerId : credentialToDelete . providerId , workspaceId } ,
800- } )
801- )
790+ if ( ! credentialToDelete . accountId || ! credentialToDelete . providerId ) {
791+ logger . error ( 'Cannot disconnect OAuth credential: missing accountId or providerId' )
792+ return
802793 }
794+ await disconnectOAuthService . mutateAsync ( {
795+ provider : credentialToDelete . providerId . split ( '-' ) [ 0 ] || credentialToDelete . providerId ,
796+ providerId : credentialToDelete . providerId ,
797+ serviceId : credentialToDelete . providerId ,
798+ accountId : credentialToDelete . accountId ,
799+ } )
800+ await refetchCredentials ( )
801+ window . dispatchEvent (
802+ new CustomEvent ( 'oauth-credentials-updated' , {
803+ detail : { providerId : credentialToDelete . providerId , workspaceId } ,
804+ } )
805+ )
803806 } else {
804807 await deleteCredential . mutateAsync ( credentialToDelete . id )
805808 }
@@ -1016,10 +1019,33 @@ export function CredentialsManager() {
10161019 </ div >
10171020 </ div >
10181021
1022+ { createType === 'secret' && (
1023+ < div >
1024+ < Label > Mode</ Label >
1025+ < div className = 'mt-[6px]' >
1026+ < Combobox
1027+ options = { [
1028+ { value : 'single' , label : 'Single' } ,
1029+ { value : 'bulk' , label : 'Bulk' } ,
1030+ ] }
1031+ value = { createSecretInputMode === 'single' ? 'Single' : 'Bulk' }
1032+ selectedValue = { createSecretInputMode }
1033+ onChange = { ( value ) => {
1034+ setCreateSecretInputMode ( value as SecretInputMode )
1035+ setCreateError ( null )
1036+ } }
1037+ placeholder = 'Select mode'
1038+ />
1039+ </ div >
1040+ </ div >
1041+ ) }
1042+
10191043 { createType === 'oauth' ? (
10201044 < div className = 'flex flex-col gap-[10px]' >
10211045 < div >
1022- < Label > Display name</ Label >
1046+ < Label >
1047+ Display name< span className = 'ml-1' > *</ span >
1048+ </ Label >
10231049 < Input
10241050 value = { createDisplayName }
10251051 onChange = { ( event ) => setCreateDisplayName ( event . target . value ) }
@@ -1040,7 +1066,7 @@ export function CredentialsManager() {
10401066 />
10411067 </ div >
10421068 < div >
1043- < Label > OAuth service </ Label >
1069+ < Label > Account </ Label >
10441070 < div className = 'mt-[6px]' >
10451071 < Combobox
10461072 options = { oauthServiceOptions }
@@ -1051,6 +1077,27 @@ export function CredentialsManager() {
10511077 selectedValue = { createOAuthProviderId }
10521078 onChange = { setCreateOAuthProviderId }
10531079 placeholder = 'Select OAuth service'
1080+ searchable
1081+ searchPlaceholder = 'Search services...'
1082+ overlayContent = {
1083+ createOAuthProviderId
1084+ ? ( ( ) => {
1085+ const config = getServiceConfigByProviderId ( createOAuthProviderId )
1086+ const label =
1087+ oauthServiceOptions . find ( ( o ) => o . value === createOAuthProviderId )
1088+ ?. label || ''
1089+ return (
1090+ < div className = 'flex items-center gap-[8px]' >
1091+ { config &&
1092+ createElement ( config . icon , {
1093+ className : 'h-[14px] w-[14px] flex-shrink-0' ,
1094+ } ) }
1095+ < span className = 'truncate' > { label } </ span >
1096+ </ div >
1097+ )
1098+ } ) ( )
1099+ : undefined
1100+ }
10541101 />
10551102 </ div >
10561103 </ div >
@@ -1071,56 +1118,36 @@ export function CredentialsManager() {
10711118 < div className = 'flex flex-col gap-[10px]' >
10721119 < div >
10731120 < Label className = 'block' > Scope</ Label >
1074- < div className = 'mt-[6px]' >
1075- < ButtonGroup
1076- value = { createSecretScope }
1077- onValueChange = { ( value ) => setCreateSecretScope ( value as SecretScope ) }
1121+ < ButtonGroup
1122+ value = { createSecretScope }
1123+ onValueChange = { ( value ) => setCreateSecretScope ( value as SecretScope ) }
1124+ className = 'mt-[6px]'
1125+ >
1126+ < ButtonGroupItem
1127+ value = 'workspace'
1128+ className = 'h-[28px] min-w-[80px] px-[10px] py-0 text-[12px]'
10781129 >
1079- < ButtonGroupItem
1080- value = 'workspace'
1081- className = 'h-[28px] min-w-[80px] px-[10px] py-0 text-[12px]'
1082- >
1083- Workspace
1084- </ ButtonGroupItem >
1085- < ButtonGroupItem
1086- value = 'personal'
1087- className = 'h-[28px] min-w-[72px] px-[10px] py-0 text-[12px]'
1088- >
1089- Personal
1090- </ ButtonGroupItem >
1091- </ ButtonGroup >
1092- </ div >
1093- </ div >
1094- < div >
1095- < Label className = 'block' > Mode</ Label >
1096- < div className = 'mt-[6px]' >
1097- < ButtonGroup
1098- value = { createSecretInputMode }
1099- onValueChange = { ( value ) => {
1100- setCreateSecretInputMode ( value as SecretInputMode )
1101- setCreateError ( null )
1102- } }
1130+ Workspace
1131+ </ ButtonGroupItem >
1132+ < ButtonGroupItem
1133+ value = 'personal'
1134+ className = 'h-[28px] min-w-[72px] px-[10px] py-0 text-[12px]'
11031135 >
1104- < ButtonGroupItem
1105- value = 'single'
1106- className = 'h-[28px] min-w-[56px] px-[10px] py-0 text-[12px]'
1107- >
1108- Single
1109- </ ButtonGroupItem >
1110- < ButtonGroupItem
1111- value = 'bulk'
1112- className = 'h-[28px] min-w-[56px] px-[10px] py-0 text-[12px]'
1113- >
1114- Bulk
1115- </ ButtonGroupItem >
1116- </ ButtonGroup >
1117- </ div >
1136+ Personal
1137+ </ ButtonGroupItem >
1138+ </ ButtonGroup >
1139+ < p className = 'mt-[4px] text-[11px] text-[var(--text-tertiary)]' >
1140+ { createSecretScope === 'workspace'
1141+ ? 'Shared across the workspace. All members can use it.'
1142+ : 'Only visible to you. Other members cannot use it.' }
1143+ </ p >
11181144 </ div >
1119-
11201145 { createSecretInputMode === 'single' ? (
11211146 < >
11221147 < div >
1123- < Label > Secret key</ Label >
1148+ < Label >
1149+ Secret key< span className = 'ml-1' > *</ span >
1150+ </ Label >
11241151 < Input
11251152 value = { createEnvKey }
11261153 onChange = { ( event ) => {
@@ -1140,7 +1167,9 @@ export function CredentialsManager() {
11401167 </ p >
11411168 </ div >
11421169 < div >
1143- < Label > Secret value</ Label >
1170+ < Label >
1171+ Secret value< span className = 'ml-1' > *</ span >
1172+ </ Label >
11441173 < Input
11451174 type = 'password'
11461175 value = { createEnvValue }
@@ -1713,32 +1742,46 @@ export function CredentialsManager() {
17131742 </ div >
17141743 ) : (
17151744 < div className = 'flex flex-col gap-[8px]' >
1716- { sortedCredentials . map ( ( credential ) => (
1717- < div key = { credential . id } className = 'flex items-center justify-between gap-[12px]' >
1718- < div className = 'flex min-w-0 flex-col justify-center gap-[1px]' >
1719- < span className = 'truncate font-medium text-[14px]' >
1720- { credential . displayName }
1721- </ span >
1722- < p className = 'truncate text-[13px] text-[var(--text-muted)]' >
1723- { credential . description || 'No description' }
1724- </ p >
1725- </ div >
1726- < div className = 'flex flex-shrink-0 items-center gap-[4px]' >
1727- < Button variant = 'default' onClick = { ( ) => handleSelectCredential ( credential ) } >
1728- Details
1729- </ Button >
1730- { credential . role === 'admin' && (
1731- < Button
1732- variant = 'ghost'
1733- onClick = { ( ) => handleDeleteClick ( credential ) }
1734- disabled = { deleteCredential . isPending || disconnectOAuthService . isPending }
1735- >
1736- Delete
1745+ { sortedCredentials . map ( ( credential ) => {
1746+ const serviceConfig =
1747+ credential . type === 'oauth' && credential . providerId
1748+ ? getServiceConfigByProviderId ( credential . providerId )
1749+ : null
1750+
1751+ return (
1752+ < div key = { credential . id } className = 'flex items-center justify-between gap-[12px]' >
1753+ < div className = 'flex min-w-0 items-center gap-[10px]' >
1754+ { serviceConfig && (
1755+ < div className = 'flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-[6px] bg-[var(--surface-5)]' >
1756+ { createElement ( serviceConfig . icon , { className : 'h-4 w-4' } ) }
1757+ </ div >
1758+ ) }
1759+ < div className = 'flex min-w-0 flex-col justify-center gap-[1px]' >
1760+ < span className = 'truncate font-medium text-[14px]' >
1761+ { credential . displayName }
1762+ </ span >
1763+ < p className = 'truncate text-[13px] text-[var(--text-muted)]' >
1764+ { credential . description || 'No description' }
1765+ </ p >
1766+ </ div >
1767+ </ div >
1768+ < div className = 'flex flex-shrink-0 items-center gap-[4px]' >
1769+ < Button variant = 'default' onClick = { ( ) => handleSelectCredential ( credential ) } >
1770+ Details
17371771 </ Button >
1738- ) }
1772+ { credential . role === 'admin' && (
1773+ < Button
1774+ variant = 'ghost'
1775+ onClick = { ( ) => handleDeleteClick ( credential ) }
1776+ disabled = { deleteCredential . isPending || disconnectOAuthService . isPending }
1777+ >
1778+ Delete
1779+ </ Button >
1780+ ) }
1781+ </ div >
17391782 </ div >
1740- </ div >
1741- ) ) }
1783+ )
1784+ } ) }
17421785 { showNoResults && (
17431786 < div className = 'py-[16px] text-center text-[13px] text-[var(--text-muted)]' >
17441787 No credentials found matching “{ searchTerm } ”
0 commit comments