@@ -14,8 +14,23 @@ import SyncStatus from '@gitops/Statuses/SyncStatus';
1414import { ArgoServer , getArgoServer , getFriendlyClusterName } from '@gitops/utils/gitops' ;
1515import { useGitOpsTranslation } from '@gitops/utils/hooks/useGitOpsTranslation' ;
1616import { useObjectModifyPermissions } from '@gitops/utils/utils' ;
17- import { k8sUpdate , ResourceLink , useK8sModel } from '@openshift-console/dynamic-plugin-sdk' ;
18- import { Label as PfLabel , ToggleGroup , ToggleGroupItem } from '@patternfly/react-core' ;
17+ import {
18+ k8sUpdate ,
19+ ResourceLink ,
20+ useK8sModel ,
21+ useModal ,
22+ } from '@openshift-console/dynamic-plugin-sdk' ;
23+ import { ModalComponent } from '@openshift-console/dynamic-plugin-sdk/lib/app/modal-support/ModalProvider' ;
24+ import {
25+ Button ,
26+ Label as PfLabel ,
27+ Modal ,
28+ ModalBody ,
29+ ModalFooter ,
30+ ModalHeader ,
31+ ToggleGroup ,
32+ ToggleGroupItem ,
33+ } from '@patternfly/react-core' ;
1934import {
2035 DescriptionList ,
2136 Flex ,
@@ -47,7 +62,7 @@ const ApplicationDetailsTab: React.FC<ApplicationDetailsTabProps> = ({ obj }) =>
4762 const [ canPatch , canUpdate ] = useObjectModifyPermissions ( obj , ApplicationModel ) ;
4863
4964 const [ argoServer , setArgoServer ] = React . useState < ArgoServer > ( { host : '' , protocol : '' } ) ;
50-
65+ const launchModal = useModal ( ) ;
5166 React . useEffect ( ( ) => {
5267 ( async ( ) => {
5368 getArgoServer ( model , obj )
@@ -60,9 +75,39 @@ const ApplicationDetailsTab: React.FC<ApplicationDetailsTabProps> = ({ obj }) =>
6075 } ) ( ) ;
6176 } , [ model , obj ] ) ;
6277
78+ const modalStyle : React . CSSProperties = {
79+ padding : '1rem 1rem' ,
80+ textAlign : 'left' ,
81+ zIndex : 9999 ,
82+ width : '500px' ,
83+ } ;
84+
85+ const syncErrorModal : ModalComponent = ( props ) => {
86+ return (
87+ < Modal
88+ isOpen
89+ onClose = { props ?. closeModal }
90+ style = { modalStyle }
91+ aria-describedby = "modal-title-icon-description"
92+ aria-labelledby = "title-icon-modal-title"
93+ >
94+ < ModalHeader title = "Error" titleIconVariant = "danger" labelId = "title-icon-modal-title" />
95+ < ModalBody >
96+ < span id = "modal-title-icon-description" >
97+ Sync policy change failed. Check your application/logs and try again.
98+ </ span >
99+ </ ModalBody >
100+ < ModalFooter >
101+ < Button key = "cancel" variant = "primary" onClick = { props ?. closeModal } >
102+ Close
103+ </ Button >
104+ </ ModalFooter >
105+ </ Modal >
106+ ) ;
107+ } ;
108+
63109 const onChangeAutomated = ( event : React . MouseEvent < any > | React . KeyboardEvent | MouseEvent ) => {
64110 const id = event . currentTarget . id ;
65-
66111 switch ( id ) {
67112 case 'automated' : {
68113 if ( obj . spec . syncPolicy ?. automated ) {
@@ -73,7 +118,7 @@ const ApplicationDetailsTab: React.FC<ApplicationDetailsTabProps> = ({ obj }) =>
73118 break ;
74119 }
75120 case 'self-heal' : {
76- if ( obj . spec . syncPolicy . automated . selfHeal ) {
121+ if ( obj . spec . syncPolicy ? .automated ? .selfHeal ) {
77122 obj . spec . syncPolicy . automated . selfHeal = false ;
78123 } else {
79124 obj . spec . syncPolicy . automated = {
@@ -84,7 +129,7 @@ const ApplicationDetailsTab: React.FC<ApplicationDetailsTabProps> = ({ obj }) =>
84129 break ;
85130 }
86131 case 'prune' : {
87- if ( obj . spec . syncPolicy . automated . prune ) {
132+ if ( obj . spec . syncPolicy ? .automated ? .prune ) {
88133 obj . spec . syncPolicy . automated . prune = false ;
89134 } else {
90135 obj . spec . syncPolicy . automated = { ...obj . spec . syncPolicy . automated , ...{ prune : true } } ;
@@ -95,7 +140,13 @@ const ApplicationDetailsTab: React.FC<ApplicationDetailsTabProps> = ({ obj }) =>
95140 k8sUpdate ( {
96141 model : ApplicationModel ,
97142 data : obj ,
98- } ) ;
143+ } )
144+ . then ( ( ) => {
145+ // ignore
146+ } )
147+ . catch ( ( e ) => {
148+ launchModal ( syncErrorModal , { errorMessage : e . message } ) ;
149+ } ) ;
99150 } ;
100151
101152 let sources : ApplicationSource [ ] ;
@@ -119,146 +170,153 @@ const ApplicationDetailsTab: React.FC<ApplicationDetailsTabProps> = ({ obj }) =>
119170 < Title headingLevel = "h2" className = "co-section-heading" >
120171 { t ( 'Application details' ) }
121172 </ Title >
122- < div className = "row" >
123- < div className = "col-sm-6" >
124- < BaseDetailsSummary
125- obj = { obj }
126- model = { ApplicationModel }
127- nameLink = {
128- < >
129- < ArgoCDLink
130- href = {
131- argoServer . protocol +
132- '://' +
133- argoServer . host +
134- '/applications/' +
135- obj ?. metadata ?. namespace +
136- '/' +
137- obj ?. metadata ?. name
138- }
139- />
140- </ >
141- }
142- />
143- </ div >
173+ < Flex
174+ justifyContent = { { default : 'justifyContentSpaceEvenly' } }
175+ direction = { { default : 'column' , lg : 'row' } }
176+ >
177+ < Flex flex = { { default : 'flex_2' } } >
178+ < FlexItem >
179+ < BaseDetailsSummary
180+ obj = { obj }
181+ model = { ApplicationModel }
182+ nameLink = {
183+ < >
184+ < ArgoCDLink
185+ href = {
186+ argoServer . protocol +
187+ '://' +
188+ argoServer . host +
189+ '/applications/' +
190+ obj ?. metadata ?. namespace +
191+ '/' +
192+ obj ?. metadata ?. name
193+ }
194+ />
195+ </ >
196+ }
197+ />
198+ </ FlexItem >
199+ </ Flex >
200+ < Flex flex = { { default : 'flex_2' } } direction = { { default : 'column' } } >
201+ < FlexItem >
202+ < DescriptionList className = "pf-c-description-list" >
203+ < DetailsDescriptionGroup
204+ title = { t ( 'Health Status' ) }
205+ help = { t ( 'Health status represents the overall health of the application.' ) }
206+ >
207+ < HealthStatus status = { obj . status ?. health ?. status || '' } />
208+ </ DetailsDescriptionGroup >
144209
145- < div className = "col-sm-6" >
146- < DescriptionList className = "pf-c-description-list" >
147- < DetailsDescriptionGroup
148- title = { t ( 'Health Status' ) }
149- help = { t ( 'Health status represents the overall health of the application.' ) }
150- >
151- < HealthStatus status = { obj . status ?. health ?. status || '' } />
152- </ DetailsDescriptionGroup >
153-
154- < DetailsDescriptionGroup
155- title = { t ( 'Current Sync Status' ) }
156- help = { t (
157- 'Sync status represents the current synchronized state for the application.' ,
158- ) }
159- >
160- < Flex >
161- < FlexItem >
162- < SyncStatus status = { obj . status ?. sync ?. status || '' } />
163- </ FlexItem >
164- < FlexItem >
165- < PfLabel >
166- < Revision
167- revision = { revisions [ 0 ] || '' }
168- repoURL = { sources [ 0 ] . repoURL }
169- helm = { obj . status ?. sourceType == 'Helm' && sources [ 0 ] . chart ? true : false }
170- revisionExtra = {
171- revisions . length > 1 && ' and ' + ( revisions . length - 1 ) + ' more'
172- }
173- />
174- </ PfLabel >
175- </ FlexItem >
176- </ Flex >
177- </ DetailsDescriptionGroup >
178-
179- < DetailsDescriptionGroup
180- title = { t ( 'Last Sync Status' ) }
181- help = { t ( 'The result of the last sync status.' ) }
182- >
183- < Flex >
184- { obj ?. status ?. operationState && (
210+ < DetailsDescriptionGroup
211+ title = { t ( 'Current Sync Status' ) }
212+ help = { t (
213+ 'Sync status represents the current synchronized state for the application.' ,
214+ ) }
215+ >
216+ < Flex >
185217 < FlexItem >
186- < OperationState app = { obj } />
218+ < SyncStatus status = { obj . status ?. sync ?. status || '' } />
187219 </ FlexItem >
188- ) }
189- { obj ?. status ?. conditions && (
190220 < FlexItem >
191- < ConditionsPopover conditions = { obj . status ?. conditions } />
221+ < PfLabel >
222+ < Revision
223+ revision = { revisions [ 0 ] || '' }
224+ repoURL = { sources [ 0 ] . repoURL }
225+ helm = { obj . status ?. sourceType == 'Helm' && sources [ 0 ] . chart ? true : false }
226+ revisionExtra = {
227+ revisions . length > 1 && ' and ' + ( revisions . length - 1 ) + ' more'
228+ }
229+ />
230+ </ PfLabel >
192231 </ FlexItem >
193- ) }
194- </ Flex >
195- </ DetailsDescriptionGroup >
232+ </ Flex >
233+ </ DetailsDescriptionGroup >
196234
197- < DetailsDescriptionGroup
198- title = { t ( 'Target Revision' ) }
199- help = { t ( 'The specified revision for the Application.' ) }
200- >
201- { sources [ 0 ] . targetRevision ? sources [ 0 ] . targetRevision : 'HEAD' }
202- </ DetailsDescriptionGroup >
235+ < DetailsDescriptionGroup
236+ title = { t ( 'Last Sync Status' ) }
237+ help = { t ( 'The result of the last sync status.' ) }
238+ >
239+ < Flex >
240+ { obj ?. status ?. operationState && (
241+ < FlexItem >
242+ < OperationState app = { obj } />
243+ </ FlexItem >
244+ ) }
245+ { obj ?. status ?. conditions && (
246+ < FlexItem >
247+ < ConditionsPopover conditions = { obj . status ?. conditions } />
248+ </ FlexItem >
249+ ) }
250+ </ Flex >
251+ </ DetailsDescriptionGroup >
203252
204- < DetailsDescriptionGroup
205- title = { t ( 'Project' ) }
206- help = { t ( 'The Argo CD Project that this application belongs to.' ) }
207- >
208- { /* TODO - Update to handle App in Any Namespace when controller namespace is in status */ }
209- < ResourceLink
210- namespace = { obj ?. metadata ?. namespace }
211- groupVersionKind = { {
212- group : 'argoproj.io' ,
213- version : 'v1alpha1' ,
214- kind : 'AppProject' ,
215- } }
216- name = { obj ?. spec ?. project }
217- />
218- </ DetailsDescriptionGroup >
253+ < DetailsDescriptionGroup
254+ title = { t ( 'Target Revision' ) }
255+ help = { t ( 'The specified revision for the Application.' ) }
256+ >
257+ { sources [ 0 ] . targetRevision ? sources [ 0 ] . targetRevision : 'HEAD' }
258+ </ DetailsDescriptionGroup >
219259
220- < DetailsDescriptionGroup
221- title = { t ( 'Destination' ) }
222- help = { t ( 'The cluster and namespace where the application is targeted' ) }
223- >
224- { getFriendlyClusterName ( obj ?. spec ?. destination . server ) } /
225- { obj ?. spec ?. destination . namespace }
226- </ DetailsDescriptionGroup >
227-
228- < DetailsDescriptionGroup
229- title = { t ( 'Sync Policy' ) }
230- help = { t ( 'Provides options to determine application synchronization behavior' ) }
231- >
232- < ToggleGroup isCompact areAllGroupsDisabled = { ! canPatch || ! canUpdate } >
233- < ToggleGroupItem
234- text = { t ( 'Automated' ) }
235- buttonId = "automated"
236- onChange = { onChangeAutomated }
237- isSelected = { obj ?. spec ?. syncPolicy ?. automated ? true : false }
238- />
239- < ToggleGroupItem
240- text = { t ( 'Prune' ) }
241- buttonId = "prune"
242- onChange = { onChangeAutomated }
243- isSelected = {
244- obj ?. spec ?. syncPolicy ?. automated && obj ?. spec ?. syncPolicy ?. automated . prune
245- }
246- isDisabled = { obj ?. spec ?. syncPolicy ?. automated ? false : true }
247- />
248- < ToggleGroupItem
249- text = { t ( 'Self Heal' ) }
250- buttonId = "self-heal"
251- onChange = { onChangeAutomated }
252- isSelected = {
253- obj ?. spec ?. syncPolicy ?. automated && obj ?. spec ?. syncPolicy ?. automated . selfHeal
254- }
255- isDisabled = { obj ?. spec ?. syncPolicy ?. automated ? false : true }
260+ < DetailsDescriptionGroup
261+ title = { t ( 'Project' ) }
262+ help = { t ( 'The Argo CD Project that this application belongs to.' ) }
263+ >
264+ { /* TODO - Update to handle App in Any Namespace when controller namespace is in status */ }
265+ < ResourceLink
266+ namespace = { obj ?. metadata ?. namespace }
267+ groupVersionKind = { {
268+ group : 'argoproj.io' ,
269+ version : 'v1alpha1' ,
270+ kind : 'AppProject' ,
271+ } }
272+ name = { obj ?. spec ?. project }
256273 />
257- </ ToggleGroup >
258- </ DetailsDescriptionGroup >
259- </ DescriptionList >
260- </ div >
261- </ div >
274+ </ DetailsDescriptionGroup >
275+
276+ < DetailsDescriptionGroup
277+ title = { t ( 'Destination' ) }
278+ help = { t ( 'The cluster and namespace where the application is targeted' ) }
279+ >
280+ { getFriendlyClusterName ( obj ?. spec ?. destination . server ) } /
281+ { obj ?. spec ?. destination . namespace }
282+ </ DetailsDescriptionGroup >
283+
284+ < DetailsDescriptionGroup
285+ title = { t ( 'Sync Policy' ) }
286+ help = { t ( 'Provides options to determine application synchronization behavior' ) }
287+ >
288+ < ToggleGroup isCompact areAllGroupsDisabled = { ! canPatch || ! canUpdate } >
289+ < ToggleGroupItem
290+ text = { t ( 'Automated' ) }
291+ buttonId = "automated"
292+ onChange = { onChangeAutomated }
293+ isSelected = { obj ?. spec ?. syncPolicy ?. automated ? true : false }
294+ />
295+ < ToggleGroupItem
296+ text = { t ( 'Prune' ) }
297+ buttonId = "prune"
298+ onChange = { onChangeAutomated }
299+ isSelected = {
300+ obj ?. spec ?. syncPolicy ?. automated && obj ?. spec ?. syncPolicy ?. automated . prune
301+ }
302+ isDisabled = { obj ?. spec ?. syncPolicy ?. automated ? false : true }
303+ />
304+ < ToggleGroupItem
305+ text = { t ( 'Self Heal' ) }
306+ buttonId = "self-heal"
307+ onChange = { onChangeAutomated }
308+ isSelected = {
309+ obj ?. spec ?. syncPolicy ?. automated &&
310+ obj ?. spec ?. syncPolicy ?. automated . selfHeal
311+ }
312+ isDisabled = { obj ?. spec ?. syncPolicy ?. automated ? false : true }
313+ />
314+ </ ToggleGroup >
315+ </ DetailsDescriptionGroup >
316+ </ DescriptionList >
317+ </ FlexItem >
318+ </ Flex >
319+ </ Flex >
262320 </ PageSection >
263321 </ div >
264322 ) ;
0 commit comments