ydb-embedded-ui 1.8.8 → 1.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.scss +43 -0
  3. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +53 -0
  4. package/dist/components/BasicNodeViewer/index.ts +1 -0
  5. package/dist/components/EntityStatus/EntityStatus.js +15 -3
  6. package/dist/components/FullNodeViewer/FullNodeViewer.js +29 -48
  7. package/dist/components/FullNodeViewer/FullNodeViewer.scss +0 -45
  8. package/dist/components/IndexInfoViewer/IndexInfoViewer.tsx +52 -0
  9. package/dist/components/InfoViewer/index.ts +4 -0
  10. package/dist/components/InfoViewer/utils.ts +32 -0
  11. package/dist/components/ProgressViewer/ProgressViewer.js +1 -1
  12. package/dist/containers/Node/Node.scss +5 -1
  13. package/dist/containers/Node/Node.tsx +7 -1
  14. package/dist/containers/Node/NodeOverview/NodeOverview.tsx +1 -3
  15. package/dist/containers/Node/NodeStructure/NodeStructure.scss +30 -1
  16. package/dist/containers/Node/NodeStructure/PDiskTitleBadge.tsx +25 -0
  17. package/dist/containers/Node/NodeStructure/Pdisk.tsx +24 -2
  18. package/dist/containers/Nodes/Nodes.js +1 -0
  19. package/dist/containers/Storage/Pdisk/Pdisk.tsx +25 -33
  20. package/dist/containers/Storage/Vdisk/Vdisk.js +2 -0
  21. package/dist/containers/Tablet/Tablet.js +2 -2
  22. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +24 -14
  23. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +3 -3
  24. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +24 -3
  25. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +80 -10
  26. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +20 -16
  27. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -0
  28. package/dist/containers/Tenant/utils/schema.ts +73 -28
  29. package/dist/containers/Tenant/utils/schemaActions.ts +45 -32
  30. package/dist/services/api.js +13 -8
  31. package/dist/store/reducers/executeQuery.js +1 -1
  32. package/dist/store/reducers/executeTopQueries.js +1 -1
  33. package/dist/store/reducers/olapStats.js +5 -1
  34. package/dist/store/reducers/preview.js +1 -1
  35. package/dist/store/reducers/shardsWorkload.js +32 -4
  36. package/dist/types/api/schema.ts +43 -1
  37. package/dist/types/api/storage.ts +54 -0
  38. package/dist/utils/getNodesColumns.js +2 -0
  39. package/dist/utils/pdisk.ts +74 -0
  40. package/dist/utils/tooltip.js +27 -0
  41. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.10.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.0...v1.10.1) (2022-08-10)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **Tenant:** fix actions set for topics ([0c75bf4](https://github.com/ydb-platform/ydb-embedded-ui/commit/0c75bf4561966dd663ab1cd7c7b81ef6b4632e50))
9
+
10
+ ## [1.10.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.9.0...v1.10.0) (2022-08-10)
11
+
12
+
13
+ ### Features
14
+
15
+ * **TopShards:** add DataSize column ([cbcd047](https://github.com/ydb-platform/ydb-embedded-ui/commit/cbcd047d277f699a67bc002a5542f3b9f6a0c942))
16
+ * **TopShards:** sort table data on backend ([dc28c5c](https://github.com/ydb-platform/ydb-embedded-ui/commit/dc28c5c75b0036480bf804d49f82fc54eac98c8e))
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * add concurrentId for sendQuery request ([dc6b32a](https://github.com/ydb-platform/ydb-embedded-ui/commit/dc6b32a8fd51064ddeca2fc60a0f08a725216334))
22
+ * **Storage:** display pdisk type in tooltip ([2b03a35](https://github.com/ydb-platform/ydb-embedded-ui/commit/2b03a35fc11ddeae3bdd30a0690b324ae917f5c3))
23
+ * **Tablet:** change Kill to Restart ([dd585b1](https://github.com/ydb-platform/ydb-embedded-ui/commit/dd585b1d1a6a5ddb484a702523773b169900f582))
24
+ * **Tenant:** add missing schema node types ([62a0ecb](https://github.com/ydb-platform/ydb-embedded-ui/commit/62a0ecb848dbcee53e18535cbf7c03a731d0cfeb))
25
+ * **Tenant:** ensure correct behavior for new schema node types ([f80c381](https://github.com/ydb-platform/ydb-embedded-ui/commit/f80c38152656e8bbbe51ec38b29fc0d954c361cc))
26
+ * **Tenant:** use new schema icons ([389a921](https://github.com/ydb-platform/ydb-embedded-ui/commit/389a9214c64b1adb183fa0c6caa6f2ec536dbef3))
27
+ * **TopShards:** disable virtualization for table ([006d3d9](https://github.com/ydb-platform/ydb-embedded-ui/commit/006d3d9fb9a4744b8bb4ad03e53693199213f80e))
28
+ * **TopShards:** format DataSize value ([c51ce66](https://github.com/ydb-platform/ydb-embedded-ui/commit/c51ce66286f6454f7252d1194628ee5a50aafba2))
29
+ * **TopShards:** only allow DESC sort ([6aa326f](https://github.com/ydb-platform/ydb-embedded-ui/commit/6aa326fc4b8165f00f8b3ecf5becdb0943ed57af))
30
+ * **TopShards:** substring tenant name out of shards path ([9e57672](https://github.com/ydb-platform/ydb-embedded-ui/commit/9e5767222c7dac7734c68abd08067cea507b1e15))
31
+
32
+ ## [1.9.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.8.8...v1.9.0) (2022-07-29)
33
+
34
+
35
+ ### Features
36
+
37
+ * **Node:** display endpoints in overview ([89e9e47](https://github.com/ydb-platform/ydb-embedded-ui/commit/89e9e470499b6f458e8949211d97293c0b7d9b97))
38
+ * **Node:** display node basic info above tabs ([aafb15b](https://github.com/ydb-platform/ydb-embedded-ui/commit/aafb15b399bf116026eff36f3c4ac817e2c40e18))
39
+ * **Node:** more informative pdisks panels ([342712b](https://github.com/ydb-platform/ydb-embedded-ui/commit/342712bcaa793971e1ca354da57fb962639ef90c))
40
+ * **Nodes:** show node endpoints in tooltip ([34be559](https://github.com/ydb-platform/ydb-embedded-ui/commit/34be55957e02f947ede30b43f22fde82d21df308))
41
+ * **Tenant:** table index overview ([2aed714](https://github.com/ydb-platform/ydb-embedded-ui/commit/2aed71488cde1175e6569c236ab609bb126f9cf3))
42
+ * **Tenant:** virtualized tree in schema ([815f558](https://github.com/ydb-platform/ydb-embedded-ui/commit/815f5588e5fed6fb86f69653c4937e975465372f))
43
+ * utils for parsing bitfields in pdisk data ([da22b4a](https://github.com/ydb-platform/ydb-embedded-ui/commit/da22b4afde9efe4d9605cefb69ddd51aed989722))
44
+
45
+
46
+ ### Bug Fixes
47
+
48
+ * **Node:** fix pdisk title items width ([ca5fec6](https://github.com/ydb-platform/ydb-embedded-ui/commit/ca5fec6388364b7d1d6362f1bda36431d9c29749))
49
+ * **Nodes:** hide tooltip on unmount ([54e4fdc](https://github.com/ydb-platform/ydb-embedded-ui/commit/54e4fdc8045c555338e79d89a93faf58e888fa0e))
50
+ * **ProgressViewer:** apply provided custom class name ([aa60e9d](https://github.com/ydb-platform/ydb-embedded-ui/commit/aa60e9d1b9c0752853f4323d3bcfd220bedd272d))
51
+ * **Tenant:** display all table props in overview ([d70e311](https://github.com/ydb-platform/ydb-embedded-ui/commit/d70e311296f6a4d1781f6e72929c70e0db7c3226))
52
+ * **Tenant:** display PartCount first in table overview ([8c09746](https://github.com/ydb-platform/ydb-embedded-ui/commit/8c09746b026a23a36fe31be94057cc92535aceaa))
53
+
3
54
  ## [1.8.8](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.8.7...v1.8.8) (2022-07-21)
4
55
 
5
56
 
@@ -0,0 +1,43 @@
1
+ @import '../../styles/mixins.scss';
2
+
3
+ .basic-node-viewer {
4
+ font-size: var(--yc-text-body-2-font-size);
5
+ line-height: var(--yc-text-body-2-line-height);
6
+
7
+ display: flex;
8
+ align-items: center;
9
+
10
+ margin: 15px 0;
11
+
12
+ &__title {
13
+ margin: 0 20px 0 0;
14
+
15
+ font-size: var(--yc-text-body-2-font-size);
16
+ font-weight: 600;
17
+ line-height: var(--yc-text-body-2-line-height);
18
+ text-transform: uppercase;
19
+ }
20
+
21
+ &__id {
22
+ margin: 0 15px 0 24px;
23
+ }
24
+
25
+ &__label {
26
+ margin-right: 10px;
27
+
28
+ font-size: var(--yc-text-body-2-font-size);
29
+ line-height: 18px;
30
+ white-space: nowrap;
31
+
32
+ color: var(--yc-color-text-hint);
33
+
34
+ .yc-root_theme_dark & {
35
+ color: var(--yc-color-text-hint);
36
+ }
37
+ }
38
+
39
+ &__link {
40
+ margin-left: 5px;
41
+ @extend .link;
42
+ }
43
+ }
@@ -0,0 +1,53 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import EntityStatus from '../EntityStatus/EntityStatus';
4
+ import Tags from '../Tags/Tags';
5
+ import Icon from '../Icon/Icon';
6
+
7
+ import './BasicNodeViewer.scss';
8
+
9
+ const b = cn('basic-node-viewer');
10
+
11
+ interface BasicNodeViewerProps {
12
+ node: any;
13
+ additionalNodesInfo?: any;
14
+ className?: string;
15
+ }
16
+
17
+ export const BasicNodeViewer = ({node, additionalNodesInfo, className}: BasicNodeViewerProps) => {
18
+ const nodeHref = additionalNodesInfo?.getNodeRef
19
+ ? additionalNodesInfo.getNodeRef(node) + 'internal'
20
+ : undefined;
21
+
22
+ return (
23
+ <div className={b(null, className)}>
24
+ {node ? (
25
+ <>
26
+ <div className={b('title')}>Node</div>
27
+ <EntityStatus status={node.SystemState} name={node.Host} />
28
+ {nodeHref && (
29
+ <a
30
+ rel="noopener noreferrer"
31
+ className={b('link', {external: true})}
32
+ href={nodeHref}
33
+ target="_blank"
34
+ >
35
+ <Icon name="external" />
36
+ </a>
37
+ )}
38
+
39
+ <div className={b('id')}>
40
+ <label className={b('label')}>NodeID</label>
41
+ <label>{node.NodeId}</label>
42
+ </div>
43
+
44
+ <Tags tags={[node.DataCenter]} />
45
+ <Tags tags={node.Roles} tagsType="blue" />
46
+ </>
47
+ ) : (
48
+ <div className="error">no data</div>
49
+ )}
50
+
51
+ </div>
52
+ );
53
+ };
@@ -0,0 +1 @@
1
+ export * from './BasicNodeViewer';
@@ -12,6 +12,8 @@ class EntityStatus extends React.Component {
12
12
  static propTypes = {
13
13
  status: PropTypes.string,
14
14
  name: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
15
+ onNameMouseEnter: PropTypes.func,
16
+ onNameMouseLeave: PropTypes.func,
15
17
  path: PropTypes.string,
16
18
  size: PropTypes.string,
17
19
  label: PropTypes.string,
@@ -49,19 +51,29 @@ class EntityStatus extends React.Component {
49
51
  );
50
52
  }
51
53
  renderLink() {
52
- const {externalLink, name, path} = this.props;
54
+ const {externalLink, name, path, onNameMouseEnter, onNameMouseLeave} = this.props;
53
55
 
54
56
  if (externalLink) {
55
57
  return <ExternalLink href={path}>{name}</ExternalLink>;
56
58
  }
57
59
 
58
60
  return path ? (
59
- <Link title={name} to={path}>
61
+ <Link
62
+ title={name}
63
+ to={path}
64
+ onMouseEnter={onNameMouseEnter}
65
+ onMouseLeave={onNameMouseLeave}
66
+ >
60
67
  {name}
61
68
  </Link>
62
69
  ) : (
63
70
  name && (
64
- <span className={b('name')} title={name}>
71
+ <span
72
+ className={b('name')}
73
+ title={name}
74
+ onMouseEnter={onNameMouseEnter}
75
+ onMouseLeave={onNameMouseLeave}
76
+ >
65
77
  {name}
66
78
  </span>
67
79
  )
@@ -3,11 +3,8 @@ import cn from 'bem-cn-lite';
3
3
  import PropTypes from 'prop-types';
4
4
 
5
5
  import InfoViewer from '../InfoViewer/InfoViewer';
6
- import EntityStatus from '../EntityStatus/EntityStatus';
7
6
  import ProgressViewer from '../ProgressViewer/ProgressViewer';
8
7
  import PoolUsage from '../PoolUsage/PoolUsage';
9
- import Tags from '../Tags/Tags';
10
- import Icon from '../Icon/Icon';
11
8
 
12
9
  import {LOAD_AVERAGE_TIME_INTERVALS} from '../../utils/constants';
13
10
  import {calcUptime} from '../../utils';
@@ -22,7 +19,6 @@ class FullNodeViewer extends React.Component {
22
19
  node: PropTypes.object.isRequired,
23
20
  backend: PropTypes.string,
24
21
  singleClusterMode: PropTypes.bool,
25
- additionalNodesInfo: PropTypes.object,
26
22
  };
27
23
 
28
24
  static defaultProps = {
@@ -30,10 +26,12 @@ class FullNodeViewer extends React.Component {
30
26
  };
31
27
 
32
28
  render() {
33
- const {node, className, additionalNodesInfo={}} = this.props;
34
- const nodeHref = additionalNodesInfo.getNodeRef
35
- ? additionalNodesInfo.getNodeRef(node) + 'internal'
36
- : undefined;
29
+ const {node, className} = this.props;
30
+
31
+ const endpointsInfo = node.Endpoints?.map(({Name, Address}) => ({
32
+ label: Name,
33
+ value: Address,
34
+ }));
37
35
 
38
36
  const commonInfo = [
39
37
  {label: 'Version', value: node.Version},
@@ -50,52 +48,35 @@ class FullNodeViewer extends React.Component {
50
48
  return (
51
49
  <div className={`${b()} ${className}`}>
52
50
  {node ? (
53
- <div>
54
- <div className={b('row')}>
55
- <div className={b('title')}>Node</div>
56
- <EntityStatus status={node.SystemState} name={node.Host} />
57
- {nodeHref && (
58
- <a
59
- rel="noopener noreferrer"
60
- className={b('link', {external: true})}
61
- href={nodeHref}
62
- target="_blank"
63
- >
64
- <Icon name="external" />
65
- </a>
66
- )}
67
-
68
- <div className={b('row', {id: true})}>
69
- <label className={b('label', {id: true})}>NodeID</label>
70
- <label>{node.NodeId}</label>
51
+ <div className={b('common-info')}>
52
+ <div>
53
+ <div className={b('section-title')}>Pools</div>
54
+ <div className={b('section', {pools: true})}>
55
+ {node.PoolStats.map((pool, poolIndex) => (
56
+ <PoolUsage key={poolIndex} data={pool} />
57
+ ))}
71
58
  </div>
72
-
73
- <Tags tags={[node.DataCenter]} />
74
- <Tags tags={node.Roles} tagsType="blue" />
75
59
  </div>
76
60
 
77
- <div className={b('common-info')}>
78
- <div>
79
- <div className={b('section-title')}>Pools</div>
80
- <div className={b('section', {pools: true})}>
81
- {node.PoolStats.map((pool, poolIndex) => (
82
- <PoolUsage key={poolIndex} data={pool} />
83
- ))}
84
- </div>
85
- </div>
86
-
61
+ {endpointsInfo && endpointsInfo.length && (
87
62
  <InfoViewer
88
- title="Common info"
63
+ title="Endpoints"
89
64
  className={b('section')}
90
- info={commonInfo}
65
+ info={endpointsInfo}
91
66
  />
92
-
93
- <InfoViewer
94
- title="Load average"
95
- className={b('section', {average: true})}
96
- info={averageInfo}
97
- />
98
- </div>
67
+ )}
68
+
69
+ <InfoViewer
70
+ title="Common info"
71
+ className={b('section')}
72
+ info={commonInfo}
73
+ />
74
+
75
+ <InfoViewer
76
+ title="Load average"
77
+ className={b('section', {average: true})}
78
+ info={averageInfo}
79
+ />
99
80
  </div>
100
81
  ) : (
101
82
  <div className="error">no data</div>
@@ -4,26 +4,6 @@
4
4
  font-size: var(--yc-text-body-2-font-size);
5
5
  line-height: var(--yc-text-body-2-line-height);
6
6
 
7
- &__title {
8
- margin: 0 20px 0 0;
9
-
10
- font-size: var(--yc-text-body-2-font-size);
11
- font-weight: 600;
12
- line-height: var(--yc-text-body-2-line-height);
13
- text-transform: uppercase;
14
- }
15
-
16
- &__row {
17
- display: flex;
18
- align-items: center;
19
-
20
- margin: 15px 0;
21
-
22
- &_id {
23
- margin: 0 15px 0 24px;
24
- }
25
- }
26
-
27
7
  &__common-info {
28
8
  display: flex;
29
9
  flex-direction: column;
@@ -46,26 +26,6 @@
46
26
  min-width: 60px;
47
27
  }
48
28
 
49
- &__label {
50
- min-width: 100px;
51
- margin-right: 25px;
52
-
53
- font-size: var(--yc-text-body-2-font-size);
54
- line-height: 18px;
55
- white-space: nowrap;
56
-
57
- color: var(--yc-color-text-hint);
58
-
59
- .yc-root_theme_dark & {
60
- color: var(--yc-color-text-hint);
61
- }
62
-
63
- &_id {
64
- min-width: auto;
65
- margin-right: 10px;
66
- }
67
- }
68
-
69
29
  &__section-title {
70
30
  margin: 15px 0 10px;
71
31
 
@@ -73,9 +33,4 @@
73
33
  font-weight: 600;
74
34
  line-height: var(--yc-text-body-2-line-height);
75
35
  }
76
-
77
- &__link {
78
- margin-left: 5px;
79
- @extend .link;
80
- }
81
36
  }
@@ -0,0 +1,52 @@
1
+ import type {TEvDescribeSchemeResult, TIndexDescription} from '../../types/api/schema';
2
+ import {InfoViewer, createInfoFormatter} from '../InfoViewer';
3
+
4
+ const DISPLAYED_FIELDS: Set<keyof TIndexDescription> = new Set([
5
+ 'Type',
6
+ 'State',
7
+ 'DataSize',
8
+ 'KeyColumnNames',
9
+ 'DataColumnNames',
10
+ ]);
11
+
12
+ const formatItem = createInfoFormatter<TIndexDescription>({
13
+ Type: (value) => value?.substring(10), // trims EIndexType prefix
14
+ State: (value) => value?.substring(11), // trims EIndexState prefix
15
+ KeyColumnNames: (value) => value?.join(', '),
16
+ DataColumnNames: (value) => value?.join(', '),
17
+ }, {
18
+ KeyColumnNames: 'Columns',
19
+ DataColumnNames: 'Includes',
20
+ });
21
+
22
+ interface IndexInfoViewerProps {
23
+ data?: TEvDescribeSchemeResult;
24
+ }
25
+
26
+ export const IndexInfoViewer = ({data}: IndexInfoViewerProps) => {
27
+ if (!data) {
28
+ return (
29
+ <div className="error">no index data</div>
30
+ );
31
+ }
32
+
33
+ const TableIndex = data.PathDescription?.TableIndex;
34
+ const info: Array<{label?: string, value?: unknown}> = [];
35
+
36
+ let key: keyof TIndexDescription;
37
+ for (key in TableIndex) {
38
+ if (DISPLAYED_FIELDS.has(key)) {
39
+ info.push(formatItem(key, TableIndex?.[key]));
40
+ }
41
+ }
42
+
43
+ return (
44
+ <>
45
+ {info.length ? (
46
+ <InfoViewer info={info}></InfoViewer>
47
+ ) : (
48
+ <>Empty</>
49
+ )}
50
+ </>
51
+ );
52
+ };
@@ -0,0 +1,4 @@
1
+ import InfoViewer from './InfoViewer';
2
+
3
+ export {InfoViewer};
4
+ export * from './utils';
@@ -0,0 +1,32 @@
1
+ type LabelMap<T> = {
2
+ [label in keyof T]?: string;
3
+ }
4
+
5
+ type FieldMappers<T> = {
6
+ [label in keyof T]?: (value: T[label]) => string | undefined;
7
+ }
8
+
9
+ function formatLabel<Shape>(label: keyof Shape, map: LabelMap<Shape>) {
10
+ return map[label] ?? label;
11
+ }
12
+
13
+ function formatValue<Shape, Key extends keyof Shape>(
14
+ label: Key,
15
+ value: Shape[Key],
16
+ mappers: FieldMappers<Shape>,
17
+ ) {
18
+ const mapper = mappers[label];
19
+ const mappedValue = mapper ? mapper(value) : value;
20
+
21
+ return String(mappedValue ?? '');
22
+ }
23
+
24
+ export function createInfoFormatter<Shape extends Record<string, any>>(
25
+ fieldMappers?: FieldMappers<Shape>,
26
+ labelMap?: LabelMap<Shape>,
27
+ ) {
28
+ return <Key extends keyof Shape>(label: Key, value: Shape[Key]) => ({
29
+ label: formatLabel(label, labelMap || {}),
30
+ value: formatValue(label, value, fieldMappers || {}),
31
+ });
32
+ }
@@ -76,7 +76,7 @@ export class ProgressViewer extends React.Component {
76
76
 
77
77
  if (!isNaN(fillWidth)) {
78
78
  return (
79
- <div className={b({size})}>
79
+ <div className={b({size}, className)}>
80
80
  <div className={b('line', {bg})} style={lineStyle}></div>
81
81
  <span className={b('text', {text})}>
82
82
  {`${valueText} ${divider} ${capacityText}`}
@@ -4,6 +4,10 @@
4
4
  overflow: auto;
5
5
  @include flex-container();
6
6
 
7
+ &__header {
8
+ margin: 16px 20px;
9
+ }
10
+
7
11
  &__content {
8
12
  position: relative;
9
13
 
@@ -19,7 +23,7 @@
19
23
  }
20
24
 
21
25
  &__tabs {
22
- padding: 16px 20px 0;
26
+ padding: 0 20px;
23
27
  }
24
28
 
25
29
  &__tab {
@@ -13,6 +13,7 @@ import Storage from '../Storage/Storage';
13
13
  import NodeOverview from './NodeOverview/NodeOverview';
14
14
  import NodeStructure from './NodeStructure/NodeStructure';
15
15
  import Loader from '../../components/Loader/Loader';
16
+ import {BasicNodeViewer} from '../../components/BasicNodeViewer';
16
17
 
17
18
  import {getNodeInfo, resetNode} from '../../store/reducers/node';
18
19
  import routes, {CLUSTER_PAGES, createHref} from '../../routes';
@@ -133,7 +134,6 @@ function Node(props: NodeProps) {
133
134
  case OVERVIEW: {
134
135
  return (
135
136
  <NodeOverview
136
- additionalNodesInfo={additionalNodesInfo}
137
137
  node={node}
138
138
  className={b('overview-wrapper')}
139
139
  />
@@ -162,6 +162,12 @@ function Node(props: NodeProps) {
162
162
  if (node) {
163
163
  return (
164
164
  <div className={b(null, props.className)}>
165
+ <BasicNodeViewer
166
+ node={node}
167
+ additionalNodesInfo={props.additionalNodesInfo}
168
+ className={b('header')}
169
+ />
170
+
165
171
  {renderTabs()}
166
172
 
167
173
  <div className={b('content')}>{renderTabContent()}</div>
@@ -5,16 +5,14 @@ import {backend} from '../../../store';
5
5
 
6
6
  interface NodeOverviewProps {
7
7
  node: any;
8
- additionalNodesInfo: any;
9
8
  className?: string;
10
9
  }
11
10
 
12
- function NodeOverview({node, additionalNodesInfo, className}: NodeOverviewProps) {
11
+ function NodeOverview({node, className}: NodeOverviewProps) {
13
12
  return (
14
13
  <FullNodeViewer
15
14
  node={node}
16
15
  backend={backend}
17
- additionalNodesInfo={additionalNodesInfo}
18
16
  className={className}
19
17
  />
20
18
  );
@@ -44,9 +44,38 @@
44
44
  &__pdisk-title-wrapper {
45
45
  display: flex;
46
46
  align-items: center;
47
- gap: 8px;
47
+ gap: 16px;
48
48
 
49
49
  font-weight: 600;
50
+
51
+ .entity-status__status-icon {
52
+ margin-right: 0;
53
+ }
54
+ }
55
+
56
+ &__pdisk-title-item {
57
+ display: flex;
58
+ gap: 4px;
59
+
60
+ &-label {
61
+ font-weight: 400;
62
+
63
+ color: var(--yc-color-text-secondary);
64
+ }
65
+ }
66
+
67
+ &__pdisk-title-id {
68
+ min-width: 110px;
69
+ }
70
+
71
+ &__pdisk-title-type {
72
+ justify-content: flex-end;
73
+
74
+ min-width: 50px;
75
+ }
76
+
77
+ &__pdisk-title-size {
78
+ min-width: 150px;
50
79
  }
51
80
 
52
81
  &__pdisk-details {
@@ -0,0 +1,25 @@
1
+ import {ReactNode} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ const b = cn('kv-node-structure');
5
+
6
+ interface PDiskTitleBadgeProps {
7
+ label?: string;
8
+ value: ReactNode;
9
+ className?: string;
10
+ }
11
+
12
+ export function PDiskTitleBadge({label, value, className}: PDiskTitleBadgeProps) {
13
+ return (
14
+ <span className={b('pdisk-title-item', className)}>
15
+ {label && (
16
+ <span className={b('pdisk-title-item-label')}>
17
+ {label}:
18
+ </span>
19
+ )}
20
+ <span className={b('pdisk-title-item-value')}>
21
+ {value}
22
+ </span>
23
+ </span>
24
+ );
25
+ }
@@ -14,9 +14,11 @@ import {Vdisk} from './Vdisk';
14
14
 
15
15
  import {bytesToGB, pad9} from '../../../utils/utils';
16
16
  import {formatStorageValuesToGb} from '../../../utils';
17
+ import {getPDiskType} from '../../../utils/pdisk';
17
18
 
18
19
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
19
20
  import {valueIsDefined} from './NodeStructure';
21
+ import {PDiskTitleBadge} from './PDiskTitleBadge';
20
22
 
21
23
  const b = cn('kv-node-structure');
22
24
 
@@ -230,6 +232,7 @@ export function PDisk(props: PDiskProps) {
230
232
  }
231
233
  if (valueIsDefined(Category)) {
232
234
  pdiskInfo.push({label: 'Category', value: Category});
235
+ pdiskInfo.push({label: 'Type', value: getPDiskType(data)});
233
236
  }
234
237
  pdiskInfo.push({
235
238
  label: 'Allocated Size',
@@ -286,8 +289,27 @@ export function PDisk(props: PDiskProps) {
286
289
  <div className={b('pdisk')} id={props.id}>
287
290
  <div className={b('pdisk-header')}>
288
291
  <div className={b('pdisk-title-wrapper')}>
289
- <span>{data.Path}</span>
290
- <EntityStatus status={data.Device} name={`${data.NodeId}-${data.PDiskId}`} />
292
+ <EntityStatus status={data.Device} />
293
+ <PDiskTitleBadge
294
+ label="PDiskID"
295
+ value={data.PDiskId}
296
+ className={b('pdisk-title-id')}
297
+ />
298
+ <PDiskTitleBadge
299
+ value={getPDiskType(data)}
300
+ className={b('pdisk-title-type')}
301
+ />
302
+ <ProgressViewer
303
+ value={data.TotalSize - data.AvailableSize}
304
+ capacity={data.TotalSize}
305
+ formatValues={formatStorageValuesToGb}
306
+ colorizeProgress={true}
307
+ className={b('pdisk-title-size')}
308
+ />
309
+ <PDiskTitleBadge
310
+ label="VDisks"
311
+ value={data.vDisks.length}
312
+ />
291
313
  </div>
292
314
  <Button onClick={unfolded ? onClosePDiskDetails : onOpenPDiskDetails} view="flat-secondary">
293
315
  <ArrowToggle direction={unfolded ? 'top' : 'bottom'} />
@@ -63,6 +63,7 @@ class Nodes extends React.Component {
63
63
  }
64
64
 
65
65
  componentWillUnmount() {
66
+ this.props.hideTooltip();
66
67
  clearInterval(this.reloadDescriptor);
67
68
  }
68
69