Skip to content

Commit d5abb58

Browse files
feat: add Versions (#394)
1 parent e8a17a1 commit d5abb58

File tree

22 files changed

+1096
-36
lines changed

22 files changed

+1096
-36
lines changed

src/containers/Cluster/Cluster.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1+
import {useRouteMatch} from 'react-router';
12
import cn from 'bem-cn-lite';
2-
//@ts-ignore
3+
4+
import type {AdditionalVersionsProps} from '../../types/additionalProps';
5+
import routes, {CLUSTER_PAGES} from '../../routes';
6+
7+
import {ClusterInfo} from '../ClusterInfo/ClusterInfo';
38
import Tenants from '../Tenants/Tenants';
4-
//@ts-ignore
59
import {Nodes} from '../Nodes/Nodes';
6-
//@ts-ignore
710
import Storage from '../Storage/Storage';
8-
import routes, {CLUSTER_PAGES} from '../../routes';
911

1012
import './Cluster.scss';
11-
import {useRouteMatch} from 'react-router';
12-
import {ClusterInfo} from '../ClusterInfo/ClusterInfo';
1313

1414
const b = cn('cluster');
1515

1616
interface ClusterProps {
1717
additionalClusterInfo?: any;
1818
additionalTenantsInfo?: any;
1919
additionalNodesInfo?: any;
20+
additionalVersionsProps?: AdditionalVersionsProps;
2021
}
2122

2223
function Cluster(props: ClusterProps) {
@@ -35,7 +36,12 @@ function Cluster(props: ClusterProps) {
3536
return <Storage {...props} />;
3637
}
3738
case CLUSTER_PAGES.cluster.id: {
38-
return <ClusterInfo additionalClusterInfo={props.additionalClusterInfo} />;
39+
return (
40+
<ClusterInfo
41+
additionalClusterInfo={props.additionalClusterInfo}
42+
additionalVersionsProps={props.additionalVersionsProps}
43+
/>
44+
);
3945
}
4046
default: {
4147
return null;

src/containers/ClusterInfo/ClusterInfo.scss

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
11
@import '../../styles/mixins';
22

33
.cluster-info {
4+
$_: &;
5+
$progress_width: 100%;
6+
7+
display: flex;
8+
flex: 1 1 auto;
9+
flex-direction: column;
10+
411
width: 100%;
12+
13+
font-size: var(--yc-text-body-2-font-size);
14+
line-height: var(--yc-text-body-2-line-height);
15+
16+
&__header {
17+
padding: 20px;
18+
19+
border-bottom: 1px solid var(--yc-color-line-generic);
20+
background: var(--yc-color-base-background);
21+
}
22+
523
&__loader {
624
display: flex;
725
justify-content: center;
@@ -69,4 +87,25 @@
6987

7088
margin-left: 5px;
7189
}
90+
&__progress-label {
91+
margin: 0 10px 0 0;
92+
93+
font-weight: 200;
94+
}
95+
96+
&__version-progress {
97+
display: flex;
98+
align-items: center;
99+
100+
width: $progress_width;
101+
margin-top: 20px;
102+
103+
& .yc-progress {
104+
width: $progress_width;
105+
}
106+
}
107+
108+
& .yc-progress__stack {
109+
cursor: pointer;
110+
}
72111
}

src/containers/ClusterInfo/ClusterInfo.tsx

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import React, {useCallback, useEffect} from 'react';
1+
import {useCallback, useEffect, useMemo} from 'react';
22
import {useDispatch} from 'react-redux';
33
import {useLocation} from 'react-router';
44
import block from 'bem-cn-lite';
55
import qs from 'qs';
66

7-
import {Link} from '@gravity-ui/uikit';
7+
import {Link, Progress} from '@gravity-ui/uikit';
88

99
import EntityStatus from '../../components/EntityStatus/EntityStatus';
1010
import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
@@ -15,14 +15,19 @@ import {Icon} from '../../components/Icon';
1515
import {Loader} from '../../components/Loader';
1616
import {ResponseError} from '../../components/Errors/ResponseError';
1717

18+
import type {AdditionalVersionsProps} from '../../types/additionalProps';
1819
import {hideTooltip, showTooltip} from '../../store/reducers/tooltip';
1920
import {getClusterInfo} from '../../store/reducers/cluster/cluster';
21+
import {getClusterNodes} from '../../store/reducers/clusterNodes/clusterNodes';
2022
import {backend, customBackend} from '../../store';
2123
import {setHeader} from '../../store/reducers/header';
2224
import {formatStorageValues} from '../../utils';
2325
import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
26+
import {parseVersionsToVersionToColorMap, parseNodesToVersionsValues} from '../../utils/versions';
2427
import routes, {CLUSTER_PAGES, createHref} from '../../routes';
2528

29+
import {Versions} from '../Versions/Versions';
30+
2631
import {compareTablets} from './utils';
2732

2833
import './ClusterInfo.scss';
@@ -32,9 +37,14 @@ const b = block('cluster-info');
3237
interface ClusterInfoProps {
3338
clusterTitle?: string;
3439
additionalClusterInfo?: InfoViewerItem[];
40+
additionalVersionsProps?: AdditionalVersionsProps;
3541
}
3642

37-
export const ClusterInfo = ({clusterTitle, additionalClusterInfo = []}: ClusterInfoProps) => {
43+
export const ClusterInfo = ({
44+
clusterTitle,
45+
additionalClusterInfo = [],
46+
additionalVersionsProps,
47+
}: ClusterInfoProps) => {
3848
const dispatch = useDispatch();
3949
const location = useLocation();
4050

@@ -44,6 +54,12 @@ export const ClusterInfo = ({clusterTitle, additionalClusterInfo = []}: ClusterI
4454
const {clusterName} = queryParams;
4555

4656
const {data: cluster, loading, wasLoaded, error} = useTypedSelector((state) => state.cluster);
57+
const {
58+
nodes,
59+
loading: nodesLoading,
60+
wasLoaded: nodesWasLoaded,
61+
error: nodesError,
62+
} = useTypedSelector((state) => state.clusterNodes);
4763
const singleClusterMode = useTypedSelector((state) => state.singleClusterMode);
4864

4965
useEffect(() => {
@@ -59,10 +75,22 @@ export const ClusterInfo = ({clusterTitle, additionalClusterInfo = []}: ClusterI
5975

6076
const fetchData = useCallback(() => {
6177
dispatch(getClusterInfo(clusterName ? String(clusterName) : undefined));
78+
dispatch(getClusterNodes());
6279
}, [dispatch, clusterName]);
6380

6481
useAutofetcher(fetchData, [fetchData], true);
6582

83+
const versionToColor = useMemo(() => {
84+
if (additionalVersionsProps?.getVersionToColorMap) {
85+
return additionalVersionsProps?.getVersionToColorMap();
86+
}
87+
return parseVersionsToVersionToColorMap(cluster?.Versions);
88+
}, [additionalVersionsProps, cluster]);
89+
90+
const versionsValues = useMemo(() => {
91+
return parseNodesToVersionsValues(nodes, versionToColor);
92+
}, [nodes, versionToColor]);
93+
6694
const onShowTooltip = (...args: Parameters<typeof showTooltip>) => {
6795
dispatch(showTooltip(...args));
6896
};
@@ -128,39 +156,52 @@ export const ClusterInfo = ({clusterTitle, additionalClusterInfo = []}: ClusterI
128156
return info;
129157
};
130158

131-
if (loading && !wasLoaded) {
159+
if ((loading && !wasLoaded) || (nodesLoading && !nodesWasLoaded)) {
132160
return <Loader size="l" />;
133161
}
134162

135-
if (error) {
136-
return <ResponseError error={error} />;
163+
if (error || nodesError) {
164+
return <ResponseError error={error || nodesError} />;
137165
}
138-
return (
139-
<React.Fragment>
140-
<div className={b('common')}>
141-
<div className={b('url')}>
142-
<EntityStatus
143-
size="m"
144-
status={cluster?.Overall}
145-
name={clusterTitle ?? cluster?.Name ?? 'Unknown cluster'}
146-
/>
147-
</div>
148-
149-
{cluster?.DataCenters && <Tags tags={cluster?.DataCenters} />}
150166

151-
<div className={b('system-tablets')}>
152-
{cluster?.SystemTablets &&
153-
cluster.SystemTablets.sort(compareTablets).map((tablet, tabletIndex) => (
154-
<Tablet
155-
onMouseEnter={onShowTooltip}
156-
onMouseLeave={onHideTooltip}
157-
key={tabletIndex}
158-
tablet={tablet}
167+
return (
168+
<div className={b()}>
169+
<div className={b('header')}>
170+
<div className="info">
171+
<div className={b('common')}>
172+
<div className={b('url')}>
173+
<EntityStatus
174+
size="m"
175+
status={cluster?.Overall}
176+
name={clusterTitle ?? cluster?.Name ?? 'Unknown cluster'}
159177
/>
160-
))}
178+
</div>
179+
180+
{cluster?.DataCenters && <Tags tags={cluster?.DataCenters} />}
181+
182+
<div className={b('system-tablets')}>
183+
{cluster?.SystemTablets &&
184+
cluster.SystemTablets.sort(compareTablets).map(
185+
(tablet, tabletIndex) => (
186+
<Tablet
187+
onMouseEnter={onShowTooltip}
188+
onMouseLeave={onHideTooltip}
189+
key={tabletIndex}
190+
tablet={tablet}
191+
/>
192+
),
193+
)}
194+
</div>
195+
</div>
196+
<InfoViewer dots={true} info={getInfo()} />
197+
</div>
198+
<div className={b('version-progress')}>
199+
<h3 className={b('progress-label')}>Versions</h3>
200+
<Progress value={100} stack={versionsValues} />
161201
</div>
162202
</div>
163-
<InfoViewer dots={true} info={getInfo()} />
164-
</React.Fragment>
203+
204+
<Versions nodes={nodes} versionToColor={versionToColor} />
205+
</div>
165206
);
166207
};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@import '../../../styles/mixins.scss';
2+
3+
.ydb-versions-grouped-node-tree {
4+
$item-width: 100%;
5+
$margin-size: 24px;
6+
7+
&_first-level {
8+
margin-top: 10px;
9+
margin-bottom: 10px;
10+
11+
border: 1px solid var(--yc-color-line-generic);
12+
border-radius: 10px;
13+
}
14+
15+
&__dt-wrapper {
16+
position: relative;
17+
z-index: 0;
18+
19+
overflow-x: auto;
20+
21+
margin-right: $margin-size;
22+
margin-left: $margin-size;
23+
24+
@include freeze-nth-column(1);
25+
@include freeze-nth-column(2, 80px);
26+
27+
@include table-styles;
28+
}
29+
30+
.ydb-tree-view {
31+
font-size: var(--yc-text-body-2-font-size);
32+
line-height: var(--yc-text-body-2-line-height);
33+
34+
// Apply margin ignoring first element of the tree
35+
.ydb-tree-view {
36+
margin-left: $margin-size;
37+
}
38+
}
39+
40+
& .tree-view_item {
41+
height: 40px;
42+
margin: 0;
43+
44+
// By default tree is rendered with padding calculated based on level
45+
// We replace padding with margin for correct hover
46+
padding: 0 10px !important;
47+
48+
border: 0;
49+
border-radius: 10px;
50+
}
51+
52+
& .tree-view_children .tree-view_item {
53+
width: $item-width;
54+
}
55+
56+
& .yc-progress__stack {
57+
cursor: pointer;
58+
}
59+
}

0 commit comments

Comments
 (0)