Skip to content

Commit 69b4b4e

Browse files
feat(Clusters): allow add, edit and delete cluster funcs (#2369)
1 parent 34ea140 commit 69b4b4e

File tree

21 files changed

+245
-306
lines changed

21 files changed

+245
-306
lines changed

src/containers/App/Content.tsx

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import {lazyComponent} from '../../utils/lazyComponent';
2727
import Authentication from '../Authentication/Authentication';
2828
import {getClusterPath} from '../Cluster/utils';
2929
import Header from '../Header/Header';
30-
import type {RawBreadcrumbItem} from '../Header/breadcrumbs';
3130

3231
import {
3332
ClusterSlot,
@@ -41,7 +40,6 @@ import {
4140
TenantSlot,
4241
VDiskPageSlot,
4342
} from './appSlots';
44-
import i18n from './i18n';
4543

4644
import './App.scss';
4745

@@ -147,26 +145,22 @@ export function Content(props: ContentProps) {
147145
const redirectProps: RedirectProps =
148146
redirect?.props ?? (singleClusterMode ? {to: getClusterPath()} : {to: routes.clusters});
149147

150-
let mainPage: RawBreadcrumbItem | undefined;
151-
if (!singleClusterMode) {
152-
mainPage = {text: i18n('pages.clusters'), link: routes.clusters};
153-
}
154-
155148
return (
156149
<Switch>
157-
{singleClusterMode
158-
? null
159-
: renderRouteSlot(slots, {
160-
path: routes.clusters,
161-
exact: true,
162-
component: Clusters,
163-
slot: ClustersSlot,
164-
})}
165150
{additionalRoutes?.rendered}
166-
{/* Single cluster routes */}
167-
<Route key="single-cluster">
168-
<Header mainPage={mainPage} />
151+
<Route>
152+
<Header />
169153
<Switch>
154+
{singleClusterMode
155+
? null
156+
: renderRouteSlot(slots, {
157+
path: routes.clusters,
158+
exact: true,
159+
component: Clusters,
160+
slot: ClustersSlot,
161+
wrapper: GetMetaCapabilities,
162+
})}
163+
{/* Single cluster routes */}
170164
{routesSlots.map((route) => {
171165
return renderRouteSlot(slots, route);
172166
})}
@@ -226,6 +220,20 @@ function GetCapabilities({children}: {children: React.ReactNode}) {
226220
);
227221
}
228222

223+
// Only for Clusters page, there is no need to request cluster capabilities there (GetCapabilities)
224+
// This wrapper is not used in GetCapabilities so the page does not wait for 2 consecutive capabilities requests
225+
function GetMetaCapabilities({children}: {children: React.ReactNode}) {
226+
useMetaCapabilitiesQuery();
227+
// It is always true if there is no meta, since request finishes with an error
228+
const metaCapabilitiesLoaded = useMetaCapabilitiesLoaded();
229+
230+
return (
231+
<LoaderWrapper loading={!metaCapabilitiesLoaded} size="l">
232+
{children}
233+
</LoaderWrapper>
234+
);
235+
}
236+
229237
interface ContentWrapperProps {
230238
singleClusterMode: boolean;
231239
isAuthenticated: boolean;

src/containers/App/i18n/en.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/containers/App/i18n/index.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/containers/App/i18n/ru.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/containers/Clusters/Clusters.scss

Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@
22

33
.clusters {
44
overflow: auto;
5+
gap: var(--g-spacing-4);
56

6-
padding-top: 15px;
7+
padding: var(--g-spacing-4) var(--g-spacing-5) 0;
78

89
@include mixins.body-2-typography();
910
@include mixins.flex-container();
11+
1012
&__autorefresh {
1113
margin-left: auto;
1214
}
13-
&__cluster {
14-
display: flex;
15-
align-items: center;
16-
}
1715
&__cluster-status {
1816
width: 18px;
1917
height: 18px;
@@ -63,11 +61,6 @@
6361
white-space: normal;
6462
}
6563

66-
&__controls {
67-
display: flex;
68-
69-
margin-bottom: 20px;
70-
}
7164
&__control {
7265
width: 200px;
7366
margin-right: 15px;
@@ -87,48 +80,6 @@
8780
transition: none;
8881
}
8982

90-
&__aggregation,
91-
&__controls {
92-
margin-right: 15px;
93-
margin-left: 15px;
94-
}
95-
96-
&__aggregation {
97-
display: flex;
98-
align-items: center;
99-
100-
width: max-content;
101-
height: 46px;
102-
margin-bottom: 20px;
103-
padding: 10px 20px;
104-
105-
border: 1px solid var(--g-color-line-generic);
106-
border-radius: 10px;
107-
background: var(--g-color-base-generic-ultralight);
108-
}
109-
110-
&__aggregation-value-container {
111-
display: flex;
112-
align-items: center;
113-
114-
max-width: 230px;
115-
116-
font-size: var(--g-text-subheader-3-font-size);
117-
line-height: var(--g-text-subheader-3-line-height);
118-
}
119-
120-
&__aggregation-value-container:not(:last-child) {
121-
margin-right: 30px;
122-
}
123-
124-
&__aggregation-label {
125-
margin-right: 8px;
126-
127-
font-weight: 200;
128-
129-
color: var(--g-color-text-complementary);
130-
}
131-
13283
&__text {
13384
color: var(--g-color-text-primary);
13485
@include mixins.body-2-typography();
@@ -147,7 +98,6 @@
14798
&__table-wrapper {
14899
overflow: auto;
149100

150-
padding-left: 5px;
151101
@include mixins.flex-container();
152102
}
153103

@@ -186,4 +136,8 @@
186136
margin-left: 15px;
187137
@include mixins.body-2-typography();
188138
}
139+
140+
&__remove-cluster {
141+
color: var(--ydb-color-status-red);
142+
}
189143
}

src/containers/Clusters/Clusters.tsx

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
11
import React from 'react';
22

33
import DataTable from '@gravity-ui/react-data-table';
4-
import {Select, TableColumnSetup} from '@gravity-ui/uikit';
4+
import {Flex, Select, TableColumnSetup, Text} from '@gravity-ui/uikit';
55
import {Helmet} from 'react-helmet-async';
66

77
import {AutoRefreshControl} from '../../components/AutoRefreshControl/AutoRefreshControl';
88
import {ResponseError} from '../../components/Errors/ResponseError';
99
import {Loader} from '../../components/Loader';
1010
import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
1111
import {Search} from '../../components/Search';
12+
import {
13+
useDeleteClusterFeatureAvailable,
14+
useEditClusterFeatureAvailable,
15+
} from '../../store/reducers/capabilities/hooks';
1216
import {changeClustersFilters, clustersApi} from '../../store/reducers/clusters/clusters';
1317
import {
14-
aggregateClustersInfo,
1518
filterClusters,
1619
selectClusterNameFilter,
1720
selectServiceFilter,
1821
selectStatusFilter,
1922
selectVersionFilter,
2023
} from '../../store/reducers/clusters/selectors';
24+
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
25+
import {uiFactory} from '../../uiFactory/uiFactory';
2126
import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
2227
import {useAutoRefreshInterval, useTypedDispatch, useTypedSelector} from '../../utils/hooks';
2328
import {useSelectedColumns} from '../../utils/hooks/useSelectedColumns';
2429
import {getMinorVersion} from '../../utils/versions';
2530

26-
import {ClustersStatistics} from './ClustersStatistics';
27-
import {CLUSTERS_COLUMNS, CLUSTERS_COLUMNS_WIDTH_LS_KEY} from './columns';
31+
import {CLUSTERS_COLUMNS_WIDTH_LS_KEY, getClustersColumns} from './columns';
2832
import {
2933
CLUSTERS_SELECTED_COLUMNS_KEY,
3034
COLUMNS_NAMES,
@@ -44,6 +48,15 @@ export function Clusters() {
4448

4549
const dispatch = useTypedDispatch();
4650

51+
React.useEffect(() => {
52+
dispatch(setHeaderBreadcrumbs('clusters', {}));
53+
}, [dispatch]);
54+
55+
const isEditClusterAvailable =
56+
useEditClusterFeatureAvailable() && uiFactory.onEditCluster !== undefined;
57+
const isDeleteClusterAvailable =
58+
useDeleteClusterFeatureAvailable() && uiFactory.onDeleteCluster !== undefined;
59+
4760
const clusterName = useTypedSelector(selectClusterNameFilter);
4861
const status = useTypedSelector(selectStatusFilter);
4962
const service = useTypedSelector(selectServiceFilter);
@@ -62,8 +75,12 @@ export function Clusters() {
6275
dispatch(changeClustersFilters({version: value}));
6376
};
6477

78+
const rawColumns = React.useMemo(() => {
79+
return getClustersColumns({isEditClusterAvailable, isDeleteClusterAvailable});
80+
}, [isDeleteClusterAvailable, isEditClusterAvailable]);
81+
6582
const {columnsToShow, columnsToSelect, setColumns} = useSelectedColumns(
66-
CLUSTERS_COLUMNS,
83+
rawColumns,
6784
CLUSTERS_SELECTED_COLUMNS_KEY,
6885
COLUMNS_TITLES,
6986
DEFAULT_COLUMNS,
@@ -99,11 +116,6 @@ export function Clusters() {
99116
return filterClusters(clusters ?? [], {clusterName, status, service, version});
100117
}, [clusterName, clusters, service, status, version]);
101118

102-
const aggregation = React.useMemo(
103-
() => aggregateClustersInfo(filteredClusters),
104-
[filteredClusters],
105-
);
106-
107119
const statuses = React.useMemo(() => {
108120
return Array.from(
109121
new Set(
@@ -114,14 +126,29 @@ export function Clusters() {
114126
.map((el) => ({value: el, content: el}));
115127
}, [clusters]);
116128

129+
const renderPageTitle = () => {
130+
return (
131+
<Flex justifyContent="space-between">
132+
<Flex gap={2}>
133+
<Text variant="header-1">{i18n('page_title')}</Text>
134+
<Text variant="header-1" color="secondary">
135+
{clusters?.length}
136+
</Text>
137+
</Flex>
138+
<AutoRefreshControl className={b('autorefresh')} />
139+
</Flex>
140+
);
141+
};
142+
117143
return (
118144
<div className={b()}>
119145
<Helmet>
120146
<title>{i18n('page_title')}</title>
121147
</Helmet>
122148

123-
<ClustersStatistics stats={aggregation} count={filteredClusters.length} />
124-
<div className={b('controls')}>
149+
{renderPageTitle()}
150+
151+
<Flex>
125152
<div className={b('control', {wide: true})}>
126153
<Search
127154
placeholder={i18n('controls_search-placeholder')}
@@ -178,8 +205,7 @@ export function Clusters() {
178205
sortable={false}
179206
/>
180207
</div>
181-
<AutoRefreshControl className={b('autorefresh')} />
182-
</div>
208+
</Flex>
183209
{query.isError ? <ResponseError error={query.error} className={b('error')} /> : null}
184210
{query.isLoading ? <Loader size="l" /> : null}
185211
{query.fulfilledTimeStamp ? (

src/containers/Clusters/ClustersStatistics.tsx

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)