Skip to content

Commit 2f58a86

Browse files
committed
feat: enhance BuildImageModal and related components with improved error handling, state management, and UI updates
1 parent 338c144 commit 2f58a86

File tree

13 files changed

+159
-142
lines changed

13 files changed

+159
-142
lines changed

src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@
5050
border-radius: 4px;
5151
border: 1px solid var(--N200);
5252
margin-bottom: 6px;
53+
background-color: var(--bg-primary);
5354

5455
&.material-selected {
5556
box-shadow: none;
5657
border-color: var(--B500);
57-
background-color: var(--bg-primary);
5858
}
5959
}
6060
}

src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ const EnvTriggerView = ({ filteredAppIds, isVirtualEnv }: AppGroupDetailDefaultT
263263
})
264264
}
265265

266-
const getWorkflowsData = async (): Promise<void> => {
266+
const getWorkflowsData = async (): Promise<WorkflowType[]> => {
267267
try {
268268
const { workflows: _workflows, filteredCIPipelines } = await getWorkflows(envId, filteredAppIds)
269269
if (processDeploymentWindowStateAppGroup && _workflows.length) {
@@ -276,6 +276,8 @@ const EnvTriggerView = ({ filteredAppIds, isVirtualEnv }: AppGroupDetailDefaultT
276276
setPageViewType(ViewType.FORM)
277277
getWorkflowStatusData(_workflows)
278278
processFilteredData(_workflows)
279+
280+
return _workflows
279281
} catch (error) {
280282
showError(error)
281283
setErrorCode(error.code)

src/components/app/details/triggerView/BranchRegexModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ const BranchRegexModal = ({
134134
isRegexMaterial: true,
135135
isTemplateView: false,
136136
})
137+
await handleReload()
137138
onCloseBranchRegexModal()
138-
handleReload()
139139
} catch (error) {
140140
showError(error)
141141
} finally {

src/components/app/details/triggerView/BuildImageModal/BuildImageModal.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
APIResponseHandler,
1414
Button,
1515
Checkbox,
16-
CIPipelineNodeType,
16+
CommonNodeAttr,
1717
ComponentSizeType,
1818
ConsequenceAction,
1919
DEFAULT_ROUTE_PROMPT_MESSAGE,
@@ -80,12 +80,12 @@ const BuildImageModal = ({
8080
const [isBuildTriggerLoading, setIsBuildTriggerLoading] = useState<boolean>(false)
8181
const [showWebhookModal, setShowWebhookModal] = useState<boolean>(false)
8282

83+
const getIsItCurrentCINode = (node: CommonNodeAttr) => node.type === WorkflowNodeType.CI && +node.id === +ciNodeId
84+
8385
// Workflows will be present since this modal is only opened when workflows are loaded
84-
const selectedWorkflow = workflows.find((workflow) =>
85-
workflow.nodes.some((node) => node.type === WorkflowNodeType.CI && +node.id === +ciNodeId),
86-
)
86+
const selectedWorkflow = workflows.find((workflow) => workflow.nodes.some(getIsItCurrentCINode))
8787
const workflowId = selectedWorkflow?.id
88-
const ciNode = selectedWorkflow?.nodes.find((node) => node.type === CIPipelineNodeType.CI && node.id === ciNodeId)
88+
const ciNode = selectedWorkflow?.nodes.find(getIsItCurrentCINode)
8989
const appId = appIdProp || selectedWorkflow?.appId
9090
const filteredCIPipelines = filteredCIPipelinesProp || filteredCIPipelineMap?.get(appId) || []
9191

@@ -98,7 +98,7 @@ const BuildImageModal = ({
9898
const [areRuntimeParamsLoading, runtimeParams, runtimeParamsError, reloadRuntimeParams, setRuntimeParams] =
9999
useAsync<GitInfoMaterialProps['runtimeParams']>(
100100
() => getRuntimeParams(ciNodeId, true),
101-
[ciNodeId, ciNode?.isTriggerBlocked],
101+
[ciNodeId],
102102
!!ciNodeId && !ciNode?.isTriggerBlocked && !!getRuntimeParams,
103103
)
104104

@@ -111,15 +111,15 @@ const BuildImageModal = ({
111111
isCINodePresent: !!ciNode,
112112
selectedWorkflow,
113113
}),
114-
[ciNodeId, ciNode?.isTriggerBlocked],
114+
[ciNodeId],
115115
!!ciNodeId && !ciNode?.isTriggerBlocked,
116116
)
117117

118118
const materialList = _materialList || []
119119

120120
const [isLoadingBlobStorageModule, blobStorageModuleRes, , reloadBlobStorageModule] = useAsync(
121121
() => getModuleConfigured(ModuleNameMap.BLOB_STORAGE),
122-
[ciNode?.isTriggerBlocked],
122+
[],
123123
!ciNode?.isTriggerBlocked,
124124
)
125125

@@ -135,15 +135,14 @@ const BuildImageModal = ({
135135
return () => materialListAbortControllerRef.current.abort()
136136
}, [])
137137

138-
const handleReload = () => {
139-
reloadMaterialList()
140-
reloadRuntimeParams()
141-
reloadBlobStorageModule()
138+
const handleReload = async () => {
139+
await Promise.allSettled([reloadMaterialList(), reloadRuntimeParams(), reloadBlobStorageModule()])
142140
}
143141

144-
const handleReloadWithWorkflows = () => {
145-
reloadWorkflows()
146-
handleReload()
142+
const handleReloadWithWorkflows = async () => {
143+
await reloadWorkflows()
144+
// Not passing new workflows since it is only used for ciConfiguredGitMaterialId, and name which should not change
145+
await handleReload()
147146
}
148147

149148
const toggleInvalidateCache = () => {
@@ -158,13 +157,15 @@ const BuildImageModal = ({
158157
return {
159158
code: materialListError.code,
160159
reload: handleReload,
160+
on404Redirect: handleClose,
161161
}
162162
}
163163

164164
if (runtimeParamsError) {
165165
return {
166166
code: runtimeParamsError.code,
167167
reload: handleReload,
168+
on404Redirect: handleClose,
168169
}
169170
}
170171

@@ -217,11 +218,10 @@ const BuildImageModal = ({
217218

218219
try {
219220
await triggerBuild({ payload, redirectToCIPipeline })
221+
setIsBuildTriggerLoading(false)
220222
reloadWorkflowStatus()
221223
handleClose()
222224
} catch {
223-
// Do nothing
224-
} finally {
225225
setIsBuildTriggerLoading(false)
226226
}
227227
}

src/components/app/details/triggerView/BuildImageModal/BulkBuildImageModal.tsx

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
useAsync,
2525
usePrompt,
2626
WorkflowNodeType,
27+
WorkflowType,
2728
} from '@devtron-labs/devtron-fe-common-lib'
2829

2930
import { ReactComponent as MechanicalOperation } from '@Images/ic-mechanical-operation.svg'
@@ -84,16 +85,21 @@ const BulkBuildImageModal = ({
8485

8586
const initialDataAbortControllerRef = useRef<AbortController>(new AbortController())
8687

87-
const selectedWorkflows = workflows.filter(
88-
(workflow) =>
89-
workflow.isSelected &&
90-
workflow.nodes.some((node) => node.type === WorkflowNodeType.CI || node.type === WorkflowNodeType.WEBHOOK),
91-
)
92-
9388
const [numberOfAppsLoading, setNumberOfAppsLoading] = useState<number>(0)
9489

9590
// Returns map of appId to BulkCIDetailType
96-
const getInitialAppList = async (appId?: number): Promise<Record<number, BulkCIDetailType>> => {
91+
const getInitialAppList = async (
92+
appId?: number,
93+
newWorkflows: WorkflowType[] = workflows,
94+
): Promise<Record<number, BulkCIDetailType>> => {
95+
const selectedWorkflows = newWorkflows.filter(
96+
(workflow) =>
97+
workflow.isSelected &&
98+
workflow.nodes.some(
99+
(node) => node.type === WorkflowNodeType.CI || node.type === WorkflowNodeType.WEBHOOK,
100+
),
101+
)
102+
97103
const validWorkflows = selectedWorkflows.filter((workflow) => !appId || workflow.appId === appId)
98104

99105
const { ciMaterialPromiseList, runtimeParamsPromiseList } = getBulkCIDataPromiseGetterList(
@@ -134,13 +140,13 @@ const BulkBuildImageModal = ({
134140

135141
const selectedAppId = selectedAppIdState ?? sortedAppList[0]?.appId ?? null
136142

137-
const reloadSelectedAppMaterialList = async () => {
143+
const reloadSelectedAppMaterialList = async (newWorkflows: WorkflowType[] = workflows) => {
138144
try {
139145
setIsLoadingSingleAppInfoMap(true)
140146
initialDataAbortControllerRef.current.abort()
141147
initialDataAbortControllerRef.current = new AbortController()
142148
// Will also handle error state
143-
const currentAppInfo = await getInitialAppList(selectedAppId)
149+
const currentAppInfo = await getInitialAppList(selectedAppId, newWorkflows)
144150
setAppInfoMapRes((prevAppInfoMapRes) => {
145151
const updatedAppInfoMap = structuredClone(prevAppInfoMapRes)
146152
updatedAppInfoMap[selectedAppId] = currentAppInfo[selectedAppId]
@@ -155,8 +161,8 @@ const BulkBuildImageModal = ({
155161

156162
const handleReloadSelectedMaterialWithWorkflows = async () => {
157163
try {
158-
await reloadSelectedAppMaterialList()
159-
reloadWorkflows()
164+
const newWorkflows = await reloadWorkflows()
165+
await reloadSelectedAppMaterialList(newWorkflows)
160166
} catch (error) {
161167
showError(error)
162168
}
@@ -221,7 +227,6 @@ const BulkBuildImageModal = ({
221227
}
222228

223229
handleAnalyticsEvent(ENV_TRIGGER_VIEW_GA_EVENTS.BulkCITriggered)
224-
setIsBuildTriggerLoading(true)
225230

226231
const {
227232
promiseList,
@@ -234,10 +239,11 @@ const BulkBuildImageModal = ({
234239
variant: ToastVariantType.error,
235240
description: 'No valid CI pipeline found',
236241
})
237-
setIsBuildTriggerLoading(false)
238242
return
239243
}
240244

245+
setIsBuildTriggerLoading(true)
246+
setNumberOfAppsLoading(promiseList.length + newResourceList.length)
241247
try {
242248
const responseArray = await ApiQueuingWithBatch(promiseList)
243249

@@ -271,6 +277,7 @@ const BulkBuildImageModal = ({
271277
// Do nothing
272278
} finally {
273279
setIsBuildTriggerLoading(false)
280+
setNumberOfAppsLoading(0)
274281
}
275282
}
276283

@@ -454,20 +461,18 @@ const BulkBuildImageModal = ({
454461
onClick={stopPropagation}
455462
>
456463
<div className="flexbox-col dc__overflow-auto flex-grow-1">
464+
<BuildImageHeader
465+
showWebhookModal={showWebhookModal}
466+
handleWebhookModalBack={handleWebhookModalBack}
467+
pipelineName={appInfoMap?.[selectedAppId]?.node?.title}
468+
handleClose={handleClose}
469+
isBulkTrigger
470+
/>
471+
457472
{responseList.length > 0 ? (
458473
<TriggerResponseModalBody responseList={responseList} isLoading={isBuildTriggerLoading} />
459474
) : (
460-
<>
461-
<BuildImageHeader
462-
showWebhookModal={showWebhookModal}
463-
handleWebhookModalBack={handleWebhookModalBack}
464-
pipelineName={appInfoMap?.[selectedAppId]?.node?.title}
465-
handleClose={handleClose}
466-
isBulkTrigger
467-
/>
468-
469-
<div className="flex-grow-1 dc__overflow-auto w-100">{renderContent()}</div>
470-
</>
475+
<div className="flex-grow-1 dc__overflow-auto w-100">{renderContent()}</div>
471476
)}
472477
</div>
473478

src/components/app/details/triggerView/BuildImageModal/GitInfoMaterial.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,7 @@ const GitInfoMaterial = ({
502502
alwaysShowTippyOnHover
503503
interactive
504504
>
505-
<span className="dc__ellipsis-right fs-13 lh-20 fw-6 cn-9">
506-
{selectedMaterial.value}
507-
</span>
505+
<span className="dc__truncate fs-13 lh-20 fw-6 cn-9">{selectedMaterial.value}</span>
508506
</Tooltip>
509507
</span>
510508
</ConditionalWrap>
@@ -610,7 +608,7 @@ const GitInfoMaterial = ({
610608
imgSrc={externalCiImg}
611609
title={`${selectedApp.name} ${BULK_CI_MESSAGING.webhookCI.title}`}
612610
subTitle={BULK_CI_MESSAGING.webhookCI.subTitle}
613-
rootClassName=""
611+
rootClassName="bg__tertiary"
614612
/>
615613
)
616614
}
@@ -623,7 +621,7 @@ const GitInfoMaterial = ({
623621
subTitle={BULK_CI_MESSAGING.emptyLinkedCI.subTitle}
624622
link={`${URLS.APP}/${selectedApp.node.parentAppId}/${URLS.APP_CI_DETAILS}/${selectedApp.node.parentCiPipeline}`}
625623
linkText={BULK_CI_MESSAGING.emptyLinkedCI.linkText}
626-
rootClassName=""
624+
rootClassName="bg__tertiary"
627625
/>
628626
)
629627
}
@@ -634,6 +632,7 @@ const GitInfoMaterial = ({
634632
title={`${BULK_CI_MESSAGING.linkedCD.title(selectedApp.node.title)}`}
635633
subTitle={BULK_CI_MESSAGING.linkedCD.subTitle(selectedApp.node.title)}
636634
image={linkedCDBuildCIImg}
635+
classname="bg__tertiary"
637636
/>
638637
)
639638
}

src/components/app/details/triggerView/BuildImageModal/TriggerBuildSidebar.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ const TriggerBuildSidebar = ({
9191
<div
9292
role="button"
9393
tabIndex={0}
94-
className={`pt-12 dc__tab-focus ${appDetails.appId === appId ? 'pb-12' : ''}`}
94+
className={`pt-12 dc__tab-focus ${appDetails.appId === appId && getCanNodeHaveMaterial(appDetails.node) ? 'pb-12' : ''}`}
9595
onClick={getHandleAppChange(appDetails.appId)}
9696
>
9797
<span className="dc__word-break fw-6 fs-13 cn-9">{appDetails.name}</span>
@@ -104,7 +104,9 @@ const TriggerBuildSidebar = ({
104104
{appDetails.appId !== appId && !!getErrorMessageFromAppDetails(appDetails) && (
105105
<span className="flexbox cr-5 fw-4 fs-12 dc__gap-4">
106106
<Icon name="ic-error" size={20} color={null} />
107-
<span className="dc__block dc__word-break lh-20">{getErrorMessageFromAppDetails(appDetails)}</span>
107+
<span className="dc__block dc__word-break lh-20 dc__truncate--clamp-6">
108+
{getErrorMessageFromAppDetails(appDetails)}
109+
</span>
108110
</span>
109111
)}
110112
{appDetails.node?.pluginBlockState &&
@@ -203,7 +205,7 @@ const TriggerBuildSidebar = ({
203205
}
204206

205207
return (
206-
<div className="material-list dc__overflow-hidden flexbox-col flex-grow-1 mh-0 border__primary--right">
208+
<div className="material-list dc__overflow-hidden flexbox-col flex-grow-1 mh-0">
207209
{RuntimeParamTabs ? (
208210
<div className="flex pt-12 pb-12 pl-16 pr-16 dc__gap-4 dc__border-bottom">
209211
<RuntimeParamTabs

src/components/app/details/triggerView/BuildImageModal/service.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@ import { NO_TASKS_CONFIGURED_ERROR } from '@Config/constantMessaging'
1313

1414
import { GetCIMaterialsProps, TriggerBuildProps } from './types'
1515

16-
export const triggerBuild = async ({ payload, redirectToCIPipeline }: TriggerBuildProps) => {
16+
export const triggerBuild = async ({ payload, redirectToCIPipeline, showToast = true }: TriggerBuildProps) => {
1717
try {
1818
await triggerCINode(payload)
19-
ToastManager.showToast({
20-
variant: ToastVariantType.success,
21-
description: 'Pipeline Triggered',
22-
})
19+
20+
if (showToast) {
21+
ToastManager.showToast({
22+
variant: ToastVariantType.success,
23+
description: 'Pipeline Triggered',
24+
})
25+
}
2326
} catch (errors) {
27+
if (!showToast) {
28+
throw errors
29+
}
30+
2431
if (errors.code === API_STATUS_CODES.PERMISSION_DENIED) {
2532
ToastManager.showToast({
2633
variant: ToastVariantType.notAuthorized,

src/components/app/details/triggerView/BuildImageModal/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export type GitInfoMaterialProps = Pick<BuildImageModalProps, 'isJobView'> & {
5656
runtimeParams: RuntimePluginVariables[]
5757
handleDisplayWebhookModal: () => void
5858
selectedCIPipeline: TriggerViewState['filteredCIPipelines'][number]
59-
handleReloadWithWorkflows: () => void
59+
handleReloadWithWorkflows: () => Promise<void>
6060
appId: number
6161
/**
6262
* Only required for isJobView
@@ -124,6 +124,10 @@ export interface TriggerBuildProps {
124124
* Only need in case of job
125125
*/
126126
redirectToCIPipeline?: () => void
127+
/**
128+
* @default true
129+
*/
130+
showToast?: boolean
127131
}
128132

129133
export interface GetCIMaterialsProps extends Pick<APIOptions, 'abortControllerRef'> {

src/components/app/details/triggerView/BuildImageModal/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ export const getTriggerCIPromiseListAndSkippedResources = (
307307
string
308308
>
309309

310-
return () => triggerBuild({ payload })
310+
return () => triggerBuild({ payload, showToast: false })
311311
})
312312

313313
return {

0 commit comments

Comments
 (0)