+ {MANIFEST_STATUS_HEADERS.map((headerKey, index) => (
// eslint-disable-next-line react/no-array-index-key
-
- {renderIcon(items.icon)}
-
{items.message}
+
+ {headerKey}
))}
- {statusBreakDownType.resourceDetails?.length ? (
-
-
- {MANIFEST_STATUS_HEADERS.map((headerKey, index) => (
- // eslint-disable-next-line react/no-array-index-key
-
- {headerKey}
-
- ))}
-
-
- {statusBreakDownType.resourceDetails.map((nodeDetails) => (
-
-
{nodeDetails.resourceKind}
-
{nodeDetails.resourceName}
-
- {nodeDetails.resourceStatus}
-
-
-
- ))}
+
+ {statusBreakDownType.resourceDetails.map((nodeDetails) => (
+
+
{nodeDetails.resourceKind}
+
{nodeDetails.resourceName}
+
+ {nodeDetails.resourceStatus}
+
+
-
- ) : null}
+ ))}
+
- )}
+ ) : null}
- ) : null
+ )
+ }
+
+ const isAccordion =
+ !!statusBreakDownType.subSteps?.length ||
+ (type === TIMELINE_STATUS.APP_HEALTH && APP_HEALTH_DROP_DOWN_LIST.includes(statusBreakDownType.icon)) ||
+ ((type === TIMELINE_STATUS.GIT_COMMIT || type === TIMELINE_STATUS.ARGOCD_SYNC) &&
+ statusBreakDownType.icon === 'failed')
+
+ const renderAccordionDetails = () => {
+ if (isCollapsed) {
+ return null
+ }
- const renderDetailChart = () =>
- !collapsed && (
+ return (
{statusBreakDownType.timelineStatus && (
{statusBreakDownType.timelineStatus}
+
{(deploymentDetailedData.deploymentStatus === DEPLOYMENT_STATUS.TIMED_OUT ||
deploymentDetailedData.deploymentStatus === DEPLOYMENT_STATUS.UNABLE_TO_FETCH) && (
-
- Try now
-
+
)}
)}
-
+
+ {type === TIMELINE_STATUS.APP_HEALTH ? (
+
+ ) : (
+ renderDetailedData()
+ )}
- {renderIcon(statusBreakDownType.icon)}
-
-
- {statusBreakDownType.displayText}
+
+ {renderDeploymentTimelineIcon(statusBreakDownType.icon)}
+
+
+ {statusBreakDownType.displayText}
+
+ {statusBreakDownType.displaySubText && (
+
+ {statusBreakDownType.displaySubText}
+
+ )}
- {statusBreakDownType.displaySubText && (
+
+ {statusBreakDownType.time !== '' && statusBreakDownType.icon !== 'inprogress' && (
- {statusBreakDownType.displaySubText}
+ {moment(statusBreakDownType.time, 'YYYY-MM-DDTHH:mm:ssZ').format(
+ DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT,
+ )}
)}
-
-
- {statusBreakDownType.time !== '' && statusBreakDownType.icon !== 'inprogress' && (
-
- {moment(statusBreakDownType.time, 'YYYY-MM-DDTHH:mm:ssZ').format(
- DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT,
- )}
-
- )}
- {((type === TIMELINE_STATUS.KUBECTL_APPLY && statusBreakDownType.kubeList?.length) ||
- (type === TIMELINE_STATUS.APP_HEALTH &&
- APP_HEALTH_DROP_DOWN_LIST.includes(statusBreakDownType.icon)) ||
- ((type === TIMELINE_STATUS.GIT_COMMIT || type === TIMELINE_STATUS.ARGOCD_SYNC) &&
- statusBreakDownType.icon === 'failed')) && (
-
+ {isAccordion && (
+
+ }
+ ariaLabel="Toggle dropdown"
+ showAriaLabelInTippy={false}
/>
)}
- {isHelmManifestPushFailed && renderErrorInfoBar()}
- {type === TIMELINE_STATUS.GIT_COMMIT && renderDetailedData()}
- {type === TIMELINE_STATUS.ARGOCD_SYNC && renderDetailedData()}
- {type === TIMELINE_STATUS.KUBECTL_APPLY && renderDetailedData()}
- {type === TIMELINE_STATUS.APP_HEALTH && renderDetailChart()}
+ {renderAccordionDetails()}
{!hideVerticalConnector &&
}
>
)
diff --git a/src/Shared/Components/CICDHistory/ErrorInfoStatusBar.tsx b/src/Shared/Components/CICDHistory/ErrorInfoStatusBar.tsx
deleted file mode 100644
index 6a7094766..000000000
--- a/src/Shared/Components/CICDHistory/ErrorInfoStatusBar.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2024. Devtron Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { ReactComponent as Error } from '../../../Assets/Icon/ic-error-exclamation.svg'
-import { TIMELINE_STATUS } from '../../constants'
-import { ErrorInfoStatusBarType } from './types'
-
-export const ErrorInfoStatusBar = ({
- nonDeploymentError,
- type,
- errorMessage,
- hideVerticalConnector,
- hideErrorIcon,
-}: ErrorInfoStatusBarType) =>
- nonDeploymentError === type ? (
- <>
-
- {!hideErrorIcon &&
}
- {errorMessage}
- {type === TIMELINE_STATUS.HELM_MANIFEST_PUSHED_TO_HELM_REPO && (
-
- Ensure provided repository path is valid
- Check if credentials provided for OCI registry are valid and have PUSH permission
-
- )}
-
- {!hideVerticalConnector &&
}
- >
- ) : null
diff --git a/src/Shared/Components/CICDHistory/LogStageAccordion.tsx b/src/Shared/Components/CICDHistory/LogStageAccordion.tsx
index aa21f4c34..cfb9577d9 100644
--- a/src/Shared/Components/CICDHistory/LogStageAccordion.tsx
+++ b/src/Shared/Components/CICDHistory/LogStageAccordion.tsx
@@ -43,6 +43,7 @@ const LogStageAccordion = ({
fullScreenView,
searchIndex,
targetPlatforms,
+ logsRendererRef,
}: LogStageAccordionProps) => {
const handleAccordionToggle = () => {
if (isOpen) {
@@ -78,6 +79,8 @@ const LogStageAccordion = ({
}
}
+ const getLogsRendererReference = () => logsRendererRef.current
+
return (
{!!targetPlatforms?.length && (
<>
-
+
diff --git a/src/Shared/Components/CICDHistory/LogsRenderer.tsx b/src/Shared/Components/CICDHistory/LogsRenderer.tsx
index 3ef941bb6..5f82948ff 100644
--- a/src/Shared/Components/CICDHistory/LogsRenderer.tsx
+++ b/src/Shared/Components/CICDHistory/LogsRenderer.tsx
@@ -179,6 +179,8 @@ const useCIEventSource = (url: string, maxLength?: number): [string[], EventSour
const LogsRenderer = ({ triggerDetails, isBlobStorageConfigured, parentType, fullScreenView }: LogsRendererType) => {
const { pipelineId, envId, appId } = useParams()
+ const logsRendererRef = useRef(null)
+
const logsURL =
parentType === HistoryComponentType.CI
? `${Host}/${ROUTES.CI_CONFIG_GET}/${pipelineId}/workflow/${triggerDetails.id}/logs`
@@ -579,6 +581,7 @@ const LogsRenderer = ({ triggerDetails, isBlobStorageConfigured, parentType, ful
isLoading={index === stageList.length - 1 && areEventsProgressing}
fullScreenView={fullScreenView}
searchIndex={searchResults[currentSearchIndex]}
+ logsRendererRef={logsRendererRef}
/>
),
)}
@@ -615,7 +618,10 @@ const LogsRenderer = ({ triggerDetails, isBlobStorageConfigured, parentType, ful
}
return (
-
+
{triggerDetails.podStatus !== POD_STATUS.PENDING &&
logsNotAvailable &&
(!isBlobStorageConfigured || !triggerDetails.blobStorageEnabled)
diff --git a/src/Shared/Components/CICDHistory/cicdHistory.scss b/src/Shared/Components/CICDHistory/cicdHistory.scss
index eab83d704..0c4624709 100644
--- a/src/Shared/Components/CICDHistory/cicdHistory.scss
+++ b/src/Shared/Components/CICDHistory/cicdHistory.scss
@@ -180,65 +180,6 @@
}
}
-.deployment-status-breakdown-container,
-.deployment-approval-container {
- .vertical-connector {
- border-left: 1px solid var(--N300);
- height: 15px;
- position: relative;
- left: 18px;
- width: 5px;
- }
- .deployment-status-breakdown-row {
- display: flex;
- align-items: center;
- justify-content: left;
-
- &.border-collapse {
- border-radius: 4px 4px 0 0;
- }
- }
-
- .pulse-highlight {
- width: 12px;
- height: 12px;
- border: solid 4px var(--O200);
- background-color: var(--O500);
- position: relative;
- top: 5px;
- right: -5px;
- border-radius: 50%;
- animation-name: pulse;
- animation-duration: 2s;
- animation-iteration-count: infinite;
- animation-timing-function: linear;
- }
-
- .green-tick {
- path {
- stroke: var(--G500);
- }
- }
-
- .app-status-row {
- display: grid;
- grid-template-columns: 150px 200px 150px auto;
- grid-column-gap: 16px;
- }
- .resource-list {
- .app-status-row {
- &:hover {
- background-color: var(--bg-secondary);
- }
- }
- }
-
- .detail-tab_border {
- border-radius: 0 0 4px 4px;
- border-top: 0;
- }
-}
-
.history-component__wrapper {
.history-component__artifact {
width: min(100%, 800px);
diff --git a/src/Shared/Components/CICDHistory/service.tsx b/src/Shared/Components/CICDHistory/service.tsx
index b28f2935c..582d524ee 100644
--- a/src/Shared/Components/CICDHistory/service.tsx
+++ b/src/Shared/Components/CICDHistory/service.tsx
@@ -81,6 +81,9 @@ export const cancelPrePostCdTrigger = (pipelineId, workflowRunner, isForceAbort:
return trash(URL)
}
+/**
+ * @deprecated
+ */
export function getDeploymentStatusDetail(
appId: string,
envId: string,
diff --git a/src/Shared/Components/CICDHistory/types.tsx b/src/Shared/Components/CICDHistory/types.tsx
index 96d353dae..83e13cbdb 100644
--- a/src/Shared/Components/CICDHistory/types.tsx
+++ b/src/Shared/Components/CICDHistory/types.tsx
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { CSSProperties, ReactElement, ReactNode } from 'react'
+import { CSSProperties, MutableRefObject, ReactElement, ReactNode } from 'react'
import { SupportedKeyboardKeysType } from '@Common/Hooks/UseRegisterShortcut/types'
@@ -33,6 +33,10 @@ import {
import { DeploymentStageType } from '../../constants'
import {
AggregationKeys,
+ AppDetails,
+ DeploymentStatusDetailsBreakdownDataType,
+ DeploymentStatusDetailsType,
+ DeploymentStatusTimelineType,
GitTriggers,
Node,
NodeType,
@@ -371,66 +375,7 @@ export type FinishedType = { artifact: string; type: HistoryComponentType } & (
}
)
-export interface SyncStageResourceDetail {
- id: number
- cdWorkflowRunnerId: number
- resourceGroup: string
- resourceKind: string
- resourceName: string
- resourcePhase: string
- resourceStatus: string
- statusMessage: string
-}
-
-export interface DeploymentStatusDetailsTimelineType {
- id: number
- cdWorkflowRunnerId: number
- status: string
- statusDetail: string
- statusTime: string
- resourceDetails?: SyncStageResourceDetail[]
-}
-export interface DeploymentStatusDetailsType extends Pick
{
- deploymentFinishedOn: string
- deploymentStartedOn: string
- triggeredBy: string
- statusFetchCount: number
- statusLastFetchedAt: string
- timelines: DeploymentStatusDetailsTimelineType[]
- wfrStatus?: string
-}
-
-export interface DeploymentStatusDetailsResponse extends ResponseType {
- result?: DeploymentStatusDetailsType
-}
-
-interface DeploymentStatusDetailRow {
- icon: string
- displayText: ReactNode
- displaySubText: string
- time: string
- resourceDetails?: any
- isCollapsed?: boolean
- kubeList?: { icon: any; message: string }[]
- timelineStatus?: string
-}
-export interface DeploymentStatusDetailsBreakdownDataType {
- deploymentStatus: string
- deploymentStatusText: string
- deploymentTriggerTime: string
- deploymentEndTime: string
- deploymentError: string
- triggeredBy: string
- nonDeploymentError: string
- deploymentStatusBreakdown: {
- DEPLOYMENT_INITIATED: DeploymentStatusDetailRow
- GIT_COMMIT?: DeploymentStatusDetailRow
- ARGOCD_SYNC?: DeploymentStatusDetailRow
- KUBECTL_APPLY?: DeploymentStatusDetailRow
- APP_HEALTH?: DeploymentStatusDetailRow
- HELM_PACKAGE_GENERATED?: DeploymentStatusDetailRow
- }
-}
+export type DeploymentStatusDetailsResponse = ResponseType
export interface DeploymentDetailStepsType extends Pick {
deploymentStatus?: string
@@ -570,22 +515,19 @@ export interface LogsRendererType
export interface DeploymentStatusDetailBreakdownType {
deploymentStatusDetailsBreakdownData: DeploymentStatusDetailsBreakdownDataType
isVirtualEnvironment?: boolean
+ /**
+ * Won't be available if coming directly to deployment history from url
+ */
+ appDetails: AppDetails | null
+ rootClassName?: string
}
-export interface DeploymentStatusDetailRowType {
- type: string
+export interface DeploymentStatusDetailRowType extends Pick {
+ type: DeploymentStatusTimelineType
hideVerticalConnector?: boolean
deploymentDetailedData: DeploymentStatusDetailsBreakdownDataType
}
-export interface ErrorInfoStatusBarType {
- nonDeploymentError: string
- type: string
- errorMessage: string
- hideVerticalConnector?: boolean
- hideErrorIcon?: boolean
-}
-
export interface DeploymentConfigurationsRes extends ResponseType {
result?: DeploymentTemplateList[]
}
@@ -892,6 +834,7 @@ export interface LogStageAccordionProps extends StageDetailType, Pick
}
export interface CreateMarkupReturnType {
diff --git a/src/Shared/Components/CICDHistory/utils.tsx b/src/Shared/Components/CICDHistory/utils.tsx
index b69018d74..c039d5e26 100644
--- a/src/Shared/Components/CICDHistory/utils.tsx
+++ b/src/Shared/Components/CICDHistory/utils.tsx
@@ -13,24 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { ReactElement } from 'react'
+import { ComponentProps, ReactElement } from 'react'
import moment from 'moment'
import { ReactComponent as ICCheck } from '@Icons/ic-check.svg'
-import { ReactComponent as Check } from '@Icons/ic-check-grey.svg'
import { ReactComponent as Close } from '@Icons/ic-close.svg'
-import { ReactComponent as Disconnect } from '@Icons/ic-disconnected.svg'
-import { ReactComponent as Error } from '@Icons/ic-error-exclamation.svg'
-import { ReactComponent as ICHelpOutline } from '@Icons/ic-help-outline.svg'
import { ReactComponent as ICInProgress } from '@Icons/ic-in-progress.svg'
-import { ReactComponent as TimeOut } from '@Icons/ic-timeout-red.svg'
-import { ReactComponent as Timer } from '@Icons/ic-timer.svg'
import { DATE_TIME_FORMATS } from '@Common/Constants'
import { DeploymentAppTypes } from '@Common/Types'
import { ALL_RESOURCE_KIND_FILTER } from '@Shared/constants'
import { isTimeStringAvailable } from '@Shared/Helpers'
-import { Node, ResourceKindType, WorkflowStatusEnum } from '../../types'
+import { DeploymentStatusBreakdownItemType, Node, ResourceKindType, WorkflowStatusEnum } from '../../types'
import { Icon } from '../Icon'
import { AppStatus, DeploymentStatus, StatusType } from '../StatusComponent'
import { TabGroupProps } from '../TabGroup'
@@ -125,33 +119,6 @@ export const buildHoverHtmlForWebhook = (eventName, condition, selectors) => {
)
}
-export const renderIcon = (iconState: string): JSX.Element => {
- switch (iconState) {
- case 'success':
- return
- case 'failed':
- return
- case 'unknown':
- return
- case 'inprogress':
- return (
-
- )
- case 'unreachable':
- return
- case 'loading':
- return
- case 'disconnect':
- return
- case 'time_out':
- return
- default:
- return
- }
-}
-
export const getStageStatusIcon = (status: StageStatusType): JSX.Element => {
switch (status) {
case StageStatusType.SUCCESS:
@@ -379,6 +346,53 @@ export const getTriggerStatusIcon = (status: string) => {
}
}
+export const renderDeploymentTimelineIcon = (iconState: DeploymentStatusBreakdownItemType['icon']): JSX.Element => {
+ const iconBaseProps: Pick, 'size' | 'color'> = {
+ color: null,
+ size: 20,
+ }
+
+ switch (iconState) {
+ case 'success':
+ return
+ case 'failed':
+ return
+ case 'unknown':
+ return
+ case 'inprogress':
+ return (
+
+ )
+ case 'unreachable':
+ return
+ case 'loading':
+ return
+ case 'disconnect':
+ return
+ case 'timed_out':
+ return
+ default:
+ return
+ }
+}
+
+export const getDeploymentTimelineBGColorFromIcon = (icon: DeploymentStatusBreakdownItemType['icon']): string => {
+ switch (icon) {
+ case 'success':
+ return 'bcg-1 cg-7'
+ case 'failed':
+ case 'disconnect':
+ case 'timed_out':
+ return 'bcr-1 cr-5'
+ case 'inprogress':
+ case 'loading':
+ return 'bcy-1 cy-5'
+ default:
+ return 'bcn-1 cn-9'
+ }
+}
export const getTriggerOutputTabs = (
triggerDetails: History,
deploymentAppType: DeploymentAppTypes,
diff --git a/src/Shared/Components/DeploymentStatusBreakdown/constants.ts b/src/Shared/Components/DeploymentStatusBreakdown/constants.ts
new file mode 100644
index 000000000..3b961cb7c
--- /dev/null
+++ b/src/Shared/Components/DeploymentStatusBreakdown/constants.ts
@@ -0,0 +1,79 @@
+import { DEPLOYMENT_STATUS } from '@Shared/constants'
+import { DeploymentPhaseType, DeploymentStatusTimelineType, TIMELINE_STATUS } from '@Shared/types'
+
+import { WorkflowRunnerStatusDTO } from './types'
+
+export const DEPLOYMENT_STATUS_TEXT_MAP: Readonly<
+ Record<(typeof DEPLOYMENT_STATUS)[keyof typeof DEPLOYMENT_STATUS], string>
+> = {
+ [DEPLOYMENT_STATUS.SUCCEEDED]: 'Succeeded',
+ [DEPLOYMENT_STATUS.HEALTHY]: 'Healthy',
+ [DEPLOYMENT_STATUS.FAILED]: 'Failed',
+ [DEPLOYMENT_STATUS.TIMED_OUT]: 'Timed out',
+ [DEPLOYMENT_STATUS.UNABLE_TO_FETCH]: 'Unable to fetch status',
+ [DEPLOYMENT_STATUS.INPROGRESS]: 'In progress',
+ [DEPLOYMENT_STATUS.PROGRESSING]: 'Progressing',
+ [DEPLOYMENT_STATUS.STARTING]: 'Progressing',
+ [DEPLOYMENT_STATUS.INITIATING]: 'Progressing',
+ [DEPLOYMENT_STATUS.SUPERSEDED]: 'Superseded',
+ [DEPLOYMENT_STATUS.QUEUED]: 'Queued',
+ [DEPLOYMENT_STATUS.UNKNOWN]: 'Unknown',
+ [DEPLOYMENT_STATUS.CHECKING]: 'Checking Status',
+}
+
+// Might be more but as per BE its only these for now
+export const WFR_STATUS_DTO_TO_DEPLOYMENT_STATUS_MAP: Readonly<
+ Record
+> = {
+ [WorkflowRunnerStatusDTO.ABORTED]: DEPLOYMENT_STATUS.FAILED,
+ [WorkflowRunnerStatusDTO.FAILED]: DEPLOYMENT_STATUS.FAILED,
+
+ [WorkflowRunnerStatusDTO.TIMED_OUT]: DEPLOYMENT_STATUS.TIMED_OUT,
+ [WorkflowRunnerStatusDTO.UNABLE_TO_FETCH]: DEPLOYMENT_STATUS.UNABLE_TO_FETCH,
+
+ // Degraded means successful deployment
+ [WorkflowRunnerStatusDTO.DEGRADED]: DEPLOYMENT_STATUS.SUCCEEDED,
+ [WorkflowRunnerStatusDTO.HEALTHY]: DEPLOYMENT_STATUS.SUCCEEDED,
+ [WorkflowRunnerStatusDTO.SUCCEEDED]: DEPLOYMENT_STATUS.SUCCEEDED,
+
+ [WorkflowRunnerStatusDTO.INITIATING]: DEPLOYMENT_STATUS.INITIATING,
+ [WorkflowRunnerStatusDTO.STARTING]: DEPLOYMENT_STATUS.STARTING,
+ [WorkflowRunnerStatusDTO.PROGRESSING]: DEPLOYMENT_STATUS.INPROGRESS,
+
+ [WorkflowRunnerStatusDTO.QUEUED]: DEPLOYMENT_STATUS.QUEUED,
+}
+
+export const PROGRESSING_DEPLOYMENT_STATUS: Readonly<(typeof DEPLOYMENT_STATUS)[keyof typeof DEPLOYMENT_STATUS][]> = [
+ DEPLOYMENT_STATUS.INPROGRESS,
+ DEPLOYMENT_STATUS.PROGRESSING,
+ DEPLOYMENT_STATUS.STARTING,
+ DEPLOYMENT_STATUS.INITIATING,
+ DEPLOYMENT_STATUS.CHECKING,
+]
+
+export const SUCCESSFUL_DEPLOYMENT_STATUS: typeof PROGRESSING_DEPLOYMENT_STATUS = [
+ DEPLOYMENT_STATUS.SUCCEEDED,
+ DEPLOYMENT_STATUS.HEALTHY,
+]
+
+export const FAILED_DEPLOYMENT_STATUS: typeof PROGRESSING_DEPLOYMENT_STATUS = [
+ DEPLOYMENT_STATUS.FAILED,
+ DEPLOYMENT_STATUS.TIMED_OUT,
+ DEPLOYMENT_STATUS.UNABLE_TO_FETCH,
+]
+
+export const PHYSICAL_ENV_DEPLOYMENT_TIMELINE_ORDER: Readonly = [
+ TIMELINE_STATUS.DEPLOYMENT_INITIATED,
+ TIMELINE_STATUS.GIT_COMMIT,
+ TIMELINE_STATUS.ARGOCD_SYNC,
+ TIMELINE_STATUS.KUBECTL_APPLY,
+ TIMELINE_STATUS.APP_HEALTH,
+]
+
+export const DEPLOYMENT_PHASES: Readonly = [
+ DeploymentPhaseType.PRE_SYNC,
+ DeploymentPhaseType.SYNC,
+ DeploymentPhaseType.POST_SYNC,
+ DeploymentPhaseType.SKIP,
+ DeploymentPhaseType.SYNC_FAIL,
+]
diff --git a/src/Shared/Components/DeploymentStatusBreakdown/index.ts b/src/Shared/Components/DeploymentStatusBreakdown/index.ts
new file mode 100644
index 000000000..6eec6bb5c
--- /dev/null
+++ b/src/Shared/Components/DeploymentStatusBreakdown/index.ts
@@ -0,0 +1,3 @@
+export * from './constants'
+export * from './types'
+export * from './utils'
diff --git a/src/Shared/Components/DeploymentStatusBreakdown/types.ts b/src/Shared/Components/DeploymentStatusBreakdown/types.ts
new file mode 100644
index 000000000..6c0cc185e
--- /dev/null
+++ b/src/Shared/Components/DeploymentStatusBreakdown/types.ts
@@ -0,0 +1,29 @@
+import { DEPLOYMENT_STATUS } from '@Shared/constants'
+import {
+ DeploymentStatusBreakdownItemType,
+ DeploymentStatusDetailsType,
+ DeploymentStatusTimelineType,
+} from '@Shared/types'
+
+export enum WorkflowRunnerStatusDTO {
+ PROGRESSING = 'Progressing',
+ ABORTED = 'Aborted',
+ FAILED = 'Failed',
+ SUCCEEDED = 'Succeeded',
+ TIMED_OUT = 'TimedOut',
+ UNABLE_TO_FETCH = 'UnableToFetch',
+ STARTING = 'Starting',
+ QUEUED = 'Queued',
+ INITIATING = 'Initiating',
+ // Not found on BE but added for Backward compatibility
+ HEALTHY = 'Healthy',
+ DEGRADED = 'Degraded',
+}
+
+export interface ProcessUnableToFetchOrTimedOutStatusType {
+ timelineData: DeploymentStatusBreakdownItemType
+ timelineStatusType: DeploymentStatusTimelineType
+ deploymentStatus: typeof DEPLOYMENT_STATUS.UNABLE_TO_FETCH | typeof DEPLOYMENT_STATUS.TIMED_OUT
+ statusLastFetchedAt: DeploymentStatusDetailsType['statusLastFetchedAt'] | null
+ statusFetchCount: DeploymentStatusDetailsType['statusFetchCount'] | null
+}
diff --git a/src/Shared/Components/DeploymentStatusBreakdown/utils.tsx b/src/Shared/Components/DeploymentStatusBreakdown/utils.tsx
new file mode 100644
index 000000000..89a232fe0
--- /dev/null
+++ b/src/Shared/Components/DeploymentStatusBreakdown/utils.tsx
@@ -0,0 +1,388 @@
+/* eslint-disable no-param-reassign */
+import { findRight, handleUTCTime, logExceptionToSentry } from '@Common/Helper'
+import { DEPLOYMENT_STATUS } from '@Shared/constants'
+import {
+ DeploymentPhaseType,
+ DeploymentStatusBreakdownItemType,
+ DeploymentStatusDetailsBreakdownDataType,
+ DeploymentStatusDetailsTimelineType,
+ DeploymentStatusDetailsType,
+ DeploymentStatusTimelineType,
+ TIMELINE_STATUS,
+} from '@Shared/types'
+
+import {
+ DEPLOYMENT_PHASES,
+ FAILED_DEPLOYMENT_STATUS,
+ PHYSICAL_ENV_DEPLOYMENT_TIMELINE_ORDER,
+ PROGRESSING_DEPLOYMENT_STATUS,
+ SUCCESSFUL_DEPLOYMENT_STATUS,
+ WFR_STATUS_DTO_TO_DEPLOYMENT_STATUS_MAP,
+} from './constants'
+import { ProcessUnableToFetchOrTimedOutStatusType } from './types'
+
+const getDefaultDeploymentStatusTimeline = (
+ data?: DeploymentStatusDetailsType,
+): DeploymentStatusDetailsBreakdownDataType => {
+ const commonProps: Pick<
+ DeploymentStatusBreakdownItemType,
+ 'displaySubText' | 'time' | 'isCollapsed' | 'timelineStatus' | 'icon'
+ > = {
+ icon: '',
+ displaySubText: '',
+ timelineStatus: '',
+ time: '',
+ isCollapsed: true,
+ }
+
+ const deploymentStatus = WFR_STATUS_DTO_TO_DEPLOYMENT_STATUS_MAP[data?.wfrStatus] || DEPLOYMENT_STATUS.INPROGRESS
+ // Incase of git commit failed or argocd sync failed, DEPLOYMENT_STATUS.FAILED won't come from BE
+ const deploymentErrorMessage =
+ deploymentStatus === DEPLOYMENT_STATUS.FAILED
+ ? data?.timelines?.find((timeline) => timeline.status === TIMELINE_STATUS.DEPLOYMENT_FAILED)
+ ?.statusDetail || ''
+ : ''
+
+ return {
+ deploymentStatus,
+ deploymentTriggerTime: data?.deploymentStartedOn || '',
+ deploymentEndTime: data?.deploymentFinishedOn || '',
+ triggeredBy: data?.triggeredBy || '',
+ deploymentStatusBreakdown: {
+ [TIMELINE_STATUS.DEPLOYMENT_INITIATED]: {
+ ...commonProps,
+ icon: 'success',
+ displayText: `Deployment initiated ${data?.triggeredBy ? `by ${data.triggeredBy}` : ''}`,
+ },
+ [TIMELINE_STATUS.GIT_COMMIT]: {
+ ...commonProps,
+ displayText: 'Push manifest to Git',
+ },
+ [TIMELINE_STATUS.ARGOCD_SYNC]: {
+ ...commonProps,
+ displayText: 'Synced with Argo CD',
+ },
+ [TIMELINE_STATUS.KUBECTL_APPLY]: {
+ ...commonProps,
+ displayText: 'Apply manifest to Kubernetes',
+ resourceDetails: [],
+ subSteps: [],
+ },
+ [TIMELINE_STATUS.APP_HEALTH]: {
+ ...commonProps,
+ displayText: 'Propagate manifest to Kubernetes resources',
+ },
+ },
+ errorBarConfig: deploymentErrorMessage
+ ? {
+ deploymentErrorMessage,
+ nextTimelineToProcess: TIMELINE_STATUS.GIT_COMMIT,
+ }
+ : null,
+ }
+}
+
+const getPredicate =
+ (timelineStatus: DeploymentStatusTimelineType) =>
+ (timelineItem: DeploymentStatusDetailsTimelineType): boolean => {
+ switch (timelineStatus) {
+ case TIMELINE_STATUS.DEPLOYMENT_INITIATED:
+ return timelineStatus === timelineItem.status
+
+ case TIMELINE_STATUS.GIT_COMMIT:
+ return timelineItem.status.includes(TIMELINE_STATUS.GIT_COMMIT)
+
+ case TIMELINE_STATUS.ARGOCD_SYNC:
+ return timelineItem.status.includes(TIMELINE_STATUS.ARGOCD_SYNC)
+
+ case TIMELINE_STATUS.KUBECTL_APPLY:
+ return timelineItem.status.includes(TIMELINE_STATUS.KUBECTL_APPLY)
+
+ case TIMELINE_STATUS.APP_HEALTH:
+ return [TIMELINE_STATUS.HEALTHY, TIMELINE_STATUS.DEGRADED].includes(timelineItem.status)
+
+ default:
+ return false
+ }
+ }
+
+const processUnableToFetchOrTimedOutStatus = ({
+ timelineData,
+ timelineStatusType,
+ deploymentStatus,
+ statusLastFetchedAt,
+ statusFetchCount,
+}: ProcessUnableToFetchOrTimedOutStatusType) => {
+ timelineData.icon = deploymentStatus === DEPLOYMENT_STATUS.UNABLE_TO_FETCH ? 'disconnect' : 'timed_out'
+ timelineData.displaySubText = 'Unknown'
+ timelineData.isCollapsed = false
+
+ const lastFetchedTime = statusLastFetchedAt ? handleUTCTime(statusLastFetchedAt, true) : '--'
+ const resourceError = `Below resources did not become healthy within 10 mins. Resource status shown below was last fetched ${lastFetchedTime}. ${statusFetchCount || '--'} retries failed.`
+ timelineData.timelineStatus = [TIMELINE_STATUS.KUBECTL_APPLY, TIMELINE_STATUS.APP_HEALTH].includes(
+ timelineStatusType,
+ )
+ ? resourceError
+ : ''
+}
+
+const processKubeCTLApply = (
+ timelineData: DeploymentStatusBreakdownItemType,
+ element: DeploymentStatusDetailsTimelineType,
+ deploymentStatus: DeploymentStatusDetailsBreakdownDataType['deploymentStatus'],
+ data: DeploymentStatusDetailsType,
+) => {
+ const tableData: {
+ currentPhase: DeploymentPhaseType | null
+ currentTableData: DeploymentStatusBreakdownItemType['subSteps']
+ } = {
+ currentPhase: null,
+ currentTableData: [{ icon: 'success', message: 'Started by Argo CD' }],
+ }
+
+ // Resource details are present in KUBECTL_APPLY_STARTED timeline alone
+ const resourceDetails = data.timelines.find(
+ (item) => item.status === TIMELINE_STATUS.KUBECTL_APPLY_STARTED,
+ )?.resourceDetails
+
+ if (resourceDetails) {
+ // Used to parse resource details base struct with current phase as last phase
+ DEPLOYMENT_PHASES.forEach((phase) => {
+ const resourceWithSamePhase = resourceDetails.find((item) => item.resourcePhase === phase)
+ if (resourceWithSamePhase) {
+ tableData.currentPhase = phase
+ tableData.currentTableData.push({
+ icon: 'success',
+ phase,
+ message: `${phase}: Create and update resources based on manifest`,
+ })
+ }
+ })
+ }
+
+ if (element.status === TIMELINE_STATUS.KUBECTL_APPLY_STARTED) {
+ timelineData.resourceDetails = (element.resourceDetails || []).filter(
+ (item) => item.resourcePhase === tableData.currentPhase,
+ )
+
+ if (PROGRESSING_DEPLOYMENT_STATUS.includes(deploymentStatus)) {
+ timelineData.icon = 'inprogress'
+ timelineData.displaySubText = 'In progress'
+ timelineData.time = element.statusTime
+ timelineData.timelineStatus = element.statusDetail
+ timelineData.isCollapsed = false
+ timelineData.subSteps = tableData.currentTableData.map((item) => ({
+ icon: item.phase === tableData.currentPhase ? 'loading' : 'success',
+ message: item.message,
+ }))
+ return
+ }
+
+ if (deploymentStatus === DEPLOYMENT_STATUS.FAILED) {
+ timelineData.icon = 'unknown'
+ timelineData.displaySubText = ': Unknown'
+ return
+ }
+
+ if (
+ deploymentStatus === DEPLOYMENT_STATUS.TIMED_OUT ||
+ deploymentStatus === DEPLOYMENT_STATUS.UNABLE_TO_FETCH
+ ) {
+ processUnableToFetchOrTimedOutStatus({
+ timelineData,
+ timelineStatusType: TIMELINE_STATUS.KUBECTL_APPLY,
+ deploymentStatus,
+ statusLastFetchedAt: data?.statusLastFetchedAt,
+ statusFetchCount: data?.statusFetchCount,
+ })
+ timelineData.subSteps = tableData.currentTableData.map((item) => ({
+ icon: item.phase === tableData.currentPhase ? 'failed' : 'success',
+ message: item.message,
+ }))
+ return
+ }
+
+ return
+ }
+
+ if (element.status === TIMELINE_STATUS.KUBECTL_APPLY_SYNCED) {
+ timelineData.resourceDetails = []
+ timelineData.displaySubText = ''
+ timelineData.time = element.statusTime
+ timelineData.icon = 'success'
+ timelineData.subSteps = tableData.currentTableData
+ }
+}
+
+/**
+ * @description
+ * This function processes the deployment status details data and returns a breakdown of the deployment status.
+ * Cases it handles:
+ * 1. If timelines are not present, say the case of helm deployment, we will parse the wfrStatus and put the status and basic deployment info [triggeredBy, deploymentStartedOn, deploymentFinishedOn] into the breakdown data and return it.
+ * 2. In case of gitops:
+ * - There are five timelines in chronological order:
+ * - Deployment Initiated
+ * - Git commit
+ * - ArgoCD Sync
+ * - Kubectl Apply
+ * - App Health
+ * - Basic flow is we traverse the timelines in order, if find the last status for that specific timeline from response by traversing the timelines in reverse order.
+ * - If element is found, we will parse the status and set the icon, display text, time, etc. for that timeline and set the next timeline to inprogress.
+ * - If element is not found, we will parse on basis of factors like:
+ * - If this timeline is not inprogress and deploymentStatus is progressing, we will set the current timeline to waiting.
+ * - In similar fashion based on the deploymentStatus we will set the icon and display text for the timeline.
+ */
+export const processDeploymentStatusDetailsData = (
+ data?: DeploymentStatusDetailsType,
+): DeploymentStatusDetailsBreakdownDataType => {
+ if (data && !WFR_STATUS_DTO_TO_DEPLOYMENT_STATUS_MAP[data.wfrStatus]) {
+ logExceptionToSentry(new Error(`New WFR status found: ${data?.wfrStatus}`))
+ }
+ const deploymentData = getDefaultDeploymentStatusTimeline(data)
+
+ const { deploymentStatus } = deploymentData
+
+ if (!data?.timelines) {
+ if (SUCCESSFUL_DEPLOYMENT_STATUS.includes(deploymentStatus)) {
+ Object.values(deploymentData.deploymentStatusBreakdown).forEach((value) => {
+ value.icon = 'success'
+ })
+ } else if (FAILED_DEPLOYMENT_STATUS.includes(deploymentStatus)) {
+ deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = 'Failed'
+ }
+
+ return deploymentData
+ }
+
+ if (!data.timelines.length) {
+ return deploymentData
+ }
+
+ const isProgressing = PROGRESSING_DEPLOYMENT_STATUS.includes(deploymentStatus)
+ const hasDeploymentFailed = deploymentStatus === DEPLOYMENT_STATUS.FAILED
+ // This key will be used since argocd sync is manual or auto based on flag on BE.
+ // And in old data as well this timeline won't be present so in KUBECTL_APPLY timeline we will set the icon to success
+ const isArgoCDSyncAvailable = data.timelines.some((timeline) =>
+ timeline.status.includes(TIMELINE_STATUS.ARGOCD_SYNC),
+ )
+
+ PHYSICAL_ENV_DEPLOYMENT_TIMELINE_ORDER.forEach((timelineStatusType, index) => {
+ const element = findRight(data.timelines, getPredicate(timelineStatusType))
+ const timelineData = deploymentData.deploymentStatusBreakdown[timelineStatusType]
+
+ if (!element) {
+ // This means the the last timeline is progressing or waiting so obviously this timeline is also waiting
+ if (isProgressing && timelineData.icon !== 'inprogress') {
+ timelineData.icon = ''
+ timelineData.displaySubText = 'Waiting'
+ }
+
+ // We don't even need to clean this in final loop since deployment status won't be in progress if next timeline is progressing
+ if (isProgressing && timelineStatusType === TIMELINE_STATUS.KUBECTL_APPLY) {
+ timelineData.subSteps = [
+ { icon: '', message: 'Waiting to be started by Argo CD' },
+ { icon: '', message: 'Create and update resources based on manifest' },
+ ]
+ timelineData.isCollapsed = false
+ }
+
+ if (hasDeploymentFailed) {
+ const hasCurrentTimelineFailed =
+ timelineStatusType === TIMELINE_STATUS.APP_HEALTH &&
+ deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon === 'success'
+
+ timelineData.displaySubText = hasCurrentTimelineFailed ? 'Failed' : ''
+ timelineData.icon = hasCurrentTimelineFailed ? 'failed' : 'unreachable'
+ }
+
+ if (
+ (deploymentStatus === DEPLOYMENT_STATUS.UNABLE_TO_FETCH ||
+ deploymentStatus === DEPLOYMENT_STATUS.TIMED_OUT) &&
+ timelineData.icon === 'inprogress'
+ ) {
+ processUnableToFetchOrTimedOutStatus({
+ timelineData,
+ timelineStatusType,
+ deploymentStatus,
+ statusLastFetchedAt: data?.statusLastFetchedAt,
+ statusFetchCount: data?.statusFetchCount,
+ })
+ }
+ return
+ }
+
+ switch (timelineStatusType) {
+ case TIMELINE_STATUS.DEPLOYMENT_INITIATED:
+ case TIMELINE_STATUS.GIT_COMMIT:
+ case TIMELINE_STATUS.ARGOCD_SYNC:
+ timelineData.time = element.statusTime
+ timelineData.icon = 'success'
+ timelineData.displaySubText = ''
+
+ // These are singular events so either their success will come or failure
+ if ([TIMELINE_STATUS.GIT_COMMIT_FAILED, TIMELINE_STATUS.ARGOCD_SYNC_FAILED].includes(element.status)) {
+ timelineData.displaySubText = 'Failed'
+ timelineData.icon = 'failed'
+ timelineData.isCollapsed = false
+ timelineData.timelineStatus = element.statusDetail
+ // Not handling the next timelines here since would be handled in the next iteration through deploymentStatus;
+ // So Assumption is deploymentStatus is going to Failed in this case
+ }
+ break
+
+ case TIMELINE_STATUS.KUBECTL_APPLY: {
+ if (!isArgoCDSyncAvailable) {
+ deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'success'
+ deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = ''
+ deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.time = element.statusTime
+ deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.isCollapsed = true
+ }
+
+ processKubeCTLApply(timelineData, element, deploymentStatus, data)
+ break
+ }
+
+ case TIMELINE_STATUS.APP_HEALTH:
+ timelineData.time = element.statusTime
+ timelineData.icon = 'success'
+ timelineData.displaySubText = element.status === TIMELINE_STATUS.HEALTHY ? '' : 'Degraded'
+ break
+
+ default:
+ break
+ }
+
+ // Moving the next timeline to inprogress
+ if (timelineData.icon === 'success' && index !== PHYSICAL_ENV_DEPLOYMENT_TIMELINE_ORDER.length - 1) {
+ const nextTimelineStatus = PHYSICAL_ENV_DEPLOYMENT_TIMELINE_ORDER[index + 1]
+ const nextTimeline = deploymentData.deploymentStatusBreakdown[nextTimelineStatus]
+
+ if (deploymentData.errorBarConfig) {
+ deploymentData.errorBarConfig.nextTimelineToProcess = nextTimelineStatus
+ }
+
+ nextTimeline.icon = 'inprogress'
+ nextTimeline.displaySubText = 'In progress'
+ }
+ })
+
+ // Traversing the timeline in reverse order so that if any status is there which is inprogress or success then we will mark all the previous steps as success
+ for (let i = PHYSICAL_ENV_DEPLOYMENT_TIMELINE_ORDER.length - 1; i >= 0; i -= 1) {
+ const timelineStatusType = PHYSICAL_ENV_DEPLOYMENT_TIMELINE_ORDER[i]
+ const timelineData = deploymentData.deploymentStatusBreakdown[timelineStatusType]
+
+ if (timelineData.icon === 'inprogress' || timelineData.icon === 'success') {
+ for (let j = i - 1; j >= 0; j -= 1) {
+ const prevTimelineStatusType = PHYSICAL_ENV_DEPLOYMENT_TIMELINE_ORDER[j]
+ const prevTimelineData = deploymentData.deploymentStatusBreakdown[prevTimelineStatusType]
+ prevTimelineData.icon = 'success'
+ prevTimelineData.displaySubText = ''
+ prevTimelineData.isCollapsed = true
+ prevTimelineData.timelineStatus = ''
+ }
+ break
+ }
+ }
+
+ return deploymentData
+}
diff --git a/src/Shared/Components/StatusComponent/utils.ts b/src/Shared/Components/StatusComponent/utils.ts
index a9c110f0c..5e5bf1e93 100644
--- a/src/Shared/Components/StatusComponent/utils.ts
+++ b/src/Shared/Components/StatusComponent/utils.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-import { TIMELINE_STATUS } from '@Shared/constants'
-import { WorkflowStatusEnum } from '@Shared/types'
+import { TIMELINE_STATUS, WorkflowStatusEnum } from '@Shared/types'
import { IconName, IconsProps } from '../Icon'
import { StatusType } from './types'
@@ -71,6 +70,8 @@ export const getIconName = (status: string, showAnimatedIcon: boolean): IconName
case 'timedout':
case 'timed_out':
return 'ic-timeout-dash'
+ case 'unable_to_fetch':
+ return 'ic-disconnect'
case 'deleting':
return 'ic-delete-dots'
case 'deleted':
@@ -95,6 +96,7 @@ export const getIconColor = (status: string): IconsProps['color'] => {
case 'request_accepted':
case 'starting':
return 'O500'
+ case 'unable_to_fetch':
case 'timedout':
case 'timed_out':
case 'deleting':
diff --git a/src/Shared/Components/TabGroup/TabGroup.component.tsx b/src/Shared/Components/TabGroup/TabGroup.component.tsx
index 63b3a54b1..d0ddae61e 100644
--- a/src/Shared/Components/TabGroup/TabGroup.component.tsx
+++ b/src/Shared/Components/TabGroup/TabGroup.component.tsx
@@ -56,6 +56,7 @@ const Tab = ({
shouldWrapTooltip,
tooltipProps,
uniqueGroupId,
+ iconElement,
}: TabProps & Pick & AdditionalTabProps) => {
const { pathname, search } = useLocation()
const ref = useRef(null)
@@ -88,7 +89,7 @@ const Tab = ({
const content = (
<>
- {getTabIcon({ className: iconClassName, icon, showError, showWarning, size, active })}
+ {getTabIcon({ className: iconClassName, icon, showError, showWarning, size, active, iconElement })}
{label}
diff --git a/src/Shared/Components/TabGroup/TabGroup.helpers.tsx b/src/Shared/Components/TabGroup/TabGroup.helpers.tsx
index 861e7550b..6be862718 100644
--- a/src/Shared/Components/TabGroup/TabGroup.helpers.tsx
+++ b/src/Shared/Components/TabGroup/TabGroup.helpers.tsx
@@ -28,7 +28,8 @@ export const getTabIcon = ({
className,
size,
active,
-}: Pick &
+ iconElement,
+}: Pick &
Pick & { className: string }) => {
if (showError) {
return
@@ -43,6 +44,9 @@ export const getTabIcon = ({
const RenderIcon = icon
return
}
+ if (iconElement) {
+ return iconElement
+ }
return null
}
diff --git a/src/Shared/Components/TabGroup/TabGroup.types.ts b/src/Shared/Components/TabGroup/TabGroup.types.ts
index 022b83254..619b9a064 100644
--- a/src/Shared/Components/TabGroup/TabGroup.types.ts
+++ b/src/Shared/Components/TabGroup/TabGroup.types.ts
@@ -93,6 +93,42 @@ type TabTooltipProps =
tooltipProps?: never
}
+/**
+ * Represents the properties for defining an icon in a tab group.
+ * This type allows for three configurations:
+ *
+ * 1. **Icon as a functional component or string**:
+ * - Use the `icon` property to specify either a functional component that renders an SVG or a string representing the name of the icon.
+ * - The `iconElement` property must not be provided in this case.
+ *
+ * 2. **Icon as a JSX element**:
+ * - Use the `iconElement` property to specify a JSX element representing the icon.
+ * - The `icon` property must not be provided in this case.
+ *
+ * 3. **No icon**:
+ * - Neither `icon` nor `iconElement` is provided, resulting in no icon being displayed.
+ *
+ */
+type TabGroupIconProp =
+ | {
+ /**
+ * A functional component rendering an SVG or a string representing the icon name. Mutually exclusive with `iconElement`.
+ */
+ icon: React.FunctionComponent> | IconName
+ iconElement?: never
+ }
+ | {
+ icon?: never
+ /**
+ * A JSX element representing the icon. Mutually exclusive with `icon`.
+ */
+ iconElement: JSX.Element
+ }
+ | {
+ icon?: never
+ iconElement?: never
+ }
+
export type TabProps = {
/**
* Unique identifier for the tab.
@@ -107,12 +143,6 @@ export type TabProps = {
* @note - If passed as a `string[]`, it will be rendered with a bullet in-between strings.
*/
description?: string | string[]
- /**
- * Icon to be displayed in the tab.
- * This can either be a functional component that renders a SVG
- * or a string representing the name of the icon to be rendered by the Icon component.
- */
- icon?: React.FunctionComponent> | IconName
/**
* Badge number to be displayed on the tab, typically for notifications.
*/
@@ -136,7 +166,8 @@ export type TabProps = {
*/
disabled?: boolean
} & ConditionalTabType &
- TabTooltipProps
+ TabTooltipProps &
+ TabGroupIconProp
export interface TabGroupProps {
/**
diff --git a/src/Shared/Components/TargetPlatforms/TargetPlatformListTooltip.tsx b/src/Shared/Components/TargetPlatforms/TargetPlatformListTooltip.tsx
index 31fad83e2..31a9e19cd 100644
--- a/src/Shared/Components/TargetPlatforms/TargetPlatformListTooltip.tsx
+++ b/src/Shared/Components/TargetPlatforms/TargetPlatformListTooltip.tsx
@@ -33,12 +33,13 @@ const TooltipContent = ({ targetPlatforms }: Pick
)
-const TargetPlatformListTooltip = ({ targetPlatforms, children }: TargetPlatformListTooltipProps) => (
+const TargetPlatformListTooltip = ({ targetPlatforms, children, appendTo }: TargetPlatformListTooltipProps) => (
}
placement="right"
alwaysShowTippyOnHover
interactive
+ appendTo={appendTo}
>
{children}
diff --git a/src/Shared/Components/TargetPlatforms/types.ts b/src/Shared/Components/TargetPlatforms/types.ts
index 99258901f..5df88d691 100644
--- a/src/Shared/Components/TargetPlatforms/types.ts
+++ b/src/Shared/Components/TargetPlatforms/types.ts
@@ -18,6 +18,6 @@ import { TooltipProps } from '@Common/Tooltip/types'
import { TargetPlatformsDTO } from '@Shared/types'
export interface TargetPlatformBadgeListProps extends Required> {}
-export interface TargetPlatformListTooltipProps extends Pick {
- children: TooltipProps['children']
-}
+export interface TargetPlatformListTooltipProps
+ extends Pick,
+ Pick {}
diff --git a/src/Shared/Components/index.ts b/src/Shared/Components/index.ts
index 36d71ccb6..12e39877c 100644
--- a/src/Shared/Components/index.ts
+++ b/src/Shared/Components/index.ts
@@ -40,6 +40,7 @@ export * from './CountrySelect'
export * from './CustomInput'
export * from './DatePicker'
export * from './DeploymentConfigDiff'
+export * from './DeploymentStatusBreakdown'
export * from './DetectBottom'
export * from './DiffViewer'
export * from './DynamicDataTable'
diff --git a/src/Shared/Helpers.tsx b/src/Shared/Helpers.tsx
index b73a459ad..2120400ef 100644
--- a/src/Shared/Helpers.tsx
+++ b/src/Shared/Helpers.tsx
@@ -36,7 +36,6 @@ import { MaterialHistoryType } from '@Shared/Services/app.types'
import {
ApprovalConfigDataType,
DATE_TIME_FORMATS,
- handleUTCTime,
ManualApprovalType,
mapByKey,
MaterialInfo,
@@ -46,18 +45,14 @@ import {
SortingOrder,
SourceTypeMap,
TOKEN_COOKIE_NAME,
+ URLS,
UserApprovalConfigType,
UserApprovalInfo,
ZERO_TIME_STRING,
} from '../Common'
import { getAggregator } from '../Pages'
-import {
- AggregatedNodes,
- DeploymentStatusDetailsBreakdownDataType,
- DeploymentStatusDetailsType,
- PodMetadatum,
-} from './Components'
-import { DEPLOYMENT_STATUS, TIMELINE_STATUS, UNSAVED_CHANGES_PROMPT_MESSAGE } from './constants'
+import { AggregatedNodes, PodMetadatum } from './Components'
+import { UNSAVED_CHANGES_PROMPT_MESSAGE } from './constants'
import {
AggregationKeys,
BorderConfigType,
@@ -306,399 +301,6 @@ export const renderValidInputButtonTippy = (children: ReactElement) => (
)
-// NOTE: Need to improve logic since in some cases the unknown status would leak to previous entites, can do that by not setting deploymentStatus as Failed by ourselves and let backend be source of truth of that
-export const processDeploymentStatusDetailsData = (
- data?: DeploymentStatusDetailsType,
-): DeploymentStatusDetailsBreakdownDataType => {
- const deploymentData = {
- deploymentStatus: 'inprogress',
- deploymentStatusText: 'In progress',
- deploymentTriggerTime: data?.deploymentStartedOn || '',
- deploymentEndTime: data?.deploymentFinishedOn || '',
- deploymentError: '',
- triggeredBy: data?.triggeredBy || '',
- nonDeploymentError: '',
- deploymentStatusBreakdown: {
- DEPLOYMENT_INITIATED: {
- icon: 'success',
- displayText: `Deployment initiated ${data?.triggeredBy ? `by ${data?.triggeredBy}` : ''}`,
- displaySubText: '',
- time: '',
- },
- GIT_COMMIT: {
- icon: '',
- displayText: 'Push manifest to Git',
- displaySubText: '',
- timelineStatus: '',
- time: '',
- isCollapsed: true,
- },
- ARGOCD_SYNC: {
- icon: '',
- displayText: 'Synced with Argo CD',
- displaySubText: '',
- timelineStatus: '',
- time: '',
- isCollapsed: true,
- },
- KUBECTL_APPLY: {
- icon: '',
- displayText: 'Apply manifest to Kubernetes',
- timelineStatus: '',
- displaySubText: '',
- time: '',
- resourceDetails: [],
- isCollapsed: true,
- kubeList: [],
- },
- APP_HEALTH: {
- icon: '',
- displayText: 'Propogate manifest to Kubernetes resources',
- timelineStatus: '',
- displaySubText: '',
- time: '',
- isCollapsed: true,
- },
- },
- }
-
- const lastFetchedTime = handleUTCTime(data?.statusLastFetchedAt, true)
- const deploymentPhases = ['PreSync', 'Sync', 'PostSync', 'Skip', 'SyncFail']
- const tableData: { currentPhase: string; currentTableData: { icon: string; phase?: string; message: string }[] } = {
- currentPhase: '',
- currentTableData: [{ icon: 'success', message: 'Started by Argo CD' }],
- }
-
- // data when timelines is available
- if (data?.timelines?.length) {
- // TO Support legacy data have to make sure that if ARGOCD_SYNC is not available then we fill it with dummy values
- const isArgoCDAvailable = data.timelines.some((timeline) =>
- timeline.status.includes(TIMELINE_STATUS.ARGOCD_SYNC),
- )
-
- for (let index = data.timelines.length - 1; index >= 0; index--) {
- const element = data.timelines[index]
- if (element.status === TIMELINE_STATUS.HEALTHY || element.status === TIMELINE_STATUS.DEGRADED) {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.SUCCEEDED
- deploymentData.deploymentStatusText = 'Succeeded'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText =
- element.status === TIMELINE_STATUS.HEALTHY ? '' : 'Degraded'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.time = element.statusTime
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'success'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'success'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.isCollapsed = true
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.isCollapsed = true
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'success'
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon = 'success'
- } else if (element.status === TIMELINE_STATUS.DEPLOYMENT_FAILED) {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.FAILED
- deploymentData.deploymentStatusText = 'Failed'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ' Failed'
- deploymentData.deploymentError = element.statusDetail
- } else if (element.status === TIMELINE_STATUS.DEPLOYMENT_SUPERSEDED) {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.SUPERSEDED
- } else if (
- index === data.timelines.length - 1 &&
- (element.status === TIMELINE_STATUS.FETCH_TIMED_OUT ||
- element.status === TIMELINE_STATUS.UNABLE_TO_FETCH_STATUS)
- ) {
- if (element.status === TIMELINE_STATUS.FETCH_TIMED_OUT) {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.TIMED_OUT
- deploymentData.deploymentStatusText = 'Timed out'
- } else if (element.status === TIMELINE_STATUS.UNABLE_TO_FETCH_STATUS) {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.UNABLE_TO_FETCH
- deploymentData.deploymentStatusText = 'Unable to fetch status'
- }
- deploymentData.deploymentError = `Below resources did not become healthy within 10 mins. Resource status shown below was last fetched ${lastFetchedTime}. ${data.statusFetchCount} retries failed.`
- } else if (element.status.includes(TIMELINE_STATUS.KUBECTL_APPLY)) {
- if (!isArgoCDAvailable) {
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'success'
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.time = element.statusTime
- }
-
- if (element?.resourceDetails) {
- deploymentPhases.forEach((phase) => {
- // eslint-disable-next-line no-restricted-syntax
- for (const item of element.resourceDetails) {
- if (phase === item.resourcePhase) {
- tableData.currentPhase = phase
- // Seems else block was forgotten to add here, TODO: Sync for this later
- // eslint-disable-next-line no-empty
- if (item.resourceStatus === 'failed') {
- }
- tableData.currentTableData.push({
- icon: 'success',
- phase,
- message: `${phase}: Create and update resources based on manifest`,
- })
- return
- }
- }
- })
- }
- if (
- element.status === TIMELINE_STATUS.KUBECTL_APPLY_STARTED &&
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.time === '' &&
- deploymentData.deploymentStatus !== DEPLOYMENT_STATUS.SUCCEEDED
- ) {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.resourceDetails =
- element.resourceDetails?.filter((item) => item.resourcePhase === tableData.currentPhase)
- if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.FAILED) {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unknown'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ': Unknown'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unknown'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ': Unknown'
- } else if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.SUCCEEDED) {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'success'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ''
- } else if (
- deploymentData.deploymentStatus === DEPLOYMENT_STATUS.TIMED_OUT ||
- deploymentData.deploymentStatus === DEPLOYMENT_STATUS.UNABLE_TO_FETCH
- ) {
- if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.TIMED_OUT) {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unknown'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.isCollapsed = false
- } else {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'disconnect'
- }
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = 'Unknown'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.timelineStatus =
- deploymentData.deploymentError
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.kubeList =
- tableData.currentTableData.map((item) => ({
- icon: item.phase === tableData.currentPhase ? 'failed' : 'success',
- message: item.message,
- }))
- } else {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'inprogress'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = 'In progress'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.time = element.statusTime
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.timelineStatus = element.statusDetail
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.isCollapsed = false
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.kubeList =
- tableData.currentTableData.map((item) => ({
- icon: item.phase === tableData.currentPhase ? 'loading' : 'success',
- message: item.message,
- }))
- }
- } else if (element.status === TIMELINE_STATUS.KUBECTL_APPLY_SYNCED) {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.resourceDetails = []
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.time = element.statusTime
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'success'
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'success'
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon = 'success'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.kubeList = tableData.currentTableData
-
- if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.INPROGRESS) {
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'inprogress'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.isCollapsed = false
- } else if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.FAILED) {
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'failed'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = 'Failed'
- } else if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.TIMED_OUT) {
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'timed_out'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = 'Unknown'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.timelineStatus =
- deploymentData.deploymentError
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.isCollapsed = false
- } else if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.UNABLE_TO_FETCH) {
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'disconnect'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = 'Unknown'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.timelineStatus =
- deploymentData.deploymentError
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.isCollapsed = false
- }
- }
- } else if (element.status.includes(TIMELINE_STATUS.ARGOCD_SYNC)) {
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.time = element.statusTime
-
- if (element.status === TIMELINE_STATUS.ARGOCD_SYNC_FAILED) {
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = 'Failed'
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'failed'
-
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.isCollapsed = false
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.FAILED
- deploymentData.deploymentStatusText = 'Failed'
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.timelineStatus = element.statusDetail
- } else {
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'success'
- if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.FAILED) {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unreachable'
-
- if (deploymentData.nonDeploymentError === '') {
- deploymentData.nonDeploymentError = TIMELINE_STATUS.KUBECTL_APPLY
- }
- } else if (
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.time === '' &&
- deploymentData.deploymentStatus === DEPLOYMENT_STATUS.INPROGRESS
- ) {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = 'Waiting'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.kubeList = [
- { icon: '', message: 'Waiting to be started by Argo CD' },
- { icon: '', message: 'Create and update resources based on manifest' },
- ]
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.isCollapsed = false
- }
- }
- } else if (element.status.includes(TIMELINE_STATUS.GIT_COMMIT)) {
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.time = element.statusTime
- if (element.status === TIMELINE_STATUS.GIT_COMMIT_FAILED) {
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.displaySubText = 'Failed'
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon = 'failed'
-
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.isCollapsed = false
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.FAILED
- deploymentData.deploymentStatusText = 'Failed'
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.timelineStatus = element.statusDetail
- } else {
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon = 'success'
- if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.FAILED) {
- if (deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon === '') {
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'unreachable'
- }
-
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unreachable'
-
- if (deploymentData.nonDeploymentError === '') {
- deploymentData.nonDeploymentError = TIMELINE_STATUS.ARGOCD_SYNC
- }
- } else if (
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.time === '' &&
- deploymentData.deploymentStatus === DEPLOYMENT_STATUS.INPROGRESS
- ) {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = 'Waiting'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.kubeList = [
- { icon: '', message: 'Waiting to be started by Argo CD' },
- { icon: '', message: 'Create and update resources based on manifest' },
- ]
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.isCollapsed = false
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'inprogress'
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = 'In progress'
- }
- }
- } else if (element.status === TIMELINE_STATUS.DEPLOYMENT_INITIATED) {
- deploymentData.deploymentStatusBreakdown.DEPLOYMENT_INITIATED.time = element.statusTime
- if (
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.time === '' &&
- deploymentData.deploymentStatus === DEPLOYMENT_STATUS.INPROGRESS
- ) {
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon = 'inprogress'
-
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = ''
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = 'Waiting'
-
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = 'Waiting'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.kubeList = [
- { icon: '', message: 'Waiting to be started by Argo CD' },
- { icon: '', message: 'Create and update resources based on manifest' },
- ]
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.isCollapsed = false
- }
- if (deploymentData.deploymentStatus === DEPLOYMENT_STATUS.FAILED) {
- if (deploymentData.deploymentStatusBreakdown.GIT_COMMIT.time === '') {
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unreachable'
-
- deploymentData.nonDeploymentError = TIMELINE_STATUS.GIT_COMMIT
- } else if (deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon !== 'failed') {
- if (deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.time === '') {
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = 'Unknown'
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'unknown'
-
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = 'Unknown'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unknown'
-
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ': Unknown'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unknown'
- } else if (deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon !== 'failed') {
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = 'Unknown'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unknown'
-
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ': Unknown'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unknown'
- }
- } else {
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'unreachable'
-
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = ''
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'unreachable'
-
- deploymentData.nonDeploymentError = TIMELINE_STATUS.GIT_COMMIT
- }
- }
- }
- }
- } else if (!data?.timelines) {
- // data when timelines is not available in case of the previously deployed app(deployment-status/timline api) )
- if (data?.wfrStatus === 'Healthy' || data?.wfrStatus === 'Succeeded') {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.SUCCEEDED
- deploymentData.deploymentStatusText = 'Succeeded'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.icon = 'success'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.icon = 'success'
- deploymentData.deploymentStatusBreakdown.KUBECTL_APPLY.isCollapsed = true
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.isCollapsed = true
- deploymentData.deploymentStatusBreakdown.ARGOCD_SYNC.icon = 'success'
- deploymentData.deploymentStatusBreakdown.GIT_COMMIT.icon = 'success'
- } else if (data?.wfrStatus === 'Failed' || data?.wfrStatus === 'Degraded') {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.FAILED
- deploymentData.deploymentStatusText = 'Failed'
- deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = 'Failed'
- } else if (data?.wfrStatus === 'Progressing') {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.INPROGRESS
- deploymentData.deploymentStatusText = 'In progress'
- } else if (data?.wfrStatus === 'TimedOut') {
- deploymentData.deploymentStatus = DEPLOYMENT_STATUS.TIMED_OUT
- deploymentData.deploymentStatusText = 'Timed out'
- }
- }
- return deploymentData
-}
-
export function aggregateNodes(nodes: any[], podMetadata: PodMetadatum[]): AggregatedNodes {
const podMetadataMap = mapByKey(podMetadata, 'name')
// group nodes
@@ -1090,3 +692,11 @@ export const getClassNameForStickyHeaderWithShadow = (isStuck: boolean, topClass
export const clearCookieOnLogout = () => {
document.cookie = `${TOKEN_COOKIE_NAME}=; expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/`
}
+
+export const getAppDetailsURL = (appId: number | string, envId?: number | string): string => {
+ const baseURL = `${URLS.APP}/${appId}/${URLS.APP_DETAILS}`
+ if (envId) {
+ return `${baseURL}/${envId}`
+ }
+ return baseURL
+}
diff --git a/src/Shared/constants.tsx b/src/Shared/constants.tsx
index b7dec2721..501c21c70 100644
--- a/src/Shared/constants.tsx
+++ b/src/Shared/constants.tsx
@@ -81,28 +81,6 @@ export enum EnvironmentTypeEnum {
nonProduction = 'Non-Production',
}
-export enum TIMELINE_STATUS {
- DEPLOYMENT_INITIATED = 'DEPLOYMENT_INITIATED',
- GIT_COMMIT = 'GIT_COMMIT',
- GIT_COMMIT_FAILED = 'GIT_COMMIT_FAILED',
- ARGOCD_SYNC = 'ARGOCD_SYNC',
- ARGOCD_SYNC_FAILED = 'ARGOCD_SYNC_FAILED',
- KUBECTL_APPLY = 'KUBECTL_APPLY',
- KUBECTL_APPLY_STARTED = 'KUBECTL_APPLY_STARTED',
- KUBECTL_APPLY_SYNCED = 'KUBECTL_APPLY_SYNCED',
- HEALTHY = 'HEALTHY',
- APP_HEALTH = 'APP_HEALTH',
- DEPLOYMENT_FAILED = 'FAILED',
- FETCH_TIMED_OUT = 'TIMED_OUT',
- UNABLE_TO_FETCH_STATUS = 'UNABLE_TO_FETCH_STATUS',
- DEGRADED = 'DEGRADED',
- DEPLOYMENT_SUPERSEDED = 'DEPLOYMENT_SUPERSEDED',
- ABORTED = 'ABORTED',
- INPROGRESS = 'INPROGRESS',
- HELM_PACKAGE_GENERATED = 'HELM_PACKAGE_GENERATED',
- HELM_MANIFEST_PUSHED_TO_HELM_REPO = 'HELM_MANIFEST_PUSHED_TO_HELM_REPO',
- HELM_MANIFEST_PUSHED_TO_HELM_REPO_FAILED = 'HELM_MANIFEST_PUSHED_TO_HELM_REPO_FAILED',
-}
/**
* Constants for NO MATCHING result
*/
@@ -599,3 +577,6 @@ export const DEPLOYMENT_STAGE_TO_NODE_MAP: Readonly
status: string
resourcesSyncResult?: Record
+ wfrId?: number
}
export enum AppType {
@@ -245,6 +248,8 @@ export interface AppDetails {
chartAvatar?: string
fluxTemplateType?: string
FluxAppStatusDetail?: FluxAppStatusDetail
+ isPipelineTriggered?: boolean
+ releaseMode?: ReleaseMode
}
export interface ConfigDriftModalProps extends Required> {
@@ -1165,6 +1170,122 @@ export enum RegistryCredentialsType {
ANONYMOUS = 'anonymous',
}
+export interface SyncStageResourceDetail {
+ id: number
+ cdWorkflowRunnerId: number
+ resourceGroup: string
+ resourceKind: string
+ resourceName: string
+ resourcePhase: string
+ resourceStatus: string
+ statusMessage: string
+}
+
+export enum TIMELINE_STATUS {
+ DEPLOYMENT_INITIATED = 'DEPLOYMENT_INITIATED',
+ GIT_COMMIT = 'GIT_COMMIT',
+ GIT_COMMIT_FAILED = 'GIT_COMMIT_FAILED',
+ ARGOCD_SYNC = 'ARGOCD_SYNC',
+ ARGOCD_SYNC_FAILED = 'ARGOCD_SYNC_FAILED',
+ KUBECTL_APPLY = 'KUBECTL_APPLY',
+ KUBECTL_APPLY_STARTED = 'KUBECTL_APPLY_STARTED',
+ KUBECTL_APPLY_SYNCED = 'KUBECTL_APPLY_SYNCED',
+ HEALTHY = 'HEALTHY',
+ APP_HEALTH = 'APP_HEALTH',
+ DEPLOYMENT_FAILED = 'FAILED',
+ FETCH_TIMED_OUT = 'TIMED_OUT',
+ UNABLE_TO_FETCH_STATUS = 'UNABLE_TO_FETCH_STATUS',
+ DEGRADED = 'DEGRADED',
+ DEPLOYMENT_SUPERSEDED = 'DEPLOYMENT_SUPERSEDED',
+ ABORTED = 'ABORTED',
+ INPROGRESS = 'INPROGRESS',
+ HELM_PACKAGE_GENERATED = 'HELM_PACKAGE_GENERATED',
+ HELM_PACKAGE_GENERATION_FAILED = 'HELM_PACKAGE_GENERATION_FAILED',
+ HELM_MANIFEST_PUSHED_TO_HELM_REPO = 'HELM_MANIFEST_PUSHED_TO_HELM_REPO',
+ HELM_MANIFEST_PUSHED_TO_HELM_REPO_FAILED = 'HELM_MANIFEST_PUSHED_TO_HELM_REPO_FAILED',
+}
+
+export interface DeploymentStatusDetailsTimelineType
+ extends Pick {
+ status: TIMELINE_STATUS
+ statusDetail: string
+ statusTime: string
+ resourceDetails?: SyncStageResourceDetail[]
+}
+
+export interface DeploymentStatusDetailsType {
+ deploymentFinishedOn: string
+ deploymentStartedOn: string
+ triggeredBy: string
+ statusFetchCount: number
+ statusLastFetchedAt: string
+ timelines: DeploymentStatusDetailsTimelineType[]
+ wfrStatus?: WorkflowRunnerStatusDTO
+ isDeploymentWithoutApproval: boolean
+}
+
+export type DeploymentStatusTimelineType =
+ | TIMELINE_STATUS.DEPLOYMENT_INITIATED
+ | TIMELINE_STATUS.GIT_COMMIT
+ | TIMELINE_STATUS.ARGOCD_SYNC
+ | TIMELINE_STATUS.KUBECTL_APPLY
+ | TIMELINE_STATUS.APP_HEALTH
+ | TIMELINE_STATUS.HELM_PACKAGE_GENERATED
+ | TIMELINE_STATUS.HELM_MANIFEST_PUSHED_TO_HELM_REPO
+
+export type DeploymentStatusBreakdownItemIconType =
+ | 'success'
+ | 'failed'
+ | 'unknown'
+ | 'inprogress'
+ | 'unreachable'
+ // Loading is for subSteps
+ | 'loading'
+ | 'disconnect'
+ | 'timed_out'
+ | ''
+
+export enum DeploymentPhaseType {
+ PRE_SYNC = 'PreSync',
+ SYNC = 'Sync',
+ POST_SYNC = 'PostSync',
+ SKIP = 'Skip',
+ SYNC_FAIL = 'SyncFail',
+}
+
+export interface DeploymentStatusBreakdownItemType {
+ icon: DeploymentStatusBreakdownItemIconType
+ displayText: ReactNode
+ displaySubText: string
+ time: string
+ /**
+ * Shown in accordion details if type is TIMELINE_STATUS.KUBECTL_APPLY to display resource details
+ */
+ resourceDetails?: SyncStageResourceDetail[]
+ isCollapsed?: boolean
+ /**
+ * Sub-Steps in accordion details in case type is TIMELINE_STATUS.KUBECTL_APPLY
+ */
+ subSteps?: { icon: DeploymentStatusBreakdownItemIconType; message: string; phase?: DeploymentPhaseType }[]
+ /**
+ * To be shown in accordion details below heading tile
+ */
+ timelineStatus?: ReactNode
+ showHelmManifest?: boolean
+}
+
+export interface DeploymentStatusDetailsBreakdownDataType {
+ deploymentStatus: (typeof DEPLOYMENT_STATUS)[keyof typeof DEPLOYMENT_STATUS]
+ deploymentTriggerTime: string
+ deploymentEndTime: string
+ triggeredBy: string
+ deploymentStatusBreakdown: Partial>
+ errorBarConfig?: {
+ deploymentErrorMessage: string
+ nextTimelineToProcess: DeploymentStatusTimelineType
+ } | null
+}
+
export interface IntelligenceConfig {
clusterId: number
metadata: Record