Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
e50452b
feat: add ResourceConflictDeployDialog for handling resource ownershi…
AbhishekA1509 Dec 4, 2025
c9f566f
feat: implement ResourceConflictDetailsModal and ConflictedResourcesT…
AbhishekA1509 Dec 4, 2025
7f658c5
feat: enhance resource conflict handling with redeploy functionality …
AbhishekA1509 Dec 8, 2025
60936ef
feat: add resource conflict management components and enhance related…
AbhishekA1509 Dec 8, 2025
2236a28
feat: enhance ConflictedResourcesTable styling and improve resource c…
AbhishekA1509 Dec 8, 2025
13407fd
fix: update alignment of content in ResourceConflictDetailsModal
AbhishekA1509 Dec 8, 2025
ddf9e21
fix: disable deploy button when no resource conflict details are avai…
AbhishekA1509 Dec 8, 2025
b907e3f
fix: rename wfrId to wfrIdForDeploymentWithSpecificTrigger in redeplo…
AbhishekA1509 Dec 8, 2025
2bb1aed
fix: remove toast notifications from redeploy functions and update ar…
AbhishekA1509 Dec 8, 2025
d8f7911
fix: update resource conflict routes and payload structure for redepl…
AbhishekA1509 Dec 10, 2025
c4c7515
fix: update version to 1.21.0-beta-4 in package.json and package-lock…
AbhishekA1509 Dec 11, 2025
a45a097
fix: integrate useMainContext to conditionally display resource confl…
AbhishekA1509 Dec 12, 2025
87b59cc
fix: update version to 1.21.1 in package.json and package-lock.json
AbhishekA1509 Dec 12, 2025
4c73a76
fix: streamline loading state handling in ResourceConflictDeployDialo…
AbhishekA1509 Dec 12, 2025
0f1d57f
fix: update button styles to warning in TriggerDetails component
AbhishekA1509 Dec 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/Common/API/reactQueryHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,18 @@ import {
import { ServerErrors } from '@Common/ServerError'
import { ResponseType } from '@Common/Types'

export const useQuery = <TQueryFnData = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
options: UseQueryOptions<ResponseType<TQueryFnData>, ServerErrors, TData, TQueryKey>,
export const useQuery = <
TQueryFnData = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
WrapWithResponseType extends boolean = true,
>(
options: UseQueryOptions<
WrapWithResponseType extends true ? ResponseType<TQueryFnData> : TQueryFnData,
ServerErrors,
TData,
TQueryKey
>,
): UseQueryResult<TData, ServerErrors> => rqUseQuery(options)

export const useMutation = <TData = unknown, TVariables = void, TContext = unknown>(
Expand Down
1 change: 1 addition & 0 deletions src/Common/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export const ROUTES = {
LICENSE_DATA: 'license/data',
ENV: 'env',
APP_METADATA: 'app-metadata',
RESOURCE_CONFLICTS_LIST: 'app/template/helm-ownership-conflicts',
} as const

export enum KEY_VALUE {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[id*="table__resource-conflict-details"] {
.generic-table__row {
.generic-table__cell {
display: flex;
align-items: center;
padding: 10px 0;
}
}
}
50 changes: 50 additions & 0 deletions src/Shared/Components/CICDHistory/ConflictedResourcesTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useMemo } from 'react'

import { FiltersTypeEnum, PaginationEnum, Table, TableViewWrapperProps } from '../Table'
import { RowsType } from '../Table/types'
import { CONFLICTED_RESOURCES_COLUMNS } from './constants'
import { ConflictedResourcesTableProps, ResourceConflictItemType } from './types'

import './ConflictedResourcesTable.scss'

const Wrapper = ({ children }: TableViewWrapperProps<unknown, FiltersTypeEnum.STATE>) => (
<div className="dc__overflow-hidden flexbox-col flex-grow-1">{children}</div>
)
const filter = () => true
Comment thread
AbhishekA1509 marked this conversation as resolved.

const ConflictedResourcesTable = ({ resourceConflictDetails }: ConflictedResourcesTableProps) => {
const rows: RowsType<ResourceConflictItemType> = useMemo(
() =>
(resourceConflictDetails || []).map<RowsType<ResourceConflictItemType>[number]>((resource) => ({
data: resource,
id: resource.id,
})),
[resourceConflictDetails],
)

return (
<Table<ResourceConflictItemType, FiltersTypeEnum.STATE>
id="table__resource-conflict-details"
columns={CONFLICTED_RESOURCES_COLUMNS}
rows={rows}
emptyStateConfig={{
noRowsConfig: {
title: 'No resource found',
},
noRowsForFilterConfig: {
title: 'No results',
subTitle: "We couldn't find any matching results",
},
}}
paginationVariant={PaginationEnum.NOT_PAGINATED}
additionalFilterProps={{
initialSortKey: 'name' satisfies keyof ResourceConflictItemType,
}}
filtersVariant={FiltersTypeEnum.STATE}
ViewWrapper={Wrapper}
filter={filter}
/>
)
}

export default ConflictedResourcesTable
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useState } from 'react'
import { useHistory, useParams } from 'react-router-dom'

import { URLS } from '@Common/Constants'
import { showError } from '@Common/Helper'

import { ButtonStyleType } from '../Button'
import { ConfirmationModal, ConfirmationModalVariantType } from '../ConfirmationModal'
import { Icon } from '../Icon'
import { resourceConflictRedeploy } from './service'
import { ResourceConflictDeployDialogProps, ResourceConflictDeployDialogURLParamsType } from './types'

const ResourceConflictDeployDialog = ({ appName, environmentName, handleClose }: ResourceConflictDeployDialogProps) => {
const history = useHistory()
const { appId, envId, pipelineId, triggerId } = useParams<ResourceConflictDeployDialogURLParamsType>()
const [isLoading, setIsLoading] = useState(false)

const handleDeploy = async () => {
setIsLoading(true)
try {
await resourceConflictRedeploy({
pipelineId,
triggerId,
appId,
})
history.push(`${URLS.APP}/${appId}/details/${envId}`)
} catch (error) {
showError(error)
} finally {
setIsLoading(false)
}
}
Comment thread
AbhishekA1509 marked this conversation as resolved.

return (
<ConfirmationModal
variant={ConfirmationModalVariantType.custom}
Icon={<Icon name="ic-warning" color={null} size={48} />}
Comment thread
AbhishekA1509 marked this conversation as resolved.
title="Take resource ownership and redeploy"
subtitle={`Ensure the resources belong to the '${appName}' application and the '${environmentName}' environment to avoid destructive changes.`}
handleClose={handleClose}
buttonConfig={{
secondaryButtonConfig: {
text: 'Cancel',
onClick: handleClose,
},
primaryButtonConfig: {
isLoading,
text: 'Re-deploy',
onClick: handleDeploy,
style: ButtonStyleType.warning,
},
}}
/>
)
}

export default ResourceConflictDeployDialog
125 changes: 125 additions & 0 deletions src/Shared/Components/CICDHistory/ResourceConflictDetailsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { useState } from 'react'
import { useHistory, useParams } from 'react-router-dom'

import { useQuery } from '@Common/API'
import { URLS } from '@Common/Constants'
import { Drawer } from '@Common/Drawer'
import { showError, stopPropagation } from '@Common/Helper'
import { ComponentSizeType } from '@Shared/constants'

import { APIResponseHandler } from '../APIResponseHandler'
import { Button, ButtonStyleType, ButtonVariantType } from '../Button'
import { Icon } from '../Icon'
import ConflictedResourcesTable from './ConflictedResourcesTable'
import { getResourceConflictDetails, resourceConflictRedeploy } from './service'
import {
ResourceConflictDeployDialogURLParamsType,
ResourceConflictDetailsModalProps,
ResourceConflictItemType,
} from './types'

const ResourceConflictDetailsModal = ({ appName, environmentName, handleClose }: ResourceConflictDetailsModalProps) => {
const { appId, envId, pipelineId, triggerId } = useParams<ResourceConflictDeployDialogURLParamsType>()
const history = useHistory()

const {
isFetching: isLoadingResourceData,
data: resourceConflictDetails,
refetch: refetchResourceConflictDetails,
error: resourceConflictDetailsError,
} = useQuery<ResourceConflictItemType[], ResourceConflictItemType[], [string, string, string, string], false>({
queryKey: ['getResourceConflictDetails', pipelineId, triggerId, appId],
queryFn: ({ signal }) => getResourceConflictDetails({ pipelineId, triggerId, appId, signal }),
})

const [isDeploying, setIsDeploying] = useState(false)

const handleDeploy = async () => {
setIsDeploying(true)
try {
await resourceConflictRedeploy({
pipelineId,
triggerId,
appId,
})
history.push(`${URLS.APP}/${appId}/details/${envId}`)
} catch (error) {
showError(error)
} finally {
setIsDeploying(false)
}
}
Comment thread
AbhishekA1509 marked this conversation as resolved.

return (
<Drawer width="800px" onClose={handleClose} onEscape={handleClose} position="right">
<div
className="flexbox-col dc__content-space h-100 bg__modal--primary shadow__modal dc__overflow-auto"
onClick={stopPropagation}
>
<div className="flexbox-col dc__overflow-auto flex-grow-1">
<div className="px-20 py-12 flexbox dc__content-space dc__align-items-center border__primary--bottom">
<h2 className="m-0 fs-16 fw-6 lh-24 cn-9">Resources with conflict</h2>

<Button
dataTestId="header-close-button"
ariaLabel="Close"
showAriaLabelInTippy={false}
onClick={handleClose}
variant={ButtonVariantType.borderLess}
style={ButtonStyleType.negativeGrey}
icon={<Icon name="ic-close-large" color={null} />}
size={ComponentSizeType.xs}
/>
</div>

<div className="flexbox-col flex-grow-1 dc__overflow-auto w-100">
<APIResponseHandler
isLoading={isLoadingResourceData}
progressingProps={{
pageLoader: true,
}}
error={resourceConflictDetailsError}
errorScreenManagerProps={{
code: resourceConflictDetailsError?.code,
reload: refetchResourceConflictDetails,
on404Redirect: handleClose,
}}
>
<ConflictedResourcesTable resourceConflictDetails={resourceConflictDetails} />
</APIResponseHandler>
</div>
</div>

<div className="flexbox dc__align-items-center dc__content-space dc__gap-20 py-16 px-20 border__primary--top dc__no-shrink">
<div className="flexbox dc__gap-8">
<Icon name="ic-warning" size={20} color={null} />
Comment thread
AbhishekA1509 marked this conversation as resolved.
<div className="flexbox-col">
<span className="cn-9 fs-13 fw-6 lh-1-5">Take resource ownership and redeploy</span>
<span>
Ensure all resources strictly belong to the {appName} application and the&nbsp;
{environmentName}
environment. Any resource outside this Helm release may cause incorrect associations and
potentially destructive changes.
</span>
</div>
</div>
<div className="dc__no-shrink">
<Button
dataTestId="footer-redeploy-button"
variant={ButtonVariantType.primary}
style={ButtonStyleType.warning}
size={ComponentSizeType.large}
text="Re-deploy"
startIcon={<Icon name="ic-rocket-launch" color={null} />}
isLoading={isDeploying}
disabled={!resourceConflictDetails?.length}
onClick={handleDeploy}
/>
</div>
</div>
</div>
</Drawer>
)
}

export default ResourceConflictDetailsModal
53 changes: 28 additions & 25 deletions src/Shared/Components/CICDHistory/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ import {
HistorySummaryCardType,
SidebarType,
} from './types'
import { getHistoryItemStatusIconFromWorkflowStages, getTriggerStatusIcon, getWorkflowNodeStatusTitle } from './utils'
import {
getHistoryItemStatusIconFromWorkflowStages,
getSortedTriggerHistory,
getTriggerStatusIcon,
getWorkflowNodeStatusTitle,
} from './utils'

/**
* @description To be shown on deployment history or when we don't have workflowExecutionStages
Expand Down Expand Up @@ -378,30 +383,28 @@ const Sidebar = React.memo(
{fetchIdData === FetchIdDataStatus.SUCCESS && (
<ViewAllCardsTile handleViewAllHistory={handleViewAllHistory} />
)}
{Array.from(triggerHistory)
.sort(([a], [b]) => b - a)
.map(([triggerId, triggerDetails], index) => (
<HistorySummaryCard
dataTestId={`deployment-history-${index}`}
key={triggerId}
id={triggerId}
status={triggerDetails.status}
startedOn={triggerDetails.startedOn}
triggeredBy={triggerDetails.triggeredBy}
triggeredByEmail={triggerDetails.triggeredByEmail}
ciMaterials={triggerDetails.ciMaterials}
gitTriggers={triggerDetails.gitTriggers}
artifact={triggerDetails.artifact}
stage={triggerDetails.stage}
type={type}
runSource={triggerDetails.runSource}
renderRunSource={renderRunSource}
resourceId={resourceId}
workflowExecutionStages={triggerDetails.workflowExecutionStages}
podName={triggerDetails.podName}
namespace={triggerDetails.namespace}
/>
))}
{getSortedTriggerHistory(triggerHistory).map(([triggerId, triggerDetails], index) => (
<HistorySummaryCard
dataTestId={`deployment-history-${index}`}
key={triggerId}
id={triggerId}
status={triggerDetails.status}
startedOn={triggerDetails.startedOn}
triggeredBy={triggerDetails.triggeredBy}
triggeredByEmail={triggerDetails.triggeredByEmail}
ciMaterials={triggerDetails.ciMaterials}
gitTriggers={triggerDetails.gitTriggers}
artifact={triggerDetails.artifact}
stage={triggerDetails.stage}
type={type}
runSource={triggerDetails.runSource}
renderRunSource={renderRunSource}
resourceId={resourceId}
workflowExecutionStages={triggerDetails.workflowExecutionStages}
podName={triggerDetails.podName}
namespace={triggerDetails.namespace}
/>
))}
{hasMore && (fetchIdData === FetchIdDataStatus.SUSPEND || !fetchIdData) && (
<DetectBottom callback={reloadNextAfterBottom} />
)}
Expand Down
Loading