Skip to content

Commit 2c80d53

Browse files
authored
Merge pull request #2718 from devtron-labs/feat/app-group-manage-traffic
feat: EnvironmentOverview - integrate ManageTraffic, BulkSelectionActionWidget
2 parents 5fd00b1 + 2b6e3c5 commit 2c80d53

File tree

14 files changed

+521
-325
lines changed

14 files changed

+521
-325
lines changed

.eslintignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ src/components/ApplicationGroup/Details/EnvCIDetails/EnvCIDetails.tsx
2424
src/components/ApplicationGroup/Details/EnvCIDetails/__test__/EnvCIDetails.test.tsx
2525
src/components/ApplicationGroup/Details/EnvironmentConfig/__test__/ApplicationRoutes.test.tsx
2626
src/components/ApplicationGroup/Details/EnvironmentConfig/__test__/EnvConfig.test.tsx
27-
src/components/ApplicationGroup/Details/EnvironmentOverview/EnvironmentOverview.tsx
2827
src/components/ApplicationGroup/Details/EnvironmentOverview/HibernateModal.tsx
2928
src/components/ApplicationGroup/Details/EnvironmentOverview/HibernateStatusListDrawer.tsx
3029
src/components/ApplicationGroup/Details/EnvironmentOverview/HibernateStatusRow.tsx

