1
1
import { Dispatch , SetStateAction , SyntheticEvent , useEffect , useMemo , useRef , useState } from 'react'
2
+ import { Prompt } from 'react-router-dom'
2
3
3
4
import {
4
5
AnimatedDeployButton ,
@@ -7,11 +8,13 @@ import {
7
8
ButtonStyleType ,
8
9
CDMaterialResponseType ,
9
10
CDMaterialServiceEnum ,
11
+ DEFAULT_ROUTE_PROMPT_MESSAGE ,
10
12
DeploymentNodeType ,
11
13
DeploymentStrategyTypeWithDefault ,
12
14
Drawer ,
13
15
genericCDMaterialsService ,
14
16
GenericEmptyState ,
17
+ getIsApprovalPolicyConfigured ,
15
18
Icon ,
16
19
MODAL_TYPE ,
17
20
ModuleNameMap ,
@@ -27,6 +30,7 @@ import {
27
30
uploadCDPipelineFile ,
28
31
useAsync ,
29
32
useMainContext ,
33
+ usePrompt ,
30
34
} from '@devtron-labs/devtron-fe-common-lib'
31
35
32
36
import { ResponseRowType } from '@Components/ApplicationGroup/AppGroup.types'
@@ -97,6 +101,8 @@ const BulkDeployModal = ({
97
101
const isSecurityModuleInstalled = moduleInfoRes && moduleInfoRes ?. result ?. status === ModuleStatus . INSTALLED
98
102
const isCDStage = stageType === DeploymentNodeType . CD
99
103
104
+ usePrompt ( { shouldPrompt : isDeploymentLoading } )
105
+
100
106
useEffect (
101
107
( ) => ( ) => {
102
108
initialDataAbortControllerRef . current . abort ( )
@@ -139,7 +145,7 @@ const BulkDeployModal = ({
139
145
( node ) => node . type === stageType && + node . environmentId === + envId ,
140
146
)
141
147
142
- if ( ! currentStageNode ) {
148
+ if ( ! currentStageNode || baseBulkCDDetailMap [ workflow . appId ] . errorMessage ) {
143
149
return ( ) => null
144
150
}
145
151
@@ -167,7 +173,7 @@ const BulkDeployModal = ({
167
173
)
168
174
169
175
const appEnvList = validWorkflows
170
- . filter ( ( workflow ) => ! baseBulkCDDetailMap [ workflow . appId ] . warningMessage )
176
+ . filter ( ( workflow ) => ! baseBulkCDDetailMap [ workflow . appId ] . errorMessage )
171
177
. map ( ( workflow ) => ( {
172
178
appId : workflow . appId ,
173
179
envId : + envId ,
@@ -223,7 +229,8 @@ const BulkDeployModal = ({
223
229
} ,
224
230
} ) )
225
231
226
- const { deploymentWindowMetadata, materialError, warningMessage } = response [ selectedAppId ] || { }
232
+ const { deploymentWindowMetadata, materialError, errorMessage, tagsWarningMessage } =
233
+ response [ selectedAppId ] || { }
227
234
228
235
if ( materialError ) {
229
236
showError ( materialError )
@@ -243,7 +250,8 @@ const BulkDeployModal = ({
243
250
[ selectedAppId ] : {
244
251
...prev [ selectedAppId ] ,
245
252
materialResponse : response [ selectedAppId ] ?. materialResponse ,
246
- warningMessage,
253
+ errorMessage,
254
+ tagsWarningMessage,
247
255
materialError,
248
256
deploymentWindowMetadata,
249
257
deployViewState : {
@@ -385,8 +393,6 @@ const BulkDeployModal = ({
385
393
return
386
394
}
387
395
388
- setIsDeploymentLoading ( true )
389
-
390
396
const { cdTriggerPromiseFunctions, triggeredAppIds } = getTriggerCDPromiseMethods ( {
391
397
appInfoMap,
392
398
appsToRetry,
@@ -396,11 +402,14 @@ const BulkDeployModal = ({
396
402
bulkDeploymentStrategy,
397
403
} )
398
404
405
+ setIsDeploymentLoading ( true )
406
+ setNumberOfAppsLoading ( triggeredAppIds . length )
407
+
399
408
if ( ! triggeredAppIds . length ) {
400
409
setIsDeploymentLoading ( false )
401
410
ToastManager . showToast ( {
402
411
variant : ToastVariantType . error ,
403
- description : 'No applications selected for deployment' ,
412
+ description : 'No valid applications are present for deployment' ,
404
413
} )
405
414
return
406
415
}
@@ -418,6 +427,7 @@ const BulkDeployModal = ({
418
427
419
428
setResponseList ( newResponseList )
420
429
setIsDeploymentLoading ( false )
430
+ setNumberOfAppsLoading ( 0 )
421
431
}
422
432
423
433
const setDeployViewState : DeployImageContentProps [ 'setDeployViewState' ] = ( getUpdatedDeployViewState ) => {
@@ -469,11 +479,9 @@ const BulkDeployModal = ({
469
479
const { tagsWarning, updatedMaterials } = getUpdatedMaterialsForTagSelection (
470
480
tagOption . value ,
471
481
appDetails . materialResponse ?. materials || [ ] ,
472
- )
473
-
474
- const { tagsWarning : previousTagWarning } = getUpdatedMaterialsForTagSelection (
475
- selectedImageTagOption . value ,
476
- appDetails . materialResponse ?. materials || [ ] ,
482
+ ! getIsApprovalPolicyConfigured (
483
+ appDetails . materialResponse ?. deploymentApprovalInfo ?. approvalConfigData ,
484
+ ) || getIsExceptionUser ( appDetails . materialResponse ) ,
477
485
)
478
486
479
487
updatedAppInfoMap [ appDetails . appId ] = {
@@ -482,8 +490,7 @@ const BulkDeployModal = ({
482
490
...appDetails . materialResponse ,
483
491
materials : updatedMaterials ,
484
492
} ,
485
- warningMessage :
486
- previousTagWarning || ! appDetails . warningMessage ? tagsWarning : appDetails . warningMessage ,
493
+ tagsWarningMessage : tagsWarning ,
487
494
}
488
495
} )
489
496
return updatedAppInfoMap
@@ -502,16 +509,37 @@ const BulkDeployModal = ({
502
509
await onClickDeploy ( e )
503
510
}
504
511
505
- const isDeployButtonDisabled = useMemo (
506
- ( ) =>
512
+ const onImageSelection : DeployImageContentProps [ 'onImageSelection' ] = ( ) => {
513
+ // Will just clear the tagsWarningMessage for app others are handled in DeployImageContent
514
+ setAppInfoMap ( ( prev ) => ( {
515
+ ...prev ,
516
+ [ selectedAppId ] : {
517
+ ...prev [ selectedAppId ] ,
518
+ tagsWarningMessage : '' ,
519
+ } ,
520
+ } ) )
521
+ }
522
+
523
+ const isDeployButtonDisabled = useMemo ( ( ) => {
524
+ const atleastOneImageSelected = Object . values ( appInfoMap ) . some ( ( appDetails ) =>
525
+ ( appDetails . materialResponse ?. materials || [ ] ) . some ( ( material ) => material . isSelected ) ,
526
+ )
527
+
528
+ if ( ! atleastOneImageSelected ) {
529
+ return true
530
+ }
531
+
532
+ return (
507
533
isDeploymentLoading ||
508
534
isLoadingAppInfoMap ||
535
+ // Not disabling deploy button even if there is a warning message, since apps with warning will not have selected materials
536
+ // and hence will not be deployed
509
537
Object . values ( appInfoMap ) . some ( ( appDetails ) => {
510
- const { materialResponse , deployViewState , areMaterialsLoading } = appDetails
511
- return areMaterialsLoading || ! materialResponse || ! deployViewState
512
- } ) ,
513
- [ appInfoMap , isDeploymentLoading , isLoadingAppInfoMap ] ,
514
- )
538
+ const { areMaterialsLoading } = appDetails
539
+ return areMaterialsLoading
540
+ } )
541
+ )
542
+ } , [ appInfoMap , isDeploymentLoading , isLoadingAppInfoMap ] )
515
543
516
544
const canDeployWithoutApproval = useMemo (
517
545
( ) =>
@@ -632,6 +660,7 @@ const BulkDeployModal = ({
632
660
handleTagChange = { handleTagChange }
633
661
changeApp = { changeApp }
634
662
selectedTagName = { selectedImageTagOption . value }
663
+ onImageSelection = { onImageSelection }
635
664
/>
636
665
)
637
666
}
@@ -682,7 +711,7 @@ const BulkDeployModal = ({
682
711
onButtonClick = { onClickStartDeploy }
683
712
disabled = { isDeployButtonDisabled }
684
713
isLoading = { isDeploymentLoading }
685
- animateStartIcon = { isCDStage }
714
+ animateStartIcon = { isCDStage && ! isDeployButtonDisabled }
686
715
style = {
687
716
canDeployWithoutApproval || canImageApproverDeploy
688
717
? ButtonStyleType . warning
@@ -701,36 +730,42 @@ const BulkDeployModal = ({
701
730
}
702
731
703
732
return (
704
- < Drawer position = "right" width = "75%" minWidth = "1024px" maxWidth = "1200px" >
705
- < div
706
- className = "flexbox-col dc__content-space h-100 bg__modal--primary shadow__modal dc__overflow-auto bulk-ci-trigger-container"
707
- onClick = { stopPropagation }
708
- >
709
- < div className = "flexbox-col dc__overflow-auto flex-grow-1" >
710
- < DeployImageHeader
711
- handleClose = { handleClose }
712
- envName = { envName }
713
- stageType = { stageType }
714
- isRollbackTrigger = { false }
715
- isVirtualEnvironment = { isVirtualEnvironment }
716
- handleNavigateToMaterialListView = { showStrategyFeasibilityPage ? handleNavigateToListView : null }
717
- title = { showStrategyFeasibilityPage ? 'Deployment feasibility for' : '' }
718
- />
733
+ < >
734
+ < Drawer position = "right" width = "75%" minWidth = "1024px" maxWidth = "1200px" >
735
+ < div
736
+ className = "flexbox-col dc__content-space h-100 bg__modal--primary shadow__modal dc__overflow-auto bulk-ci-trigger-container"
737
+ onClick = { stopPropagation }
738
+ >
739
+ < div className = "flexbox-col dc__overflow-auto flex-grow-1" >
740
+ < DeployImageHeader
741
+ handleClose = { handleClose }
742
+ envName = { envName }
743
+ stageType = { stageType }
744
+ isRollbackTrigger = { false }
745
+ isVirtualEnvironment = { isVirtualEnvironment }
746
+ handleNavigateToMaterialListView = {
747
+ showStrategyFeasibilityPage ? handleNavigateToListView : null
748
+ }
749
+ title = { showStrategyFeasibilityPage ? 'Deployment feasibility for' : '' }
750
+ />
751
+
752
+ < div className = "flex-grow-1 dc__overflow-auto bg__tertiary w-100" > { renderContent ( ) } </ div >
753
+ </ div >
719
754
720
- < div className = "flex-grow-1 dc__overflow-auto bg__tertiary w-100" > { renderContent ( ) } </ div >
755
+ { isLoadingAppInfoMap || showStrategyFeasibilityPage ? null : renderFooter ( ) }
721
756
</ div >
722
757
723
- { isLoadingAppInfoMap || showStrategyFeasibilityPage ? null : renderFooter ( ) }
724
- </ div >
758
+ { showResistanceBox && (
759
+ < BulkDeployResistanceTippy
760
+ actionHandler = { onClickStartDeploy }
761
+ handleOnClose = { hideResistanceBox }
762
+ modalType = { MODAL_TYPE . DEPLOY }
763
+ />
764
+ ) }
765
+ </ Drawer >
725
766
726
- { showResistanceBox && (
727
- < BulkDeployResistanceTippy
728
- actionHandler = { onClickStartDeploy }
729
- handleOnClose = { hideResistanceBox }
730
- modalType = { MODAL_TYPE . DEPLOY }
731
- />
732
- ) }
733
- </ Drawer >
767
+ < Prompt when = { isDeploymentLoading } message = { DEFAULT_ROUTE_PROMPT_MESSAGE } />
768
+ </ >
734
769
)
735
770
}
736
771
0 commit comments