16
16
17
17
import React , { useEffect , useRef , useState , useMemo } from 'react'
18
18
import { NavLink , Redirect , Route , Switch , useParams , useRouteMatch , useLocation } from 'react-router-dom'
19
- import { showError , Checkbox , CHECKBOX_VALUE , OptionType } from '@devtron-labs/devtron-fe-common-lib'
19
+ import {
20
+ showError ,
21
+ Checkbox ,
22
+ CHECKBOX_VALUE ,
23
+ OptionType ,
24
+ DeploymentAppTypes ,
25
+ } from '@devtron-labs/devtron-fe-common-lib'
26
+ import { ReactComponent as ICArrowsLeftRight } from '@Icons/ic-arrows-left-right.svg'
27
+ import { ReactComponent as ICPencil } from '@Icons/ic-pencil.svg'
28
+ import { ReactComponent as ICCheck } from '@Icons/ic-check.svg'
20
29
import EventsComponent from './NodeDetailTabs/Events.component'
21
30
import LogsComponent from './NodeDetailTabs/Logs.component'
22
31
import ManifestComponent from './NodeDetailTabs/Manifest.component'
23
32
import TerminalComponent from './NodeDetailTabs/Terminal.component'
24
33
import SummaryComponent from './NodeDetailTabs/Summary.component'
25
34
import { NodeDetailTab , ParamsType } from './nodeDetail.type'
26
- import { ManifestViewRefType , NodeDetailPropsType , NodeType , Options , OptionsBase } from '../../appDetails.type'
35
+ import {
36
+ AppType ,
37
+ ManifestCodeEditorMode ,
38
+ ManifestViewRefType ,
39
+ NodeDetailPropsType ,
40
+ NodeType ,
41
+ Options ,
42
+ OptionsBase ,
43
+ } from '../../appDetails.type'
27
44
import AppDetailsStore from '../../appDetails.store'
28
45
import { useSharedState } from '../../../utils/useSharedState'
29
46
import IndexStore from '../../index.store'
@@ -74,6 +91,8 @@ const NodeDetailComponent = ({
74
91
const podMetaData = ! isResourceBrowserView && IndexStore . getMetaDataForPod ( params . podName )
75
92
const { path, url } = useRouteMatch ( )
76
93
const [ showDeleteDialog , setShowDeleteDialog ] = useState ( false )
94
+ const [ showManifestCompareView , setShowManifestCompareView ] = useState ( false )
95
+ const [ manifestCodeEditorMode , setManifestCodeEditorMode ] = useState < ManifestCodeEditorMode > ( null )
77
96
78
97
const toggleManagedFields = ( managedFieldsExist : boolean ) => {
79
98
if ( selectedTabName === NodeDetailTab . MANIFEST && managedFieldsExist ) {
@@ -98,6 +117,20 @@ const NodeDetailComponent = ({
98
117
containers : [ ] ,
99
118
}
100
119
120
+ const currentResource = isResourceBrowserView
121
+ ? selectedResource
122
+ : appDetails . resourceTree . nodes . filter (
123
+ ( data ) => data . name === params . podName && data . kind . toLowerCase ( ) === params . nodeType ,
124
+ ) [ 0 ]
125
+
126
+ const showDesiredAndCompareManifest =
127
+ ! isResourceBrowserView &&
128
+ appDetails . appType === AppType . EXTERNAL_HELM_CHART &&
129
+ ! currentResource ?. [ 'parentRefs' ] ?. length
130
+
131
+ const isResourceMissing =
132
+ appDetails . appType === AppType . EXTERNAL_HELM_CHART && currentResource ?. [ 'health' ] ?. status === 'Missing'
133
+
101
134
const [ containers , setContainers ] = useState < Options [ ] > (
102
135
( isResourceBrowserView ? selectedResource ?. containers ?? [ ] : getContainersData ( podMetaData ) ) as Options [ ] ,
103
136
)
@@ -117,13 +150,12 @@ const NodeDetailComponent = ({
117
150
manifest : '' ,
118
151
activeManifestEditorData : '' ,
119
152
modifiedManifest : '' ,
120
- isEditmode : false ,
121
- activeTab : 'Live manifest' , // NOTE: default activeTab
122
153
} ,
123
154
id : '' ,
124
155
} )
125
156
126
157
useEffect ( ( ) => setManagedFields ( ( prev ) => prev && selectedTabName === NodeDetailTab . MANIFEST ) , [ selectedTabName ] )
158
+
127
159
useEffect ( ( ) => {
128
160
if ( location . pathname . endsWith ( '/terminal' ) && params . nodeType === Nodes . Pod . toLowerCase ( ) ) {
129
161
setStartTerminal ( true )
@@ -301,6 +333,14 @@ const NodeDetailComponent = ({
301
333
return Object . values ( params ) . join ( '/' )
302
334
}
303
335
336
+ const handleManifestApplyChanges = ( ) => setManifestCodeEditorMode ( ManifestCodeEditorMode . APPLY_CHANGES )
337
+
338
+ const handleManifestCancel = ( ) => setManifestCodeEditorMode ( ManifestCodeEditorMode . CANCEL )
339
+
340
+ const handleManifestEdit = ( ) => setManifestCodeEditorMode ( ManifestCodeEditorMode . EDIT )
341
+
342
+ const handleManifestCompareWithDesired = ( ) => setShowManifestCompareView ( true )
343
+
304
344
const renderPodTerminal = ( ) : JSX . Element => {
305
345
if ( ! startTerminal ) {
306
346
return null
@@ -324,6 +364,69 @@ const NodeDetailComponent = ({
324
364
)
325
365
}
326
366
367
+ const renderManifestTabHeader = ( ) => (
368
+ < >
369
+ { ( isExternalApp ||
370
+ isResourceBrowserView ||
371
+ ( appDetails . deploymentAppType === DeploymentAppTypes . GITOPS &&
372
+ appDetails . deploymentAppDeleteRequest ) ) &&
373
+ manifestCodeEditorMode &&
374
+ ! showManifestCompareView &&
375
+ ! isResourceMissing && (
376
+ < >
377
+ < div className = "ml-4 mr-12 tab-cell-border" />
378
+ { manifestCodeEditorMode === ManifestCodeEditorMode . EDIT ? (
379
+ < div className = "flex dc__gap-12" >
380
+ < button
381
+ type = "button"
382
+ className = "dc__unset-button-styles cb-5 fs-12 lh-1-5 fw-6 flex dc__gap-4"
383
+ onClick = { handleManifestApplyChanges }
384
+ >
385
+ < >
386
+ < ICCheck className = "icon-dim-16 scb-5" />
387
+ < span > Apply changes</ span >
388
+ </ >
389
+ </ button >
390
+ < button
391
+ type = "button"
392
+ className = "dc__unset-button-styles fs-12 lh-1-5 fw-6 flex cn-6"
393
+ onClick = { handleManifestCancel }
394
+ >
395
+ Cancel
396
+ </ button >
397
+ </ div >
398
+ ) : (
399
+ < button
400
+ type = "button"
401
+ className = "dc__unset-button-styles cb-5 fs-12 lh-1-5 fw-6 flex dc__gap-4"
402
+ onClick = { handleManifestEdit }
403
+ >
404
+ < >
405
+ < ICPencil className = "icon-dim-16 scb-5" />
406
+ < span > Edit live manifest</ span >
407
+ </ >
408
+ </ button >
409
+ ) }
410
+ </ >
411
+ ) }
412
+ { manifestCodeEditorMode === ManifestCodeEditorMode . READ &&
413
+ ! showManifestCompareView &&
414
+ ( showDesiredAndCompareManifest || isResourceMissing ) && (
415
+ < >
416
+ < div className = "ml-12 mr-12 tab-cell-border" />
417
+ < button
418
+ type = "button"
419
+ className = "dc__unset-button-styles cb-5 fs-12 lh-1-5 fw-6 flex dc__gap-4"
420
+ onClick = { handleManifestCompareWithDesired }
421
+ >
422
+ < ICArrowsLeftRight className = "icon-dim-16 scb-5" />
423
+ < span > Compare with desired</ span >
424
+ </ button >
425
+ </ >
426
+ ) }
427
+ </ >
428
+ )
429
+
327
430
return (
328
431
< >
329
432
< div
@@ -388,6 +491,7 @@ const NodeDetailComponent = ({
388
491
</ div >
389
492
</ >
390
493
) }
494
+ { selectedTabName === NodeDetailTab . MANIFEST && renderManifestTabHeader ( ) }
391
495
</ div >
392
496
{ isResourceBrowserView &&
393
497
! hideDeleteButton && ( // hide delete button if resource is deleted or user is not authorized
@@ -414,7 +518,10 @@ const NodeDetailComponent = ({
414
518
selectedResource = { selectedResource }
415
519
manifestViewRef = { manifestViewRef }
416
520
getComponentKey = { getComponentKeyFromParams }
417
- isExternalApp = { isExternalApp }
521
+ showManifestCompareView = { showManifestCompareView }
522
+ setShowManifestCompareView = { setShowManifestCompareView }
523
+ manifestCodeEditorMode = { manifestCodeEditorMode }
524
+ setManifestCodeEditorMode = { setManifestCodeEditorMode }
418
525
/>
419
526
</ Route >
420
527
< Route path = { `${ path } /${ NodeDetailTab . EVENTS } ` } >
0 commit comments