Skip to content

Commit 81bd2b8

Browse files
authored
Merge pull request #2714 from devtron-labs/feat/cluster-env-categories
feat: Global cluster & env categories
2 parents 9999ebf + 6c0abd5 commit 81bd2b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1082
-1022
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.15.1-pre-2",
7+
"@devtron-labs/devtron-fe-common-lib": "1.15.1-pre-4",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",
File renamed without changes.
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { useEffect, useState } from 'react'
18+
import { generatePath, Route, useHistory } from 'react-router-dom'
19+
20+
import {
21+
Button,
22+
ButtonComponentType,
23+
ComponentSizeType,
24+
ErrorScreenNotAuthorized,
25+
FeatureTitleWithInfo,
26+
Icon,
27+
Progressing,
28+
Reload,
29+
showError,
30+
sortCallback,
31+
URLS as CommonURLS,
32+
} from '@devtron-labs/devtron-fe-common-lib'
33+
34+
import { importComponentFromFELibrary } from '@Components/common'
35+
import { ViewType } from '@Config/constants'
36+
import { URLS } from '@Config/routes'
37+
import { ClusterEnvironmentDrawer } from '@Pages/GlobalConfigurations/ClustersAndEnvironments/ClusterEnvironmentDrawer'
38+
import CreateCluster from '@Pages/GlobalConfigurations/ClustersAndEnvironments/CreateCluster/CreateCluster.component'
39+
import { CreateClusterTypeEnum } from '@Pages/GlobalConfigurations/ClustersAndEnvironments/CreateCluster/types'
40+
41+
import { getClusterList, getEnvironmentList } from './cluster.service'
42+
import { ClusterMetadataTypes, ClusterProps } from './cluster.type'
43+
import { getSelectParsedCategory } from './cluster.util'
44+
import { ClusterList } from './ClusterList'
45+
46+
const ManageCategories = importComponentFromFELibrary('ManageCategories', null, 'function')
47+
const ManageCategoryButton = importComponentFromFELibrary('ManageCategoryButton', null, 'function')
48+
49+
const ClusterComponents = ({ isSuperAdmin }: ClusterProps) => {
50+
const [view, setView] = useState(ViewType.LOADING)
51+
const [clusters, setClusters] = useState<ClusterMetadataTypes[]>([])
52+
53+
const history = useHistory()
54+
55+
const initialize = () => {
56+
Promise.all([getClusterList(), window._env_.K8S_CLIENT ? { result: undefined } : getEnvironmentList()])
57+
.then(([clusterRes, envResponse]) => {
58+
const environments = envResponse.result || []
59+
const clusterEnvironmentMap = environments.reduce((agg, curr) => {
60+
const newAgg = { ...agg }
61+
newAgg[curr.cluster_id] = newAgg[curr.cluster_id] || []
62+
newAgg[curr.cluster_id].push(curr)
63+
return newAgg
64+
}, {})
65+
66+
let clustersList = clusterRes.result || []
67+
clustersList = clustersList.map((cluster) => ({
68+
...cluster,
69+
environments: clusterEnvironmentMap[cluster.id] || [],
70+
category: getSelectParsedCategory(cluster.category),
71+
}))
72+
73+
clustersList = clustersList.sort((a, b) => sortCallback('cluster_name', a, b))
74+
75+
setClusters(clustersList)
76+
setView(ViewType.FORM)
77+
})
78+
.catch((error) => {
79+
showError(error)
80+
setView(ViewType.ERROR)
81+
})
82+
}
83+
84+
const handleRedirectToClusterList = () => {
85+
history.push(URLS.GLOBAL_CONFIG_CLUSTER)
86+
}
87+
88+
useEffect(() => {
89+
if (isSuperAdmin) {
90+
initialize()
91+
}
92+
}, [isSuperAdmin])
93+
94+
if (!isSuperAdmin) {
95+
return (
96+
<div className="dc__align-reload-center">
97+
<ErrorScreenNotAuthorized />
98+
</div>
99+
)
100+
}
101+
102+
if (view === ViewType.LOADING) return <Progressing pageLoader />
103+
if (view === ViewType.ERROR) return <Reload className="dc__align-reload-center" />
104+
105+
const moduleBasedTitle = `Clusters${window._env_.K8S_CLIENT ? '' : ' and Environments'}`
106+
107+
return (
108+
<section className="global-configuration__component flex-1">
109+
<div data-testid="cluster_and_env_header" className="flexbox dc__content-space">
110+
<FeatureTitleWithInfo
111+
title={moduleBasedTitle}
112+
renderDescriptionContent={() => `Manage your organization’s ${moduleBasedTitle.toLowerCase()}.`}
113+
docLink="GLOBAL_CONFIG_CLUSTER"
114+
showInfoIconTippy
115+
additionalContainerClasses="mb-20"
116+
/>
117+
<div className="flexbox dc__gap-8">
118+
{ManageCategoryButton && <ManageCategoryButton />}
119+
<Button
120+
dataTestId="add_cluster_button"
121+
linkProps={{
122+
to: generatePath(URLS.GLOBAL_CONFIG_CREATE_CLUSTER, {
123+
type: CreateClusterTypeEnum.CONNECT_CLUSTER,
124+
}),
125+
}}
126+
component={ButtonComponentType.link}
127+
startIcon={<Icon name="ic-add" color="N700" />}
128+
size={ComponentSizeType.medium}
129+
text="New Cluster"
130+
/>
131+
</div>
132+
</div>
133+
134+
{clusters.map((cluster) => (
135+
<ClusterList
136+
reload={initialize}
137+
key={cluster.id || Math.random().toString(36).substr(2, 5)}
138+
clusterName={cluster.cluster_name}
139+
isVirtualCluster={cluster.isVirtualCluster}
140+
environments={cluster.environments}
141+
sshTunnelConfig={cluster.sshTunnelConfig}
142+
isProd={cluster.isProd}
143+
serverURL={cluster.server_url}
144+
prometheusURL={cluster.prometheus_url}
145+
prometheusAuth={cluster.prometheusAuth}
146+
proxyUrl={cluster.proxyUrl}
147+
insecureSkipTlsVerify={cluster.insecureSkipTlsVerify}
148+
installationId={cluster.installationId}
149+
category={cluster.category}
150+
toConnectWithSSHTunnel={cluster.toConnectWithSSHTunnel}
151+
clusterId={cluster.id}
152+
/>
153+
))}
154+
155+
{ManageCategories && (
156+
<Route path={CommonURLS.GLOBAL_CONFIG_MANAGE_CATEGORIES}>
157+
<ManageCategories />
158+
</Route>
159+
)}
160+
161+
<Route path={URLS.GLOBAL_CONFIG_CREATE_CLUSTER}>
162+
<CreateCluster handleReloadClusterList={initialize} />
163+
</Route>
164+
165+
<Route
166+
path={`${URLS.GLOBAL_CONFIG_CLUSTER}/:clusterName${URLS.CREATE_ENVIRONMENT}`}
167+
render={(props) => {
168+
const { clusterName } = props.match.params
169+
const foundCluster: ClusterMetadataTypes | { isVirtualCluster?: boolean; id?: null } =
170+
clusters.find((c) => c.cluster_name === clusterName) || {}
171+
const { isVirtualCluster, id: clusterId } = foundCluster
172+
173+
return (
174+
<ClusterEnvironmentDrawer
175+
reload={initialize}
176+
clusterName={clusterName}
177+
id={null}
178+
environmentName={null}
179+
clusterId={clusterId}
180+
namespace={null}
181+
isProduction={null}
182+
description={null}
183+
hideClusterDrawer={handleRedirectToClusterList}
184+
isVirtual={isVirtualCluster}
185+
category={null}
186+
/>
187+
)
188+
}}
189+
/>
190+
</section>
191+
)
192+
}
193+
194+
export default ClusterComponents

0 commit comments

Comments
 (0)