1- import { CircularProgress , Dialog , DialogContent , DialogTitle , Divider , IconButton , Stack , TextField , Typography } from "@mui/material" ;
2- import { Card , CardContent } from "@mui/material" ;
3- import { useEffect , useState } from "react" ;
4- import { CatalogItemRichened } from "../../types/catalog" ;
5- import { Save , LockReset } from "@mui/icons-material" ;
6- import ConfigurationModal from "./Modal" ;
7- import Top from "./Top" ;
8- import Center from "./Center" ;
9- import Bottom from "./Bottom" ;
10- import { v1 } from "@docker/extension-api-client-types" ;
11- import { useSecrets } from "../../queries/useSecrets" ;
12- import { useCatalogAll , useCatalogOperations } from "../../queries/useCatalog" ;
13- import { MCP_POLICY_NAME } from "../../Constants" ;
1+ import { v1 } from '@docker/extension-api-client-types' ;
2+ import { LockReset , Save } from '@mui/icons-material' ;
3+ import {
4+ Card ,
5+ CardActionArea ,
6+ CardContent ,
7+ CircularProgress ,
8+ Dialog ,
9+ DialogContent ,
10+ DialogTitle ,
11+ Divider ,
12+ IconButton ,
13+ Stack ,
14+ TextField ,
15+ Typography ,
16+ } from '@mui/material' ;
17+ import { useState } from 'react' ;
18+
19+ import { MCP_POLICY_NAME } from '../../Constants' ;
20+ import { useCatalogAll , useCatalogOperations } from '../../queries/useCatalog' ;
21+ import { useSecrets } from '../../queries/useSecrets' ;
22+ import { CatalogItemRichened } from '../../types/catalog' ;
23+ import Bottom from './Bottom' ;
24+ import Center from './Center' ;
25+ import ConfigurationModal from './Modal' ;
26+ import Top from './Top' ;
1427
1528type TileProps = {
16- item : CatalogItemRichened ;
17- client : v1 . DockerDesktopClient ;
18- }
29+ item : CatalogItemRichened ;
30+ client : v1 . DockerDesktopClient ;
31+ } ;
1932
2033const Tile = ( { item, client } : TileProps ) => {
34+ const [ showSecretDialog , setShowSecretDialog ] = useState ( false ) ;
35+ const [ assignedSecrets ] = useState < { name : string ; assigned : boolean } [ ] > ( [ ] ) ;
36+ const [ changedSecrets , setChangedSecrets ] = useState < {
37+ [ key : string ] : string | undefined ;
38+ } > ( { } ) ;
39+ const [ secretLoading , setSecretLoading ] = useState ( false ) ;
40+ const [ showConfigModal , setShowConfigModal ] = useState ( false ) ;
41+ const { isLoading : secretsLoading , mutate : mutateSecret } =
42+ useSecrets ( client ) ;
43+ const { registerCatalogItem, unregisterCatalogItem } =
44+ useCatalogOperations ( client ) ;
45+ const { registryLoading } = useCatalogAll ( client ) ;
2146
22- const [ showSecretDialog , setShowSecretDialog ] = useState ( false )
23- const [ assignedSecrets ] = useState < { name : string , assigned : boolean } [ ] > ( [ ] )
24- const [ changedSecrets , setChangedSecrets ] = useState < { [ key : string ] : string | undefined } > ( { } )
25- const [ secretLoading , setSecretLoading ] = useState ( false )
26- const [ showConfigModal , setShowConfigModal ] = useState ( false )
27- const { isLoading : secretsLoading , mutate : mutateSecret } = useSecrets ( client )
28- const { registerCatalogItem, unregisterCatalogItem } = useCatalogOperations ( client )
29- const { registryLoading } = useCatalogAll ( client )
30-
31- if ( registryLoading || secretsLoading ) {
32- return < >
33- < CircularProgress size = { 20 } />
34- < Typography > Loading registry...</ Typography >
35- </ >
36- }
47+ if ( registryLoading || secretsLoading ) {
48+ return (
49+ < >
50+ < CircularProgress size = { 20 } />
51+ < Typography > Loading registry...</ Typography >
52+ </ >
53+ ) ;
54+ }
3755
38- const unAssignedSecrets = assignedSecrets . filter ( s => ! s . assigned )
56+ const unAssignedSecrets = assignedSecrets . filter ( ( s ) => ! s . assigned ) ;
3957
40- return (
41- < >
42- < Dialog open = { showSecretDialog } onClose = { ( ) => setShowSecretDialog ( false ) } >
43- < DialogTitle >
44- < Typography variant = "h6" >
45- Secrets
46- </ Typography >
47- </ DialogTitle >
48- < DialogContent >
49- < Stack direction = "column" spacing = { 2 } >
50- { assignedSecrets ?. map ( secret => {
51- const isAssigned = assignedSecrets . find ( s => s . name === secret . name )
52- return (
53- < Stack key = { secret . name } direction = "row" spacing = { 2 } alignItems = "center" >
54- < Typography variant = "body2" > { secret . name } { isAssigned ?. assigned ? 'assigned' : 'not assigned' } </ Typography >
55- < TextField placeholder = { isAssigned ?. assigned ? '********' : 'Enter secret value' } type = "password" key = { secret . name } label = { secret . name } value = { changedSecrets [ secret . name ] || '' } onChange = { ( event ) => setChangedSecrets ( { ...changedSecrets , [ secret . name ] : event . target . value } ) } />
56- { isAssigned ?. assigned && changedSecrets [ secret . name ] && < IconButton onClick = { ( ) => setChangedSecrets ( { ...changedSecrets , [ secret . name ] : undefined } ) } >
57- < LockReset />
58- </ IconButton > }
59- { changedSecrets [ secret . name ] && < IconButton onClick = { ( ) => {
60- setSecretLoading ( true )
61- mutateSecret . mutateAsync ( { name : secret . name , value : changedSecrets [ secret . name ] || '' , policies : [ MCP_POLICY_NAME ] } ) . then ( ( ) => {
62- setSecretLoading ( false )
63- const newChangedSecrets = { ...changedSecrets }
64- delete newChangedSecrets [ secret . name ]
65- setChangedSecrets ( newChangedSecrets )
66- } )
67- } } >
68- { secretLoading ? < CircularProgress size = { 20 } /> : < Save /> }
69- </ IconButton > }
70- </ Stack >
71- )
72- } ) }
73- </ Stack >
74- </ DialogContent >
75- </ Dialog >
76- < ConfigurationModal
77- open = { showConfigModal }
78- onClose = { ( ) => setShowConfigModal ( false ) }
79- catalogItem = { item }
80- client = { client }
81- />
82- < Card onClick = { ( e ) => {
83- if ( ( e . target as HTMLElement ) . tagName !== 'INPUT' ) {
84- setShowConfigModal ( true )
85- }
86- } } sx = { { height : 150 , borderColor : 'divider' , borderWidth : 1 , borderStyle : 'solid' , p : 0 , cursor : 'pointer' , transition : 'background-color 0.1s ease' , '&:hover' : { backgroundColor : 'action.hover' } } } >
87- < CardContent sx = { { paddingBottom : 0 , paddingTop : 2 } } >
88- < Stack direction = "column" spacing = { 0 } >
89- < Top onToggleRegister = { ( checked ) => {
90- if ( checked ) {
91- registerCatalogItem ( item )
92- } else {
93- unregisterCatalogItem ( item )
94- }
95- } } item = { item } />
96- < Center item = { item } />
97- < Divider sx = { { marginBottom : 1 } } />
98- < Bottom item = { item } needsConfiguration = { Boolean ( unAssignedSecrets . length ) } />
99- </ Stack >
100- </ CardContent >
101- </ Card >
102- </ >
103- )
104- }
58+ return (
59+ < >
60+ < Dialog
61+ open = { showSecretDialog }
62+ onClose = { ( ) => setShowSecretDialog ( false ) }
63+ >
64+ < DialogTitle >
65+ < Typography variant = "h6" > Secrets</ Typography >
66+ </ DialogTitle >
67+ < DialogContent >
68+ < Stack direction = "column" spacing = { 2 } >
69+ { assignedSecrets ?. map ( ( secret ) => {
70+ const isAssigned = assignedSecrets . find (
71+ ( s ) => s . name === secret . name
72+ ) ;
73+ return (
74+ < Stack
75+ key = { secret . name }
76+ direction = "row"
77+ spacing = { 2 }
78+ alignItems = "center"
79+ >
80+ < Typography variant = "body2" >
81+ { secret . name } { ' ' }
82+ { isAssigned ?. assigned ? 'assigned' : 'not assigned' }
83+ </ Typography >
84+ < TextField
85+ placeholder = {
86+ isAssigned ?. assigned ? '********' : 'Enter secret value'
87+ }
88+ type = "password"
89+ key = { secret . name }
90+ label = { secret . name }
91+ value = { changedSecrets [ secret . name ] || '' }
92+ onChange = { ( event ) =>
93+ setChangedSecrets ( {
94+ ...changedSecrets ,
95+ [ secret . name ] : event . target . value ,
96+ } )
97+ }
98+ />
99+ { isAssigned ?. assigned && changedSecrets [ secret . name ] && (
100+ < IconButton
101+ onClick = { ( ) =>
102+ setChangedSecrets ( {
103+ ...changedSecrets ,
104+ [ secret . name ] : undefined ,
105+ } )
106+ }
107+ >
108+ < LockReset />
109+ </ IconButton >
110+ ) }
111+ { changedSecrets [ secret . name ] && (
112+ < IconButton
113+ onClick = { ( ) => {
114+ setSecretLoading ( true ) ;
115+ mutateSecret
116+ . mutateAsync ( {
117+ name : secret . name ,
118+ value : changedSecrets [ secret . name ] || '' ,
119+ policies : [ MCP_POLICY_NAME ] ,
120+ } )
121+ . then ( ( ) => {
122+ setSecretLoading ( false ) ;
123+ const newChangedSecrets = { ...changedSecrets } ;
124+ delete newChangedSecrets [ secret . name ] ;
125+ setChangedSecrets ( newChangedSecrets ) ;
126+ } ) ;
127+ } }
128+ >
129+ { secretLoading ? (
130+ < CircularProgress size = { 20 } />
131+ ) : (
132+ < Save />
133+ ) }
134+ </ IconButton >
135+ ) }
136+ </ Stack >
137+ ) ;
138+ } ) }
139+ </ Stack >
140+ </ DialogContent >
141+ </ Dialog >
142+ < ConfigurationModal
143+ open = { showConfigModal }
144+ onClose = { ( ) => setShowConfigModal ( false ) }
145+ catalogItem = { item }
146+ client = { client }
147+ />
148+ < Card sx = { { height : 150 } } >
149+ < CardActionArea
150+ onClick = { ( e ) => {
151+ if ( ( e . target as HTMLElement ) . tagName !== 'INPUT' ) {
152+ setShowConfigModal ( true ) ;
153+ }
154+ } }
155+ >
156+ < CardContent sx = { { paddingBottom : 0 , paddingTop : 2 } } >
157+ < Stack direction = "column" spacing = { 0 } >
158+ < Top
159+ onToggleRegister = { ( checked ) => {
160+ if ( checked ) {
161+ registerCatalogItem ( item ) ;
162+ } else {
163+ unregisterCatalogItem ( item ) ;
164+ }
165+ } }
166+ item = { item }
167+ />
168+ < Center item = { item } />
169+ < Divider sx = { { marginBottom : 1 } } />
170+ < Bottom
171+ item = { item }
172+ needsConfiguration = { Boolean ( unAssignedSecrets . length ) }
173+ />
174+ </ Stack >
175+ </ CardContent >
176+ </ CardActionArea >
177+ </ Card >
178+ </ >
179+ ) ;
180+ } ;
105181
106- export default Tile ;
182+ export default Tile ;
0 commit comments