Skip to content

Commit 97c4dfe

Browse files
authored
Merge pull request #2866 from devtron-labs/feat/cluster-status
feat: add cluster status cell from fe-lib
2 parents 1ddc3e0 + 85a2380 commit 97c4dfe

File tree

12 files changed

+158
-88
lines changed

12 files changed

+158
-88
lines changed

src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterList.components.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FunctionComponent } from 'react'
1+
import { FunctionComponent, useEffect, useRef } from 'react'
22
import { generatePath, Link, useHistory, useLocation, useParams } from 'react-router-dom'
33

44
import {
@@ -18,6 +18,7 @@ import {
1818
noop,
1919
stopPropagation,
2020
TableCellComponentProps,
21+
TableSignalEnum,
2122
Tooltip,
2223
URLS as COMMON_URLS,
2324
} from '@devtron-labs/devtron-fe-common-lib'
@@ -200,7 +201,26 @@ export const ClusterListCellComponent: FunctionComponent<
200201
data: { clusterId, clusterName, clusterType, envCount, serverUrl, clusterCategory, isVirtualCluster, status },
201202
},
202203
isRowActive,
204+
signals,
203205
}: TableCellComponentProps<ClusterRowData, FiltersTypeEnum.STATE, {}>) => {
206+
const linkRef = useRef<HTMLAnchorElement>(null)
207+
208+
useEffect(() => {
209+
const handleEnter = ({ detail: { activeRowData } }) => {
210+
if (activeRowData.data.clusterId === clusterId) {
211+
linkRef.current?.click()
212+
}
213+
}
214+
215+
if (isRowActive) {
216+
signals.addEventListener(TableSignalEnum.ENTER_PRESSED, handleEnter)
217+
}
218+
219+
return () => {
220+
signals.removeEventListener(TableSignalEnum.ENTER_PRESSED, handleEnter)
221+
}
222+
}, [isRowActive])
223+
204224
switch (field) {
205225
case ClusterListFields.ICON:
206226
return (
@@ -211,6 +231,7 @@ export const ClusterListCellComponent: FunctionComponent<
211231
case ClusterListFields.CLUSTER_NAME:
212232
return (
213233
<Link
234+
ref={linkRef}
214235
to={getUrlWithSearchParams(URLS.GLOBAL_CONFIG_CLUSTER, {
215236
selectedTab: ClusterEnvTabs.ENVIRONMENTS,
216237
clusterId,

src/Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterList.tsx

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ const ClusterList = () => {
104104
initialSortKey: EnvListSortableKeys.ENV_NAME,
105105
})
106106

107+
const clearSearch = () => {
108+
handleSearch('')
109+
}
110+
107111
const [clusterListLoading, clusterListResult, clusterListError, reloadClusterList] = useAsync(
108112
getClusterList,
109113
[],
@@ -263,7 +267,7 @@ const ClusterList = () => {
263267
const handleChangeTab = (selectedSegment: OptionType<ClusterEnvTabs>) => {
264268
updateSearchParams({ selectedTab: selectedSegment.value, clusterId: null })
265269
if (searchKey) {
266-
handleSearch('')
270+
clearSearch()
267271
}
268272
if (showUnmappedEnvs) {
269273
setShowUnmappedEnvs(false)
@@ -288,6 +292,13 @@ const ClusterList = () => {
288292
}
289293

290294
if (isEnvironmentsView) {
295+
const allEnvsList = Object.values(clusterIdVsEnvMap).flat()
296+
297+
// In case no cluster is selected on env list page and no env is found, show global empty state
298+
if (!filterClusterId && allEnvsList.filter((env) => env.environmentName.includes(searchKey)).length === 0) {
299+
return <GenericFilterEmptyState handleClearFilters={clearSearch} />
300+
}
301+
291302
return (
292303
<EnvironmentList
293304
clusterIdVsEnvMap={clusterIdVsEnvMap}
@@ -303,24 +314,26 @@ const ClusterList = () => {
303314
}
304315

305316
if (searchKey && !filteredClusterList.length) {
306-
return <GenericFilterEmptyState handleClearFilters={() => handleSearch('')} />
317+
return <GenericFilterEmptyState handleClearFilters={clearSearch} />
307318
}
308319

309320
return (
310321
<>
311322
<ClusterMap isLoading={isClusterEnvListLoading} filteredList={filteredClusterList} />
312-
<Table<ClusterRowData, FiltersTypeEnum.STATE, {}>
313-
id="table__cluster-list"
314-
columns={tableColumns}
315-
rows={tableRows}
316-
filtersVariant={FiltersTypeEnum.STATE}
317-
paginationVariant={PaginationEnum.NOT_PAGINATED}
318-
emptyStateConfig={null}
319-
filter={() => true}
320-
additionalFilterProps={{
321-
initialSortKey: 'clusterName',
322-
}}
323-
/>
323+
<div className="cluster-table-wrapper">
324+
<Table<ClusterRowData, FiltersTypeEnum.STATE, {}>
325+
id="table__cluster-list"
326+
columns={tableColumns}
327+
rows={tableRows}
328+
filtersVariant={FiltersTypeEnum.STATE}
329+
paginationVariant={PaginationEnum.NOT_PAGINATED}
330+
emptyStateConfig={null}
331+
filter={() => true}
332+
additionalFilterProps={{
333+
initialSortKey: 'clusterName',
334+
}}
335+
/>
336+
</div>
324337
</>
325338
)
326339
}
@@ -378,6 +391,7 @@ const ClusterList = () => {
378391
}}
379392
handleEnter={handleSearch}
380393
size={ComponentSizeType.medium}
394+
keyboardShortcut="/"
381395
/>
382396
{ManageCategoryButton && <ManageCategoryButton search={search} />}
383397
<Button

src/Pages/GlobalConfigurations/ClustersAndEnvironments/EnvironmentList.tsx

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const ClustersEnvironmentsList = ({
4848
clusterDetails,
4949
environments,
5050
filterConfig: { sortBy, searchKey, sortOrder },
51+
filterClusterId,
5152
showUnmappedEnvs,
5253
setDeleteEnvConfig: setDeleteEnvId,
5354
setEditEnvConfig: setEditEnvId,
@@ -135,6 +136,26 @@ const ClustersEnvironmentsList = ({
135136
})
136137
}
137138

139+
const sortedFilteredList = namespaceEnvList
140+
.filter((env) => env.environmentName.includes(searchKey))
141+
.sort((a, b) => {
142+
switch (sortBy) {
143+
case EnvListSortableKeys.ENV_CATEGORY:
144+
return stringComparatorBySortOrder(a.category, b.category, sortOrder)
145+
case EnvListSortableKeys.ENV_NAMESPACE:
146+
return stringComparatorBySortOrder(a.namespace, b.namespace, sortOrder)
147+
case EnvListSortableKeys.ENV_TYPE:
148+
return stringComparatorBySortOrder(a.envType, b.envType, sortOrder)
149+
case EnvListSortableKeys.ENV_NAME:
150+
default:
151+
return environmentNameComparator(
152+
a.environmentName,
153+
b.environmentName,
154+
sortOrder || SortingOrder.ASC,
155+
)
156+
}
157+
})
158+
138159
const renderNamespaceEnvList = () => {
139160
if (namespaceListLoading) {
140161
return <ClusterEnvLoader />
@@ -144,26 +165,7 @@ const ClustersEnvironmentsList = ({
144165
return <GenericSectionErrorState reload={reloadNamespaces} />
145166
}
146167

147-
const sortedFilteredList = namespaceEnvList
148-
.filter((env) => env.environmentName.includes(searchKey))
149-
.sort((a, b) => {
150-
switch (sortBy) {
151-
case EnvListSortableKeys.ENV_CATEGORY:
152-
return stringComparatorBySortOrder(a.category, b.category, sortOrder)
153-
case EnvListSortableKeys.ENV_NAMESPACE:
154-
return stringComparatorBySortOrder(a.namespace, b.namespace, sortOrder)
155-
case EnvListSortableKeys.ENV_TYPE:
156-
return stringComparatorBySortOrder(a.envType, b.envType, sortOrder)
157-
case EnvListSortableKeys.ENV_NAME:
158-
default:
159-
return environmentNameComparator(
160-
a.environmentName,
161-
b.environmentName,
162-
sortOrder || SortingOrder.ASC,
163-
)
164-
}
165-
})
166-
168+
// Empty state when a particular cluster is selected
167169
if (searchKey && !sortedFilteredList.length) {
168170
return (
169171
<div className="p-16">
@@ -272,6 +274,11 @@ const ClustersEnvironmentsList = ({
272274
)
273275
}
274276

277+
// If no cluster is selected and no environments are found, return null
278+
if (!filterClusterId && !sortedFilteredList.length) {
279+
return null
280+
}
281+
275282
return (
276283
<>
277284
{/* Cluster metadata */}
@@ -449,6 +456,7 @@ const EnvironmentList = ({
449456
showUnmappedEnvs={showUnmappedEnvs}
450457
setDeleteEnvConfig={setDeleteEnvConfig}
451458
setEditEnvConfig={setEditEnvConfig}
459+
filterClusterId={filterClusterId}
452460
/>
453461
))}
454462
{deleteEnvConfig && (

src/Pages/GlobalConfigurations/ClustersAndEnvironments/cluster.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,14 @@
9696
.cluster-metadata-header {
9797
grid-template-columns: 24px 200px 200px 100px 100px 1fr;
9898
grid-column-gap: 16px;
99+
}
100+
101+
.cluster-table-wrapper {
102+
// Apply to every first child up to 4 levels deep
103+
> :first-child,
104+
> :first-child > :first-child,
105+
> :first-child > :first-child > :first-child,
106+
> :first-child > :first-child > :first-child > :first-child {
107+
overflow: visible !important;
108+
}
99109
}

src/Pages/GlobalConfigurations/ClustersAndEnvironments/cluster.type.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ export type DeleteEnvConfigType = Pick<EnvNamespaceRowType, 'envId' | 'clusterId
375375

376376
export type EditEnvConfigType = Pick<EnvNamespaceRowType, 'envId' | 'clusterId'> & { isVirtualCluster: boolean }
377377

378-
export interface ClusterEnvListProps extends Pick<EnvironmentListProps, 'filterConfig' | 'showUnmappedEnvs'> {
378+
export interface ClusterEnvListProps
379+
extends Pick<EnvironmentListProps, 'filterConfig' | 'showUnmappedEnvs' | 'filterClusterId'> {
379380
clusterDetails: Cluster
380381
environments: Environment[]
381382
setDeleteEnvConfig: Dispatch<SetStateAction<DeleteEnvConfigType>>

src/components/ClusterNodes/ClusterList/ClusterListRow.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@ import { ReactComponent as Error } from '@Icons/ic-error-exclamation.svg'
2121
import { importComponentFromFELibrary } from '@Components/common'
2222
import { getClusterChangeRedirectionUrl } from '@Components/ResourceBrowser/Utils'
2323

24-
import { ClusterMapInitialStatus } from '../ClusterMapInitialStatus'
2524
import { CLUSTER_PROD_TYPE } from '../constants'
25+
import { ClusterStatus } from './ClusterStatus'
2626
import { ClusterListRowTypes } from './types'
2727

2828
const CompareClusterButton = importComponentFromFELibrary('CompareClusterButton', null, 'function')
29-
const ClusterStatusCell = importComponentFromFELibrary('ClusterStatus', null, 'function')
3029
const KubeConfigButton = importComponentFromFELibrary('KubeConfigButton', null, 'function')
3130
const KubeConfigRowCheckbox = importComponentFromFELibrary('KubeConfigRowCheckbox', null, 'function')
3231

@@ -49,15 +48,11 @@ const ClusterListRow = ({
4948
}
5049

5150
const renderClusterStatus = ({ errorInNodeListing, status }: ClusterDetail) => {
52-
if (!status && !errorInNodeListing) {
51+
if (!status) {
5352
return null
5453
}
5554

56-
if (ClusterStatusCell && status) {
57-
return <ClusterStatusCell status={status} errorInNodeListing={errorInNodeListing} />
58-
}
59-
60-
return <ClusterMapInitialStatus errorInNodeListing={errorInNodeListing} />
55+
return <ClusterStatus status={status} errorInNodeListing={errorInNodeListing} />
6156
}
6257

6358
const isIdentifierSelected = !!bulkSelectionState[clusterData.name]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*/
4+
5+
import { StatusComponent, Tooltip } from '@devtron-labs/devtron-fe-common-lib'
6+
7+
import { ClusterStatusProps } from './types'
8+
import { getClusterStatus } from './utils'
9+
10+
export const ClusterStatus = ({ status, errorInNodeListing }: ClusterStatusProps) => (
11+
<Tooltip alwaysShowTippyOnHover={!!errorInNodeListing} content={errorInNodeListing} interactive>
12+
{/* This div is added to render the tooltip, otherwise it is not visible. */}
13+
<div className="flex left">
14+
<StatusComponent status={getClusterStatus(status)} hideIconTooltip message={status} />
15+
</div>
16+
</Tooltip>
17+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*/
4+
5+
export * from './ClusterStatus'
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*/
4+
5+
import { ClusterStatusType } from '@devtron-labs/devtron-fe-common-lib'
6+
7+
export interface ClusterStatusProps {
8+
status: ClusterStatusType
9+
errorInNodeListing?: string
10+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*/
4+
5+
import { ClusterStatusType, InstallationClusterStatus, StatusType } from '@devtron-labs/devtron-fe-common-lib'
6+
7+
export const getClusterStatus = (status: ClusterStatusType | InstallationClusterStatus) => {
8+
switch (status) {
9+
case InstallationClusterStatus.Installed:
10+
case InstallationClusterStatus.Updated:
11+
return StatusType.SUCCEEDED
12+
13+
case InstallationClusterStatus.Deleting:
14+
return StatusType.DELETING
15+
16+
case InstallationClusterStatus.Deleted:
17+
return 'deleted'
18+
19+
case InstallationClusterStatus.Creating:
20+
case InstallationClusterStatus.Updating:
21+
return StatusType.PROGRESSING
22+
23+
case ClusterStatusType.CONNECTION_FAILED:
24+
case InstallationClusterStatus.Failed:
25+
return StatusType.FAILED
26+
27+
case ClusterStatusType.HEALTHY:
28+
return StatusType.HEALTHY
29+
30+
default:
31+
return StatusType.DEGRADED
32+
}
33+
}

0 commit comments

Comments
 (0)