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