src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverview.constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,5 @@ export const EnvironmentOverviewTableHeaderValues: Record<keyof typeof Environme
4545
DEPLOYED_BY: 'DEPLOYED BY',
4646
STATUS: null,
4747
}
48+
49+
export const ENVIRONMENT_OVERVIEW_DRAG_SELECTOR_IDENTIFIER = 'environment-overview-drag-selector'
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*/
4+
5+
import { useEffect } from 'react'
6+
7+
import {
8+
Button,
9+
ButtonStyleType,
10+
ButtonVariantType,
11+
ComponentSizeType,
12+
DraggableButton,
13+
DraggablePositionVariant,
14+
DraggableWrapper,
15+
Icon,
16+
useRegisterShortcut,
17+
} from '@devtron-labs/devtron-fe-common-lib'
18+
19+
import { ENVIRONMENT_OVERVIEW_DRAG_SELECTOR_IDENTIFIER } from './EnvironmentOverview.constants'
20+
import { EnvironmentOverviewPopupMenu } from './EnvironmentOverviewPopupMenu'
21+
import { EnvironmentOverviewBulkSelectionWidgetProps } from './EnvironmentOverviewTable.types'
22+
23+
export const EnvironmentOverviewBulkSelectionWidget = ({
24+
count,
25+
onClose,
26+
parentRef,
27+
popUpMenuItems,
28+
children,
29+
}: EnvironmentOverviewBulkSelectionWidgetProps) => {
30+
const { registerShortcut, unregisterShortcut } = useRegisterShortcut()
31+
32+
useEffect(() => {
33+
registerShortcut({ keys: ['Escape'], callback: onClose })
34+
35+
return () => {
36+
unregisterShortcut(['Escape'])
37+
}
38+
}, [])
39+
40+
return (
41+
<DraggableWrapper
42+
dragSelector={`.${ENVIRONMENT_OVERVIEW_DRAG_SELECTOR_IDENTIFIER}`}
43+
positionVariant={DraggablePositionVariant.PARENT_BOTTOM_CENTER}
44+
zIndex="calc(var(--modal-index) - 1)"
45+
parentRef={parentRef}
46+
>
47+
<div className="bulk-selection-widget br-8 pl-7 pr-11 py-11 flex dc__gap-8">
48+
<DraggableButton dragClassName={ENVIRONMENT_OVERVIEW_DRAG_SELECTOR_IDENTIFIER} />
49+
<div className="flex dc__gap-8 fs-13 lh-20 fw-6">
50+
<span className="bcb-5 br-4 px-6 cn-0">{count}</span>
51+
<span className="cn-9">Selected</span>
52+
</div>
53+
<div className="w-1 h-20 bcb-1" />
54+
{children}
55+
{popUpMenuItems.length > 0 && <EnvironmentOverviewPopupMenu popUpMenuItems={popUpMenuItems} />}
56+
<div className="w-1 h-20 bcb-1" />
57+
<Button
58+
icon={<Icon name="ic-close-large" color={null} />}
59+
dataTestId="environment-overview-table-action-widget-close"
60+
style={ButtonStyleType.negativeGrey}
61+
variant={ButtonVariantType.borderLess}
62+
ariaLabel="Clear selection(s)"
63+
size={ComponentSizeType.small}
64+
onClick={onClose}
65+
showAriaLabelInTippy={false}
66+
/>
67+
</div>
68+
</DraggableWrapper>
69+
)
70+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// TODO: Replace this with ActionMenu component
2+
import {
3+
Button,
4+
ButtonStyleType,
5+
ButtonVariantType,
6+
ComponentSizeType,
7+
Icon,
8+
PopupMenu,
9+
} from '@devtron-labs/devtron-fe-common-lib'
10+
11+
import { EnvironmentOverviewTableRow } from './EnvironmentOverviewTable.types'
12+
13+
export const EnvironmentOverviewPopupMenu = ({
14+
popUpMenuItems,
15+
}: Pick<EnvironmentOverviewTableRow, 'popUpMenuItems'>) => (
16+
<PopupMenu autoClose>
17+
<PopupMenu.Button isKebab rootClassName="p-0 dc__no-border cursor">
18+
<Button
19+
icon={<Icon name="ic-more-vertical" color={null} />}
20+
dataTestId="environment-overview-pop-up-menu"
21+
style={ButtonStyleType.neutral}
22+
variant={ButtonVariantType.borderLess}
23+
ariaLabel="environment-overview-pop-up-menu"
24+
size={ComponentSizeType.xs}
25+
showAriaLabelInTippy={false}
26+
/>
27+
</PopupMenu.Button>
28+
<PopupMenu.Body rootClassName="dc__border py-4 w-180">
29+
{popUpMenuItems.map((popUpMenuItem) => {
30+
if ('label' in popUpMenuItem) {
31+
const { label, onClick, disabled, iconName } = popUpMenuItem
32+
33+
return (
34+
<button
35+
key={label}
36+
type="button"
37+
className={`dc__transparent w-100 py-6 px-8 flexbox dc__align-items-center dc__gap-8 ${disabled ? ' dc__opacity-0_5 cursor-not-allowed' : 'dc__hover-n50'}`}
38+
onClick={onClick}
39+
disabled={disabled}
40+
>
41+
{iconName && <Icon name={iconName} color="N800" />}
42+
<span className="dc__truncate cn-9 fs-13 lh-20">{label}</span>
43+
</button>
44+
)
45+
}
46+
47+
return popUpMenuItem
48+
})}
49+
</PopupMenu.Body>
50+
</PopupMenu>
51+
)

src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.component.tsx

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
getRandomColor,
2626
handleRelativeDateSorting,
2727
ImageChipCell,
28-
PopupMenu,
2928
processDeployedTime,
3029
RegistryType,
3130
SortableTableHeaderCell,
@@ -38,56 +37,18 @@ import {
3837
import { ReactComponent as ICActivity } from '@Icons/ic-activity.svg'
3938
import { ReactComponent as ICArrowLineDown } from '@Icons/ic-arrow-line-down.svg'
4039
import { ReactComponent as DevtronIcon } from '@Icons/ic-devtron-app.svg'
41-
import { ReactComponent as ICMoreOption } from '@Icons/ic-more-option.svg'
4240

4341
import {
4442
EnvironmentOverviewTableHeaderFixedKeys,
4543
EnvironmentOverviewTableHeaderValues,
4644
EnvironmentOverviewTableHeaderVariableKeys,
4745
EnvironmentOverviewTableSortableKeys,
4846
} from './EnvironmentOverview.constants'
49-
import {
50-
EnvironmentOverviewTableProps,
51-
EnvironmentOverviewTableRow,
52-
EnvironmentOverviewTableRowData,
53-
} from './EnvironmentOverviewTable.types'
47+
import { EnvironmentOverviewPopupMenu } from './EnvironmentOverviewPopupMenu'
48+
import { EnvironmentOverviewTableProps, EnvironmentOverviewTableRowData } from './EnvironmentOverviewTable.types'
5449

5550
import './EnvironmentOverviewTable.scss'
5651

57-
const renderPopUpMenu = (items: EnvironmentOverviewTableRow['popUpMenuItems']) => (
58-
<PopupMenu autoClose>
59-
<PopupMenu.Button isKebab rootClassName="p-4 flex dc__no-border cursor">
60-
<ICMoreOption className="icon-dim-16 fcn-6 rotateBy--90" />
61-
</PopupMenu.Button>
62-
<PopupMenu.Body rootClassName="dc__border py-4 w-180">
63-
{items.map((popUpMenuItem) => {
64-
if ('label' in popUpMenuItem) {
65-
const { label, onClick, disabled, Icon, iconType = 'fill' } = popUpMenuItem
66-
67-
return (
68-
<button
69-
key={label}
70-
type="button"
71-
className={`dc__transparent w-100 py-6 px-8 flexbox dc__align-items-center dc__gap-8 ${disabled ? ' dc__opacity-0_5 cursor-not-allowed' : 'dc__hover-n50'}`}
72-
onClick={onClick}
73-
disabled={disabled}
74-
>
75-
{Icon && (
76-
<Icon
77-
className={`icon-dim-16 ${iconType === 'fill' ? 'fcn-7' : ''} ${iconType === 'stroke' ? 'scn-7' : ''}`}
78-
/>
79-
)}
80-
<span className="dc__truncate cn-9 fs-13 lh-20">{label}</span>
81-
</button>
82-
)
83-
}
84-
85-
return popUpMenuItem
86-
})}
87-
</PopupMenu.Body>
88-
</PopupMenu>
89-
)
90-
9152
export const EnvironmentOverviewTable = ({
9253
rows = [],
9354
isVirtualEnv,
@@ -108,10 +69,10 @@ export const EnvironmentOverviewTable = ({
10869
() =>
10970
rows.sort((a, b) => {
11071
if (sortBy === EnvironmentOverviewTableSortableKeys.DEPLOYED_AT) {
111-
return handleRelativeDateSorting(a.environment.deployedAt, b.environment.deployedAt, sortOrder)
72+
return handleRelativeDateSorting(a.app.deployedAt, b.app.deployedAt, sortOrder)
11273
}
11374

114-
return stringComparatorBySortOrder(a.environment.name, b.environment.name, sortOrder)
75+
return stringComparatorBySortOrder(a.app.name, b.app.name, sortOrder)
11576
}),
11677
[rows, sortBy, sortOrder],
11778
)
@@ -202,15 +163,15 @@ export const EnvironmentOverviewTable = ({
202163
)
203164

204165
const renderRow = ({
205-
environment,
166+
app,
206167
isChecked,
207168
deployedAtLink,
208169
redirectLink,
209170
onCommitClick,
210171
onLastDeployedImageClick,
211172
popUpMenuItems = [],
212173
}: EnvironmentOverviewTableProps['rows'][0]) => {
213-
const { id, name, status, commits, deployedAt, deployedBy, deploymentStatus, lastDeployedImage } = environment
174+
const { id, name, status, commits, deployedAt, deployedBy, deploymentStatus, lastDeployedImage } = app
214175

215176
return (
216177
<div className={`environment-overview-table__row ${isChecked ? isCheckedRowClassName : ''}`}>
@@ -231,7 +192,7 @@ export const EnvironmentOverviewTable = ({
231192
{name}
232193
</Link>
233194
</Tooltip>
234-
{!!popUpMenuItems?.length && renderPopUpMenu(popUpMenuItems)}
195+
{!!popUpMenuItems?.length && <EnvironmentOverviewPopupMenu popUpMenuItems={popUpMenuItems} />}
235196
</div>
236197
</div>
237198
<div
@@ -283,7 +244,7 @@ export const EnvironmentOverviewTable = ({
283244
<div className="environment-overview-table dc__border br-4 bg__primary w-100">
284245
{renderHeaderRow()}
285246
{sortedRows.map((row) => (
286-
<Fragment key={row.environment.id}>{renderRow(row)}</Fragment>
247+
<Fragment key={row.app.id}>{renderRow(row)}</Fragment>
287248
))}
288249
</div>
289250
)

src/Pages/Shared/EnvironmentOverviewTable/EnvironmentOverviewTable.types.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { FunctionComponent, MouseEvent, ReactElement, SVGProps } from 'react'
17+
import { MouseEvent, MutableRefObject, PropsWithChildren, ReactElement } from 'react'
18+
19+
import { IconName } from '@devtron-labs/devtron-fe-common-lib'
1820

1921
import { EnvironmentOverviewTableHeaderKeys } from './EnvironmentOverview.constants'
2022

@@ -32,15 +34,14 @@ export interface EnvironmentOverviewTableRowData {
3234
export type EnvironmentOverviewTablePopUpMenuItem =
3335
| {
3436
label: string
35-
Icon?: FunctionComponent<SVGProps<SVGSVGElement>>
36-
iconType?: 'fill' | 'stroke'
37+
iconName?: IconName
3738
disabled?: boolean
3839
onClick?: (event: MouseEvent<HTMLButtonElement>) => void
3940
}
4041
| ReactElement
4142

4243
export interface EnvironmentOverviewTableRow {
43-
environment: EnvironmentOverviewTableRowData
44+
app: EnvironmentOverviewTableRowData
4445
isChecked?: boolean
4546
onLastDeployedImageClick: (event: MouseEvent<HTMLButtonElement>) => void
4647
onCommitClick: (event: MouseEvent<HTMLButtonElement>) => void
@@ -54,3 +55,11 @@ export interface EnvironmentOverviewTableProps {
5455
isVirtualEnv?: boolean
5556
onCheckboxSelect: (id: EnvironmentOverviewTableRowData['id'], isChecked: boolean, isAllChecked: boolean) => void
5657
}
58+
59+
export interface EnvironmentOverviewBulkSelectionWidgetProps
60+
extends PropsWithChildren<{
61+
count: number
62+
onClose: () => void
63+
parentRef: MutableRefObject<HTMLDivElement>
64+
}>,
65+
Pick<EnvironmentOverviewTableRow, 'popUpMenuItems'> {}

src/Pages/Shared/EnvironmentOverviewTable/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
*/
1616

1717
export { EnvironmentOverviewTableSortableKeys } from './EnvironmentOverview.constants'
18+
export * from './EnvironmentOverviewBulkSelectionActionWidget'
1819
export * from './EnvironmentOverviewTable.component'
1920
export * from './EnvironmentOverviewTable.types'
Lines changed: 20 additions & 0 deletions
Loading

src/components/ApplicationGroup/AppGroup.utils.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
CDWorkflowStatusType,
3636
CIWorkflowStatusType,
3737
ProcessWorkFlowStatusType,
38+
AppGroupListType,
3839
} from './AppGroup.types'
3940

4041
let timeoutId
@@ -335,3 +336,39 @@ export const getAppGroupDeploymentHistoryLink = (
335336
}
336337
return `${URLS.APP}/${appId}/${URLS.APP_CD_DETAILS}/${envId}/${pipelineId}${type ? `?type=${type}` : ''}`
337338
}
339+
340+
export const parseAppListData = (
341+
data: AppGroupListType,
342+
statusRecord: Record<string, { status: string; pipelineId: number }>,
343+
) => {
344+
const parsedData = {
345+
environment: data.environmentName,
346+
namespace: data.namespace || '-',
347+
cluster: data.clusterName,
348+
appInfoList: [],
349+
}
350+
351+
data?.apps?.forEach((app) => {
352+
const appInfo = {
353+
appId: app.appId,
354+
application: app.appName,
355+
appStatus: app.appStatus,
356+
deploymentStatus: statusRecord[app.appId].status,
357+
pipelineId: statusRecord[app.appId].pipelineId,
358+
lastDeployed: app.lastDeployedTime,
359+
lastDeployedBy: app.lastDeployedBy,
360+
lastDeployedImage: app.lastDeployedImage,
361+
commits: app.commits,
362+
ciArtifactId: app.ciArtifactId,
363+
}
364+
parsedData.appInfoList.push(appInfo)
365+
})
366+
367+
return parsedData
368+
}
369+
370+
export const getDeploymentHistoryLink = (appId: number, pipelineId: number, envId: string) =>
371+
`${URLS.APPLICATION_GROUP}/${envId}/cd-details/${appId}/${pipelineId}/`
372+
373+
export const getAppRedirectLink = (appId: number, envId: number) =>
374+
`${URLS.APPLICATION_GROUP}/${envId}${URLS.DETAILS}/${appId}`

0 commit comments

Comments
 (0)