1
- import { Alert , Badge , Box , ButtonGroup , Chip , CircularProgress , Dialog , DialogContent , DialogTitle , Divider , FormControlLabel , FormHelperText , Grid2 , IconButton , Link , Modal , Paper , Stack , Switch , Tab , Tabs , TextField , Tooltip , Typography , useTheme } from "@mui/material" ;
1
+ import { Alert , Badge , Box , ButtonGroup , Chip , CircularProgress , Dialog , DialogContent , DialogTitle , Divider , FormControlLabel , FormHelperText , Grid2 , IconButton , Link , ListItem , Modal , Paper , Stack , Switch , Tab , Tabs , TextField , Tooltip , Typography , useTheme } from "@mui/material" ;
2
2
import { CheckOutlined , Close , CloseOutlined , Code , Delete , DeleteOutline , DeleteOutlined , LockReset , Save , SaveOutlined } from "@mui/icons-material" ;
3
- import { useEffect , useState } from "react" ;
3
+ import { useEffect , useMemo , useState } from "react" ;
4
4
import { CatalogItemWithName } from "../../types/catalog" ;
5
5
import Secrets from "../../Secrets" ;
6
6
import { v1 } from "@docker/extension-api-client-types" ;
7
7
import { useCatalogContext } from "../../context/CatalogContext" ;
8
8
import { useConfigContext } from "../../context/ConfigContext" ;
9
- import { deepFlattenObject , deepGet , deepSet , mergeDeep } from "../../MergeDeep" ;
10
- import { DeepObject } from "../../types/utils" ;
11
- import { Parameter , ParameterArray , ParameterObject , Parameters , ParsedParameters , Config } from "../../types/config" ;
12
- import { Ref } from "../../Refs" ;
13
9
import { ASSIGNED_SECRET_PLACEHOLDER , CATALOG_LAYOUT_SX , UNASSIGNED_SECRET_PLACEHOLDER } from "../../Constants" ;
14
- import JsonSchemaLibrary from "json-schema-library" ;
15
10
import ConfigEditor from "./ConfigEditor" ;
16
11
17
12
// Styles for the tab panel
@@ -42,11 +37,6 @@ function TabPanel(props: TabPanelProps) {
42
37
) ;
43
38
}
44
39
45
- // Define types reference
46
- const types = [ 'string' , 'number' , 'boolean' , 'array' , 'object' ] as const ;
47
-
48
-
49
-
50
40
interface ConfigurationModalProps {
51
41
open : boolean ;
52
42
onClose : ( ) => void ;
@@ -55,7 +45,6 @@ interface ConfigurationModalProps {
55
45
onToggleRegister : ( checked : boolean ) => void ;
56
46
registered : boolean ;
57
47
onSecretChange : ( secret : { name : string , value : string } ) => Promise < void > ;
58
- unAssignedSecrets : { name : string , assigned : boolean } [ ] ;
59
48
}
60
49
61
50
@@ -67,42 +56,13 @@ const ConfigurationModal = ({
67
56
onToggleRegister,
68
57
registered,
69
58
onSecretChange,
70
- unAssignedSecrets,
71
59
} : ConfigurationModalProps ) => {
72
60
73
- const { registryItems, secrets } = useCatalogContext ( ) ;
74
- const { config , configLoading } = useConfigContext ( ) ;
61
+ const { registryItems, secrets, getCanRegisterCatalogItem } = useCatalogContext ( ) ;
62
+ const { configLoading , config } = useConfigContext ( ) ;
75
63
const [ localSecrets , setLocalSecrets ] = useState < { [ key : string ] : string | undefined } > ( { } ) ;
76
64
const theme = useTheme ( ) ;
77
65
78
- // Helper function to get default values based on type
79
- const getDefaultValue = ( schema : any ) => {
80
- if ( ! schema || ! schema . type ) return '' ;
81
-
82
- switch ( schema . type ) {
83
- case 'string' :
84
- return schema . default || '' ;
85
- case 'number' :
86
- case 'integer' :
87
- return schema . default || 0 ;
88
- case 'boolean' :
89
- return schema . default || false ;
90
- case 'array' :
91
- return schema . default || [ ] ;
92
- case 'object' :
93
- if ( schema . properties ) {
94
- const objTemplate : Record < string , any > = { } ;
95
- Object . entries ( schema . properties ) . forEach ( ( [ key , propSchema ] : [ string , any ] ) => {
96
- objTemplate [ key ] = getDefaultValue ( propSchema ) ;
97
- } ) ;
98
- return schema . default || objTemplate ;
99
- }
100
- return schema . default || { } ;
101
- default :
102
- return '' ;
103
- }
104
- } ;
105
-
106
66
const toolChipStyle = {
107
67
padding : '2px 8px' ,
108
68
justifyContent : 'center' ,
@@ -128,7 +88,7 @@ const ConfigurationModal = ({
128
88
129
89
// Load assigned secrets
130
90
useEffect ( ( ) => {
131
- const loadedSecrets = Secrets . getAssignedSecrets ( catalogItem , secrets ) ;
91
+ const loadedSecrets = Secrets . getSecretsWithAssignment ( catalogItem , secrets ) ;
132
92
setAssignedSecrets ( loadedSecrets ) ;
133
93
setLocalSecrets ( loadedSecrets . reduce ( ( acc , secret ) => {
134
94
acc [ secret . name ] = secret . assigned ? ASSIGNED_SECRET_PLACEHOLDER : '' ;
@@ -142,25 +102,16 @@ const ConfigurationModal = ({
142
102
setTabValue ( newValue ) ;
143
103
} ;
144
104
145
- // Determine if we should show the secrets tab and config tab
146
- const hasSecrets = assignedSecrets . length > 0 ;
147
- const hasConfig = catalogItem . config && catalogItem . config . length > 0 ;
105
+ // Memoize the canRegister value to prevent unnecessary recalculations
106
+ const canRegister = useMemo ( ( ) => getCanRegisterCatalogItem ( catalogItem ) , [ getCanRegisterCatalogItem , catalogItem , config ] ) ;
148
107
149
- // If there's only one tab to show, automatically select it
150
- useEffect ( ( ) => {
151
- if ( ! hasSecrets && hasConfig || hasSecrets && ! hasConfig ) {
152
- setTabValue ( 0 ) ; // Config tab
153
- }
154
- } , [ hasSecrets , hasConfig ] ) ;
155
-
156
- const hasAllSecrets = unAssignedSecrets . length === 0
157
- const emptyConfig = catalogItem . config && ( ! config ?. [ catalogItem . name ] || Object . keys ( config ?. [ catalogItem . name ] || { } ) . length === 0 )
108
+ const contributesNoConfigOrSecrets = ( ! catalogItem . config || catalogItem . config . length === 0 ) && ( ! catalogItem . secrets || catalogItem . secrets . length === 0 ) ;
158
109
159
110
useEffect ( ( ) => {
160
- if ( ! hasAllSecrets || emptyConfig ) {
161
- setTabValue ( 1 ) ;
111
+ if ( ! canRegister && ! contributesNoConfigOrSecrets ) {
112
+ setTabValue ( 1 ) ; // Config tab
162
113
}
163
- } , [ hasAllSecrets , emptyConfig ] ) ;
114
+ } , [ canRegister , contributesNoConfigOrSecrets ] ) ;
164
115
165
116
166
117
if ( ! registryItems ) {
@@ -200,8 +151,8 @@ const ConfigurationModal = ({
200
151
< Typography sx = { { mt : 2 , maxHeight : '5em' , overflow : 'auto' } } color = "text.secondary" >
201
152
{ catalogItem . description }
202
153
</ Typography >
203
- < Tooltip placement = "right" title = { ! hasAllSecrets || emptyConfig ? 'You must assign all secrets and configure the item before it can be used.' : '' } >
204
- < FormControlLabel control = { < Switch disabled = { ! hasAllSecrets || emptyConfig } checked = { registered } onChange = { ( e ) => onToggleRegister ( e . target . checked ) } /> } label = { registered ? 'Disable ' + `${ catalogItem . name } tools` : 'Enable ' + `${ catalogItem . name } tools` } sx = { { mt : 2 } } />
154
+ < Tooltip placement = "right" title = { ! canRegister ? 'You must assign all secrets and configure the item before it can be used.' : '' } >
155
+ < FormControlLabel control = { < Switch disabled = { ! canRegister } checked = { registered } onChange = { ( e ) => onToggleRegister ( e . target . checked ) } /> } label = { registered ? 'Disable ' + `${ catalogItem . name } tools` : 'Enable ' + `${ catalogItem . name } tools` } sx = { { mt : 2 } } />
205
156
</ Tooltip >
206
157
< Divider sx = { { mt : 2 } } />
207
158
< Typography variant = "caption" sx = { { mt : 2 , color : 'text.secondary' } } >
@@ -220,10 +171,9 @@ const ConfigurationModal = ({
220
171
< Tabs value = { tabValue } onChange = { handleTabChange } >
221
172
< Tab label = "Tools" />
222
173
{ /* <Tab label="Prompts" /> */ }
223
- < Tab disabled = { ! hasSecrets && ! hasConfig } label = { < Badge invisible = { hasAllSecrets && ! emptyConfig } sx = { { pl : 1 , pr : 1 } } variant = "dot" badgeContent = { hasSecrets ? 'Secrets' : 'Config' } color = "error" > Config & Secrets </ Badge > } />
174
+ < Tab disabled = { contributesNoConfigOrSecrets } label = { < Badge invisible = { canRegister } sx = { { pl : 1 , pr : 1 } } variant = "dot" badgeContent = { catalogItem . config && catalogItem . config . length > 0 ? 'Secrets' : 'Config' } color = "error" > Config & Secrets </ Badge > } />
224
175
</ Tabs >
225
176
</ Box >
226
-
227
177
< TabPanel value = { tabValue } index = { 0 } >
228
178
{ ! catalogItem ?. tools ?. length && (
229
179
< Typography >
@@ -247,46 +197,51 @@ const ConfigurationModal = ({
247
197
< Stack direction = "column" spacing = { 2 } sx = { { border : '2px solid' , borderColor : theme . palette . warning . contrastText , borderRadius : 2 , p : 2 , mt : 2 } } >
248
198
< ConfigEditor catalogItem = { catalogItem } />
249
199
< Typography variant = "h6" sx = { { mb : 1 } } > Secrets</ Typography >
250
- { assignedSecrets ?. map ( secret => {
251
- const secretEdited = secret . assigned ? localSecrets [ secret . name ] !== ASSIGNED_SECRET_PLACEHOLDER : localSecrets [ secret . name ] !== '' ;
252
- return (
253
- < Stack key = { secret . name } direction = "row" spacing = { 2 } alignItems = "center" >
254
- < TextField key = { secret . name } label = { secret . name } value = { localSecrets [ secret . name ] } fullWidth onChange = { ( e ) => {
255
- setLocalSecrets ( { ...localSecrets , [ secret . name ] : e . target . value } ) ;
256
- } } type = 'password' />
257
- { secret . assigned && ! secretEdited && < IconButton size = "small" color = "error" onClick = { ( ) => {
258
- setLocalSecrets ( { ...localSecrets , [ secret . name ] : UNASSIGNED_SECRET_PLACEHOLDER } ) ;
259
- onSecretChange ( { name : secret . name , value : UNASSIGNED_SECRET_PLACEHOLDER } ) ;
260
- } } >
261
- < DeleteOutlined />
262
- </ IconButton > }
263
- { secretEdited && < ButtonGroup >
264
- < IconButton onClick = { async ( ) => {
265
- await onSecretChange ( { name : secret . name , value : localSecrets [ secret . name ] ! } ) ;
266
- } } >
267
- < CheckOutlined sx = { { color : 'success.main' } } />
268
- </ IconButton >
269
- < IconButton onClick = { async ( ) => {
270
- setLocalSecrets ( { ...localSecrets , [ secret . name ] : secret . assigned ? ASSIGNED_SECRET_PLACEHOLDER : '' } ) ;
271
- } } >
272
- < CloseOutlined sx = { { color : 'error.main' } } />
273
- </ IconButton >
274
- </ ButtonGroup > }
275
- </ Stack >
200
+ {
201
+ catalogItem . secrets && catalogItem . secrets ?. length > 0 ? (
202
+ assignedSecrets ?. map ( secret => {
203
+ const secretEdited = secret . assigned ? localSecrets [ secret . name ] !== ASSIGNED_SECRET_PLACEHOLDER : localSecrets [ secret . name ] !== '' ;
204
+ return (
205
+ < Stack key = { secret . name } direction = "row" spacing = { 2 } alignItems = "center" >
206
+ < TextField key = { secret . name } label = { secret . name } value = { localSecrets [ secret . name ] } fullWidth onChange = { ( e ) => {
207
+ setLocalSecrets ( { ...localSecrets , [ secret . name ] : e . target . value } ) ;
208
+ } } type = 'password' />
209
+ { secret . assigned && ! secretEdited && < IconButton size = "small" color = "error" onClick = { ( ) => {
210
+ setLocalSecrets ( { ...localSecrets , [ secret . name ] : UNASSIGNED_SECRET_PLACEHOLDER } ) ;
211
+ onSecretChange ( { name : secret . name , value : UNASSIGNED_SECRET_PLACEHOLDER } ) ;
212
+ } } >
213
+ < DeleteOutlined />
214
+ </ IconButton > }
215
+ { secretEdited && < ButtonGroup >
216
+ < IconButton onClick = { async ( ) => {
217
+ await onSecretChange ( { name : secret . name , value : localSecrets [ secret . name ] ! } ) ;
218
+ } } >
219
+ < CheckOutlined sx = { { color : 'success.main' } } />
220
+ </ IconButton >
221
+ < IconButton onClick = { async ( ) => {
222
+ setLocalSecrets ( { ...localSecrets , [ secret . name ] : secret . assigned ? ASSIGNED_SECRET_PLACEHOLDER : '' } ) ;
223
+ } } >
224
+ < CloseOutlined sx = { { color : 'error.main' } } />
225
+ </ IconButton >
226
+ </ ButtonGroup > }
227
+ </ Stack >
228
+ )
229
+ } ) ) : (
230
+ < Alert severity = "info" > No secrets available for this item.</ Alert >
276
231
)
277
- } ) }
232
+ }
278
233
</ Stack >
279
234
</ Stack >
280
- </ TabPanel >
235
+ </ TabPanel >
281
236
< TabPanel value = { tabValue } index = { 2 } >
282
237
< Typography > Examples</ Typography >
283
238
WIP
284
239
</ TabPanel >
285
240
</ >
286
241
) }
287
- </ Paper >
288
- </ Modal >
289
- ) ;
242
+ </ Paper >
243
+ </ Modal >
244
+ )
290
245
} ;
291
246
292
247
export default ConfigurationModal ;
0 commit comments