Skip to content

Commit b05150e

Browse files
Merge pull request #1323 from devtron-labs/app_details_env_filter
feat: App details env filter
2 parents 04f3a63 + 208f6b7 commit b05150e

File tree

16 files changed

+618
-312
lines changed

16 files changed

+618
-312
lines changed

src/components/ApplicationGroup/AppGroup.types.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ export interface BulkCIDetailType extends BulkTriggerAppDetailType {
3131
filteredCIPipelines: any
3232
}
3333

34-
export interface BulkCDDetailTypeResponse{
35-
bulkCDDetailType: BulkCDDetailType[],
36-
uniqueReleaseTags: string[],
34+
export interface BulkCDDetailTypeResponse {
35+
bulkCDDetailType: BulkCDDetailType[]
36+
uniqueReleaseTags: string[]
3737
}
3838

3939
export interface BulkCDDetailType extends BulkTriggerAppDetailType {
@@ -233,11 +233,7 @@ export interface AppOverridesType {
233233
setEnvironments: any
234234
}
235235

236-
export interface EnvHeaderType {
237-
envName: string
238-
setEnvName: (label: string) => void
239-
setShowEmpty: (empty: boolean) => void
240-
showEmpty: boolean
236+
export interface GroupFilterType {
241237
appListOptions: OptionType[]
242238
selectedAppList: MultiValue<OptionType>
243239
setSelectedAppList: React.Dispatch<React.SetStateAction<MultiValue<OptionType>>>
@@ -251,6 +247,13 @@ export interface EnvHeaderType {
251247
isSuperAdmin: boolean
252248
}
253249

250+
export interface EnvHeaderType extends GroupFilterType {
251+
envName: string
252+
setEnvName: (label: string) => void
253+
setShowEmpty: (empty: boolean) => void
254+
showEmpty: boolean
255+
}
256+
254257
export interface AppGroupAdminType {
255258
isSuperAdmin: boolean
256259
}
@@ -289,6 +292,7 @@ export interface AppGroupAppFilterContextType {
289292
openCreateGroup: (e, groupId?: string, _edit?: boolean) => void
290293
openDeleteGroup: (e, groupId: string, _delete?: boolean) => void
291294
isSuperAdmin: boolean
295+
filterParentType: FilterParentType
292296
}
293297

294298
export interface CreateGroupAppListType {
@@ -297,7 +301,7 @@ export interface CreateGroupAppListType {
297301
isSelected: boolean
298302
}
299303

300-
export interface CreateTypeOfAppListType{
304+
export interface CreateTypeOfAppListType {
301305
id: number
302306
appName: string
303307
}
@@ -345,7 +349,7 @@ export interface EnvGroupListType {
345349
description: string
346350
}
347351

348-
export interface CheckPermissionType{
352+
export interface CheckPermissionType {
349353
id?: number
350354
appIds: number[]
351355
name?: string
@@ -378,3 +382,8 @@ export interface SearchBarType {
378382
searchApplied: boolean
379383
setSearchApplied: React.Dispatch<React.SetStateAction<boolean>>
380384
}
385+
386+
export enum FilterParentType {
387+
app = 'app',
388+
env = 'env',
389+
}

src/components/ApplicationGroup/AppGroupAppFilter.components.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,27 @@ import { ReactComponent as InfoIcon } from '../../assets/icons/ic-info-outlined.
1010
import { ReactComponent as Edit } from '../../assets/icons/ic-pencil.svg'
1111
import { ReactComponent as Trash } from '../../assets/icons/ic-delete-interactive.svg'
1212
import { ReactComponent as CheckIcon } from '../../assets/icons/ic-check.svg'
13-
import { AppGroupAppFilterContextType } from './AppGroup.types'
13+
import { AppGroupAppFilterContextType, FilterParentType } from './AppGroup.types'
1414
import { AppFilterTabs } from './Constants'
1515
import { ConditionalWrap } from '@devtron-labs/devtron-fe-common-lib'
1616
import Tippy from '@tippyjs/react'
1717

1818
export const ValueContainer = (props): JSX.Element => {
19-
const { appListOptions, selectedAppList, selectedFilterTab, selectedGroupFilter }: AppGroupAppFilterContextType =
20-
useAppGroupAppFilterContext()
19+
const {
20+
appListOptions,
21+
selectedAppList,
22+
selectedFilterTab,
23+
selectedGroupFilter,
24+
filterParentType,
25+
}: AppGroupAppFilterContextType = useAppGroupAppFilterContext()
2126
let selectorText,
2227
selectedAppsLength = props.getValue().length
2328
if (selectedFilterTab === AppFilterTabs.GROUP_FILTER && selectedGroupFilter[0]) {
2429
selectorText = selectedGroupFilter[0]?.label
2530
} else {
2631
selectorText = `${selectedAppList.length > 0 ? selectedAppList.length : appListOptions.length}/${
2732
appListOptions.length
28-
} Applications`
33+
} ${filterParentType === FilterParentType.env ? 'Applications' : 'Environments'}`
2934
}
3035
return (
3136
<components.ValueContainer {...props}>
@@ -144,6 +149,7 @@ export const MenuList = (props: any): JSX.Element => {
144149
openCreateGroup,
145150
selectedGroupFilter,
146151
setSelectedGroupFilter,
152+
filterParentType,
147153
}: AppGroupAppFilterContextType = useAppGroupAppFilterContext()
148154
const clearSelection = (): void => {
149155
setSelectedAppList([])
@@ -152,6 +158,7 @@ export const MenuList = (props: any): JSX.Element => {
152158
const onTabChange = (e): void => {
153159
setSelectedFilterTab(e.currentTarget.dataset.selectedTab)
154160
}
161+
const selectedType = filterParentType === FilterParentType.env ? 'applications' : 'environments'
155162
return (
156163
<components.MenuList {...props}>
157164
<div className="dc__position-sticky dc__top-0 bcn-0">
@@ -183,7 +190,7 @@ export const MenuList = (props: any): JSX.Element => {
183190
selectedFilterTab === AppFilterTabs.APP_FILTER ? 'fw-6 active' : 'fw-4'
184191
}`}
185192
>
186-
<span>All applications </span>
193+
<span>All {selectedType} </span>
187194
</div>
188195
{selectedFilterTab === AppFilterTabs.APP_FILTER && <div className="apps-tab__active-tab" />}
189196
</li>
@@ -192,7 +199,7 @@ export const MenuList = (props: any): JSX.Element => {
192199
<div className="flex flex-justify dc__window-bg w-100 pt-6 pr-8 pb-6 pl-8">
193200
<span className="fs-12 fw-6 cn-9">
194201
Working with {selectedAppList?.length > 0 ? selectedAppList.length : appListOptions.length}/
195-
{appListOptions?.length} Applications
202+
{appListOptions?.length} {selectedType}
196203
</span>
197204
{selectedAppList?.length > 0 && selectedAppList.length !== appListOptions?.length && (
198205
<Clear className="icon-dim-16 mr-4 mw-18 cursor icon-n4" onClick={clearSelection} />
@@ -206,8 +213,8 @@ export const MenuList = (props: any): JSX.Element => {
206213
<InfoIcon className="icon-dim-20 mr-4 mw-18 cursor fcn-6 mb-4" />
207214
<div className="fs-13 fw-6 cn-9 mb-4">No saved filters</div>
208215
<div className="fs-12 fw-4 cn-7 dc__align-center ">
209-
To save a filter, select some applications from All applications and click on ‘Save selection as
210-
filter’
216+
To save a filter, select some {selectedType} from All {selectedType} and click on ‘Save
217+
selection as filter’
211218
</div>
212219
</div>
213220
)}

src/components/ApplicationGroup/AppGroupAppFilter.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState } from 'react'
22
import ReactSelect from 'react-select'
33
import { useAppGroupAppFilterContext } from './AppGroupDetailsRoute'
44
import { appGroupAppSelectorStyle } from './AppGroup.utils'
5-
import { AppGroupAppFilterContextType } from './AppGroup.types'
5+
import { AppGroupAppFilterContextType, FilterParentType } from './AppGroup.types'
66
import { AppFilterTabs } from './Constants'
77
import { MenuList, Option, ValueContainer } from './AppGroupAppFilter.components'
88

@@ -18,6 +18,7 @@ export default function AppGroupAppFilter() {
1818
groupFilterOptions,
1919
selectedGroupFilter,
2020
setSelectedGroupFilter,
21+
filterParentType
2122
}: AppGroupAppFilterContextType = useAppGroupAppFilterContext()
2223
const [appFilterAppInput, setAppFilterAppInput] = useState('')
2324
const [appFilterGroupInput, setAppFilterGroupInput] = useState('')
@@ -76,6 +77,13 @@ export default function AppGroupAppFilter() {
7677
}
7778
}
7879

80+
const getPlaceHolder = (): string => {
81+
if (selectedFilterTab === AppFilterTabs.APP_FILTER) {
82+
return `Search ${filterParentType === FilterParentType.env ? 'applications' : 'environments'}`
83+
}
84+
return 'Search filters'
85+
}
86+
7987
return (
8088
<ReactSelect
8189
menuIsOpen={isMenuOpen}
@@ -98,7 +106,7 @@ export default function AppGroupAppFilter() {
98106
ValueContainer: ValueContainer,
99107
MenuList: MenuList,
100108
}}
101-
placeholder={selectedFilterTab === AppFilterTabs.APP_FILTER ? 'Search applications' : 'Search filters'}
109+
placeholder={getPlaceHolder()}
102110
styles={appGroupAppSelectorStyle}
103111
onKeyDown={escHandler}
104112
/>

src/components/ApplicationGroup/AppGroupDetailsRoute.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
CheckPermissionType,
3232
CreateGroupAppListType,
3333
EnvHeaderType,
34+
FilterParentType,
3435
GroupOptionType,
3536
} from './AppGroup.types'
3637
import { MultiValue } from 'react-select'
@@ -43,7 +44,7 @@ import { CONTEXT_NOT_AVAILABLE_ERROR } from '../../config/constantMessaging'
4344
import { toast } from 'react-toastify'
4445
import CreateAppGroup from './CreateAppGroup'
4546

46-
const AppGroupAppFilterContext = React.createContext<AppGroupAppFilterContextType>(null)
47+
export const AppGroupAppFilterContext = React.createContext<AppGroupAppFilterContextType>(null)
4748

4849
export function useAppGroupAppFilterContext() {
4950
const context = React.useContext(AppGroupAppFilterContext)
@@ -445,6 +446,7 @@ export function EnvHeader({
445446
openCreateGroup,
446447
openDeleteGroup,
447448
isSuperAdmin,
449+
filterParentType: FilterParentType.env
448450
}),
449451
[
450452
appListOptions,

src/components/app/Overview/Overview.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from 'react'
1+
import React, { useEffect, useMemo, useState } from 'react'
22
import moment from 'moment'
33
import { Link, useParams } from 'react-router-dom'
44
import { ModuleNameMap, Moment12HourFormat, URLS } from '../../../config'
@@ -44,7 +44,7 @@ import { DEFAULT_ENV } from '../details/triggerView/Constants'
4444
import GenericDescription from '../../common/Description/GenericDescription'
4545
const MandatoryTagWarning = importComponentFromFELibrary('MandatoryTagWarning')
4646

47-
export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverview }: AppOverviewProps) {
47+
export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverview, filteredEnvIds }: AppOverviewProps) {
4848
const { appId } = useParams<{ appId: string }>()
4949
const [isLoading, setIsLoading] = useState(true)
5050
const [currentLabelTags, setCurrentLabelTags] = useState<TagType[]>([])
@@ -94,6 +94,18 @@ export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverv
9494
}
9595
}, [appId])
9696

97+
const envList = useMemo(() => {
98+
if (otherEnvsResult?.[0]?.result?.length > 0) {
99+
const filteredEnvMap = filteredEnvIds?.split(',').reduce((agg, curr) => agg.set(+curr, true), new Map())
100+
return (
101+
otherEnvsResult[0].result
102+
.filter((env) => !filteredEnvMap || filteredEnvMap.get(env.environmentId))
103+
?.sort((a, b) => (a.environmentName > b.environmentName ? 1 : -1)) || []
104+
)
105+
}
106+
return []
107+
}, [filteredEnvIds, otherEnvsResult])
108+
97109
const getExternalLinksDetails = (): void => {
98110
getExternalLinks(0, appId, ExternalLinkIdentifierType.DevtronApp)
99111
.then((externalLinksRes) => {
@@ -289,8 +301,7 @@ export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverv
289301

290302
const renderDeploymentComponent = () => {
291303

292-
if (otherEnvsResult?.[0]?.result?.length > 0) {
293-
otherEnvsResult[0].result.sort((a, b) => (a.environmentName > b.environmentName ? 1 : -1))
304+
if (envList.length > 0) {
294305
return (
295306
<div className="env-deployments-info-wrapper w-100">
296307
<div
@@ -304,7 +315,7 @@ export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverv
304315
</div>
305316

306317
<div className="env-deployments-info-body">
307-
{otherEnvsResult[0].result.map(
318+
{envList.map(
308319
(_env, index) =>
309320
!_env.deploymentAppDeleteRequest && (
310321
<div
@@ -383,7 +394,7 @@ export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverv
383394
)
384395
}
385396
}
386-
397+
387398
const renderWorkflowComponent = () => {
388399
if (!Array.isArray(jobPipelines) || !jobPipelines.length) {
389400
return (

0 commit comments

Comments
 (0)