ydb-embedded-ui 3.2.3 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/InfoViewer/InfoViewer.scss +10 -0
  3. package/dist/components/InfoViewer/InfoViewer.tsx +12 -2
  4. package/dist/components/InfoViewer/formatters/cdcStream.ts +10 -0
  5. package/dist/components/InfoViewer/formatters/index.ts +3 -0
  6. package/dist/components/InfoViewer/formatters/pqGroup.ts +51 -0
  7. package/dist/components/InfoViewer/formatters/schema.ts +1 -29
  8. package/dist/components/InfoViewer/formatters/topicStats.tsx +50 -0
  9. package/dist/components/InfoViewer/schemaInfo/index.ts +0 -2
  10. package/dist/components/InfoViewer/utils.ts +15 -0
  11. package/dist/components/InternalLink/InternalLink.tsx +17 -0
  12. package/dist/components/InternalLink/index.ts +1 -0
  13. package/dist/components/Tablet/Tablet.js +1 -1
  14. package/dist/components/TabletsStatistic/TabletsStatistic.tsx +1 -1
  15. package/dist/components/VerticalBars/VerticalBars.scss +15 -0
  16. package/dist/components/VerticalBars/VerticalBars.tsx +38 -0
  17. package/dist/components/VerticalBars/index.ts +1 -0
  18. package/dist/containers/App/App.js +0 -11
  19. package/dist/containers/App/App.scss +0 -1
  20. package/dist/containers/Cluster/Cluster.tsx +2 -2
  21. package/dist/containers/Nodes/Nodes.scss +5 -1
  22. package/dist/containers/Nodes/Nodes.tsx +196 -0
  23. package/dist/containers/{App → Nodes}/NodesTable.scss +2 -2
  24. package/dist/{utils/getNodesColumns.js → containers/Nodes/getNodesColumns.tsx} +60 -35
  25. package/dist/containers/Nodes/i18n/en.json +3 -0
  26. package/dist/containers/Nodes/i18n/index.ts +11 -0
  27. package/dist/containers/Nodes/i18n/ru.json +3 -0
  28. package/dist/containers/Nodes/index.ts +1 -0
  29. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +14 -20
  30. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +32 -16
  31. package/dist/containers/Storage/DiskStateProgressBar/index.ts +1 -0
  32. package/dist/containers/Storage/{Pdisk/Pdisk.scss → PDisk/PDisk.scss} +15 -4
  33. package/dist/containers/Storage/PDisk/PDisk.tsx +145 -0
  34. package/dist/containers/Storage/PDisk/__tests__/colors.tsx +37 -0
  35. package/dist/containers/Storage/PDisk/index.ts +1 -0
  36. package/dist/containers/Storage/PDiskPopup/PDiskPopup.scss +3 -0
  37. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +82 -0
  38. package/dist/containers/Storage/PDiskPopup/index.ts +1 -0
  39. package/dist/containers/Storage/Storage.js +1 -1
  40. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +10 -10
  41. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +1 -0
  42. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +7 -4
  43. package/dist/containers/Storage/VDisk/VDisk.scss +7 -0
  44. package/dist/containers/Storage/VDisk/VDisk.tsx +148 -0
  45. package/dist/containers/Storage/VDisk/__tests__/colors.tsx +209 -0
  46. package/dist/containers/Storage/VDisk/index.ts +1 -0
  47. package/dist/containers/Storage/VDiskPopup/VDiskPopup.scss +14 -0
  48. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +134 -0
  49. package/dist/containers/Storage/VDiskPopup/index.ts +1 -0
  50. package/dist/containers/Storage/utils/constants.ts +2 -9
  51. package/dist/containers/TabletsFilters/TabletsFilters.js +10 -6
  52. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +2 -2
  53. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -4
  54. package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.tsx +1 -1
  55. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +69 -0
  56. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/index.ts +1 -0
  57. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +18 -16
  58. package/dist/containers/Tenant/Diagnostics/Overview/TopicInfo/TopicInfo.tsx +37 -0
  59. package/dist/containers/Tenant/Diagnostics/Overview/TopicInfo/index.ts +1 -0
  60. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +30 -0
  61. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +94 -0
  62. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/en.json +3 -0
  63. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/index.ts +11 -0
  64. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/ru.json +3 -0
  65. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/index.ts +1 -0
  66. package/dist/containers/Tenant/Diagnostics/Overview/utils/index.ts +1 -0
  67. package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.ts +42 -0
  68. package/dist/containers/Tenant/utils/schema.ts +19 -0
  69. package/dist/containers/UserSettings/UserSettings.tsx +18 -10
  70. package/dist/services/api.d.ts +8 -1
  71. package/dist/services/api.js +27 -8
  72. package/dist/store/reducers/index.ts +3 -1
  73. package/dist/store/reducers/nodes.ts +148 -14
  74. package/dist/store/reducers/{clusterNodes.js → nodesList.js} +0 -41
  75. package/dist/store/reducers/settings.js +10 -4
  76. package/dist/store/reducers/storage.js +24 -13
  77. package/dist/store/reducers/tenant.js +5 -4
  78. package/dist/store/reducers/tooltip.ts +1 -1
  79. package/dist/store/reducers/topic.ts +52 -0
  80. package/dist/styles/mixins.scss +19 -11
  81. package/dist/types/api/common.ts +5 -0
  82. package/dist/types/api/compute.ts +1 -1
  83. package/dist/types/api/consumer.ts +12 -10
  84. package/dist/types/api/nodes.ts +2 -0
  85. package/dist/types/api/pdisk.ts +1 -0
  86. package/dist/types/api/schema.ts +3 -3
  87. package/dist/types/api/topic.ts +5 -4
  88. package/dist/types/api/vdisk.ts +2 -1
  89. package/dist/types/store/nodes.ts +56 -6
  90. package/dist/types/store/tooltip.ts +1 -1
  91. package/dist/types/store/topic.ts +21 -0
  92. package/dist/utils/constants.ts +5 -1
  93. package/dist/utils/i18n/i18n.ts +10 -2
  94. package/dist/utils/index.js +1 -1
  95. package/dist/utils/timeParsers/__test__/formatDuration.test.ts +50 -0
  96. package/dist/utils/timeParsers/__test__/protobuf.test.ts +74 -0
  97. package/dist/utils/timeParsers/formatDuration.ts +46 -0
  98. package/dist/utils/timeParsers/i18n/en.json +7 -0
  99. package/dist/utils/timeParsers/i18n/index.ts +11 -0
  100. package/dist/utils/timeParsers/i18n/ru.json +7 -0
  101. package/dist/utils/timeParsers/index.ts +2 -0
  102. package/dist/utils/timeParsers/protobuf.ts +36 -0
  103. package/package.json +1 -1
  104. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +0 -48
  105. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +0 -30
  106. package/dist/components/InternalLink/InternalLink.js +0 -23
  107. package/dist/containers/Nodes/Nodes.js +0 -214
  108. package/dist/containers/NodesViewer/NodesViewer.js +0 -163
  109. package/dist/containers/NodesViewer/NodesViewer.scss +0 -66
  110. package/dist/containers/Storage/Pdisk/Pdisk.tsx +0 -153
  111. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +0 -41
  112. package/dist/containers/Storage/Vdisk/Vdisk.js +0 -275
  113. package/dist/containers/Storage/Vdisk/Vdisk.scss +0 -22
  114. package/dist/containers/Storage/Vdisk/__tests__/colors.tsx +0 -163
  115. package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +0 -139
  116. package/dist/containers/Tenant/Diagnostics/Compute/Compute.scss +0 -14
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.2.3...v3.3.0) (2023-01-30)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Nodes:** use /viewer/json/nodes endpoint ([226cc70](https://github.com/ydb-platform/ydb-embedded-ui/commit/226cc70dcb89262890856b4d0cb03eac0675256d))
9
+ * **Overview:** display topic stats for topics and streams ([08e9fe0](https://github.com/ydb-platform/ydb-embedded-ui/commit/08e9fe0ee379715229474322a03ec668e26bdb9b))
10
+ * **Storage:** display vdisks over pdisks ([bb5d1fa](https://github.com/ydb-platform/ydb-embedded-ui/commit/bb5d1fa5ae2953ca30b13df45340b7a1a63056cb))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * add duration formatter ([e325d98](https://github.com/ydb-platform/ydb-embedded-ui/commit/e325d98845d29dea208debdfcb88d330c1d6daee))
16
+ * add protobuf time formatters ([c74cd9d](https://github.com/ydb-platform/ydb-embedded-ui/commit/c74cd9d0949674414ba2c9754e3dcc5c2be622a5))
17
+ * add verticalBars component ([053ffa8](https://github.com/ydb-platform/ydb-embedded-ui/commit/053ffa8fd460f89f4296a96fcf46a9267ac4cae3))
18
+ * **PDisk:** grey color for unknown state ([54f7e15](https://github.com/ydb-platform/ydb-embedded-ui/commit/54f7e159aaddd932ccecddfb10265ee596fed1e2))
19
+ * **Storage:** request only static nodes ([e91e136](https://github.com/ydb-platform/ydb-embedded-ui/commit/e91e136d7c72bea694c7a282c83d577cc60e5386))
20
+ * **Topic:** add reducer for describe_topic ([7c61dc9](https://github.com/ydb-platform/ydb-embedded-ui/commit/7c61dc906452df2e1a77a2ff602916f6ea785df5))
21
+ * update PDisks and VDisks tests ([3bf660e](https://github.com/ydb-platform/ydb-embedded-ui/commit/3bf660e41d92e1a32444872c5fb9d47209bef8b5))
22
+
3
23
  ## [3.2.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.2.2...v3.2.3) (2023-01-16)
4
24
 
5
25
 
@@ -42,6 +42,16 @@
42
42
  color: var(--yc-color-text-secondary);
43
43
  }
44
44
 
45
+ &__label-text {
46
+ &_multiline {
47
+ overflow: visible;
48
+
49
+ max-width: 180px;
50
+
51
+ white-space: normal;
52
+ }
53
+ }
54
+
45
55
  &__dots {
46
56
  display: flex;
47
57
  flex: 1 1 auto;
@@ -14,11 +14,19 @@ interface InfoViewerProps {
14
14
  dots?: boolean;
15
15
  size?: 's';
16
16
  className?: string;
17
+ multilineLabels?: boolean;
17
18
  }
18
19
 
19
20
  const b = cn('info-viewer');
20
21
 
21
- const InfoViewer = ({title, info, dots = true, size, className}: InfoViewerProps) => (
22
+ const InfoViewer = ({
23
+ title,
24
+ info,
25
+ dots = true,
26
+ size,
27
+ className,
28
+ multilineLabels,
29
+ }: InfoViewerProps) => (
22
30
  <div className={b({size}, className)}>
23
31
  {title && <div className={b('title')}>{title}</div>}
24
32
  {info && info.length > 0 ? (
@@ -26,7 +34,9 @@ const InfoViewer = ({title, info, dots = true, size, className}: InfoViewerProps
26
34
  {info.map((data, infoIndex) => (
27
35
  <div className={b('row')} key={data.label + infoIndex}>
28
36
  <div className={b('label')}>
29
- {data.label}
37
+ <div className={b('label-text', {multiline: multilineLabels})}>
38
+ {data.label}
39
+ </div>
30
40
  {dots && <div className={b('dots')} />}
31
41
  </div>
32
42
 
@@ -0,0 +1,10 @@
1
+ import {TCdcStreamDescription} from '../../../types/api/schema';
2
+
3
+ import {createInfoFormatter} from '../utils';
4
+
5
+ export const formatCdcStreamItem = createInfoFormatter<TCdcStreamDescription>({
6
+ values: {
7
+ Mode: (value) => value?.substring('ECdcStreamMode'.length),
8
+ Format: (value) => value?.substring('ECdcStreamFormat'.length),
9
+ },
10
+ });
@@ -1,2 +1,5 @@
1
1
  export * from './common';
2
2
  export * from './schema';
3
+ export * from './topicStats';
4
+ export * from './pqGroup';
5
+ export * from './cdcStream';
@@ -0,0 +1,51 @@
1
+ import {
2
+ EMeteringMode,
3
+ TPersQueueGroupDescription,
4
+ TPQPartitionConfig,
5
+ TPQTabletConfig,
6
+ } from '../../../types/api/schema';
7
+ import {formatBps, formatBytes, formatNumber} from '../../../utils';
8
+ import {HOUR_IN_SECONDS} from '../../../utils/constants';
9
+
10
+ import {createInfoFormatter} from '../utils';
11
+
12
+ const EMeteringModeToNames: Record<EMeteringMode, string> = {
13
+ [EMeteringMode.METERING_MODE_REQUEST_UNITS]: 'request-units',
14
+ [EMeteringMode.METERING_MODE_RESERVED_CAPACITY]: 'reserved-capacity',
15
+ };
16
+
17
+ export const formatPQGroupItem = createInfoFormatter<TPersQueueGroupDescription>({
18
+ values: {
19
+ Partitions: (value) => formatNumber(value?.length || 0),
20
+ PQTabletConfig: (value) => {
21
+ const hours =
22
+ Math.round((value.PartitionConfig.LifetimeSeconds / HOUR_IN_SECONDS) * 100) / 100;
23
+ return `${formatNumber(hours)} hours`;
24
+ },
25
+ },
26
+ labels: {
27
+ Partitions: 'Partitions count',
28
+ PQTabletConfig: 'Retention',
29
+ },
30
+ });
31
+
32
+ export const formatPQTabletConfig = createInfoFormatter<TPQTabletConfig>({
33
+ values: {
34
+ Codecs: (value) => value && Object.values(value.Codecs || {}).join(', '),
35
+ MeteringMode: (value) => value && EMeteringModeToNames[value],
36
+ },
37
+ labels: {
38
+ MeteringMode: 'Metering mode',
39
+ },
40
+ });
41
+
42
+ export const formatPQPartitionConfig = createInfoFormatter<TPQPartitionConfig>({
43
+ values: {
44
+ StorageLimitBytes: formatBytes,
45
+ WriteSpeedInBytesPerSecond: formatBps,
46
+ },
47
+ labels: {
48
+ StorageLimitBytes: 'Retention storage',
49
+ WriteSpeedInBytesPerSecond: 'Partitions write speed',
50
+ },
51
+ });
@@ -1,10 +1,4 @@
1
- import type {
2
- TCdcStreamDescription,
3
- TIndexDescription,
4
- TPersQueueGroupDescription,
5
- } from '../../../types/api/schema';
6
- import {formatNumber} from '../../../utils';
7
- import {HOUR_IN_SECONDS} from '../../../utils/constants';
1
+ import {TIndexDescription} from '../../../types/api/schema';
8
2
 
9
3
  import {createInfoFormatter} from '../utils';
10
4
 
@@ -20,25 +14,3 @@ export const formatTableIndexItem = createInfoFormatter<TIndexDescription>({
20
14
  DataColumnNames: 'Includes',
21
15
  },
22
16
  });
23
-
24
- export const formatCdcStreamItem = createInfoFormatter<TCdcStreamDescription>({
25
- values: {
26
- Mode: (value) => value?.substring('ECdcStreamMode'.length),
27
- Format: (value) => value?.substring('ECdcStreamFormat'.length),
28
- },
29
- });
30
-
31
- export const formatPQGroupItem = createInfoFormatter<TPersQueueGroupDescription>({
32
- values: {
33
- Partitions: (value) => formatNumber(value?.length || 0),
34
- PQTabletConfig: (value) => {
35
- const hours =
36
- Math.round((value.PartitionConfig.LifetimeSeconds / HOUR_IN_SECONDS) * 100) / 100;
37
- return `${formatNumber(hours)} hours`;
38
- },
39
- },
40
- labels: {
41
- Partitions: 'Partitions count',
42
- PQTabletConfig: 'Retention',
43
- },
44
- });
@@ -0,0 +1,50 @@
1
+ import type {MultipleWindowsStat} from '../../../types/api/consumer';
2
+ import type {TopicStats} from '../../../types/api/topic';
3
+ import {formatBytes} from '../../../utils';
4
+ import {DAY_IN_SECONDS, HOUR_IN_SECONDS, MINUTE_IN_SECONDS} from '../../../utils/constants';
5
+
6
+ import {
7
+ parseProtobufTimestampToMs,
8
+ parseProtobufDurationToMs,
9
+ formatDurationToShortTimeFormat,
10
+ } from '../../../utils/timeParsers';
11
+
12
+ import {VerticalBars} from '../../VerticalBars/VerticalBars';
13
+
14
+ import {createInfoFormatter} from '../utils';
15
+
16
+ export const prepareBytesWritten = (data?: MultipleWindowsStat) => {
17
+ return {
18
+ per_minute:
19
+ data && data.per_minute ? Math.floor(Number(data.per_minute) / MINUTE_IN_SECONDS) : 0,
20
+ per_hour: data && data.per_hour ? Math.floor(Number(data.per_hour) / HOUR_IN_SECONDS) : 0,
21
+ per_day: data && data.per_day ? Math.floor(Number(data.per_day) / DAY_IN_SECONDS) : 0,
22
+ };
23
+ };
24
+
25
+ export const formatTopicStats = createInfoFormatter<TopicStats>({
26
+ values: {
27
+ store_size_bytes: formatBytes,
28
+ min_last_write_time: (value) => {
29
+ if (!value) {
30
+ return formatDurationToShortTimeFormat(0);
31
+ }
32
+
33
+ const durationMs = Date.now() - parseProtobufTimestampToMs(value);
34
+
35
+ // Duration could be negative because of the difference between server and local time
36
+ // Usually it below 100ms, so it could be omitted
37
+ return formatDurationToShortTimeFormat(durationMs < 0 ? 0 : durationMs);
38
+ },
39
+ max_write_time_lag: (value) =>
40
+ formatDurationToShortTimeFormat(value ? parseProtobufDurationToMs(value) : 0),
41
+ bytes_written: (value) =>
42
+ value && <VerticalBars values={Object.values(prepareBytesWritten(value))} />,
43
+ },
44
+ labels: {
45
+ store_size_bytes: 'Store size',
46
+ min_last_write_time: 'Partitions max time since last write',
47
+ max_write_time_lag: 'Partitions max write time lag',
48
+ bytes_written: 'Average write speed',
49
+ },
50
+ });
@@ -1,3 +1 @@
1
- export * from './CDCStreamInfo';
2
1
  export * from './TableIndexInfo';
3
- export * from './PersQueueGroupInfo';
@@ -1,5 +1,7 @@
1
1
  import type {ReactNode} from 'react';
2
2
 
3
+ import {InfoViewerItem} from './InfoViewer';
4
+
3
5
  type LabelMap<T> = {
4
6
  [label in keyof T]?: string;
5
7
  };
@@ -40,3 +42,16 @@ export function createInfoFormatter<Shape extends Record<string, any>>({
40
42
  value: formatValue(label, value, valueFormatters || {}, defaultValueFormatter),
41
43
  });
42
44
  }
45
+
46
+ export const formatObject = <Shape extends Record<string, any>>(
47
+ formatter: <Key extends keyof Shape>(value: Key, label: Shape[Key]) => InfoViewerItem,
48
+ obj?: Partial<Shape>,
49
+ ): InfoViewerItem[] => {
50
+ if (!obj) {
51
+ return [];
52
+ }
53
+
54
+ return Object.entries(obj)
55
+ .map(([label, value]) => formatter(label, value))
56
+ .filter(({value}) => Boolean(value));
57
+ };
@@ -0,0 +1,17 @@
1
+ import {Link, LinkProps} from 'react-router-dom';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ const bLink = cn('yc-link');
5
+
6
+ interface InternalLinkProps extends Omit<LinkProps, 'to'> {
7
+ to?: LinkProps['to'];
8
+ }
9
+
10
+ export const InternalLink = ({className, to, onClick, ...props}: InternalLinkProps) =>
11
+ to ? (
12
+ <Link to={to} onClick={onClick} className={bLink({view: 'normal'}, className)} {...props} />
13
+ ) : (
14
+ <span className={className} onClick={onClick}>
15
+ {props.children}
16
+ </span>
17
+ );
@@ -0,0 +1 @@
1
+ export * from './InternalLink';
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
- import InternalLink from '../InternalLink/InternalLink';
5
+ import {InternalLink} from '../InternalLink';
6
6
 
7
7
  import {getTabletLabel} from '../../utils/constants';
8
8
  import routes, {createHref} from '../../routes';
@@ -29,7 +29,7 @@ const prepareTablets = (tablets: ITablets) => {
29
29
 
30
30
  interface TabletsStatisticProps {
31
31
  tablets: ITablets;
32
- path: string;
32
+ path: string | undefined;
33
33
  nodeIds: string[] | number[];
34
34
  }
35
35
 
@@ -0,0 +1,15 @@
1
+ .ydb-bars {
2
+ display: flex;
3
+ flex-direction: row;
4
+ align-items: flex-end;
5
+
6
+ height: 20px;
7
+
8
+ &__value {
9
+ width: 6px;
10
+ min-height: 3px;
11
+ margin-right: 2px;
12
+
13
+ background-color: var(--yc-color-infographics-info-heavy);
14
+ }
15
+ }
@@ -0,0 +1,38 @@
1
+ import cn from 'bem-cn-lite';
2
+ import {useMemo} from 'react';
3
+
4
+ import './VerticalBars.scss';
5
+
6
+ const b = cn('ydb-bars');
7
+
8
+ const calculateValuesInPercents = (values: number[]) => {
9
+ const max = Math.max(...values);
10
+
11
+ if (!max) {
12
+ return values;
13
+ }
14
+
15
+ const res = [];
16
+
17
+ for (const value of values) {
18
+ res.push((value / max) * 100);
19
+ }
20
+
21
+ return res;
22
+ };
23
+
24
+ interface VerticalBarsProps {
25
+ values: number[];
26
+ }
27
+
28
+ export const VerticalBars = ({values}: VerticalBarsProps) => {
29
+ const preparedValues = useMemo(() => calculateValuesInPercents(values), [values]);
30
+
31
+ const getBars = () => {
32
+ return preparedValues.map((value, index) => {
33
+ return <div key={index} style={{height: `${value}%`}} className={b('value')} />;
34
+ });
35
+ };
36
+
37
+ return <div className={b()}>{getBars()}</div>;
38
+ };
@@ -0,0 +1 @@
1
+ export * from './VerticalBars';
@@ -2,10 +2,6 @@ import React from 'react';
2
2
  import {connect} from 'react-redux';
3
3
  import PropTypes from 'prop-types';
4
4
 
5
- import {configure as configureUiKit} from '@gravity-ui/uikit';
6
- import {configure as configureYdbUiComponents} from 'ydb-ui-components';
7
- import {i18n, Lang} from '../../utils/i18n';
8
-
9
5
  import ContentWrapper, {Content} from './Content';
10
6
  import AsideNavigation from '../AsideNavigation/AsideNavigation';
11
7
 
@@ -24,13 +20,6 @@ class App extends React.Component {
24
20
  children: PropTypes.node,
25
21
  };
26
22
 
27
- constructor(props) {
28
- super(props);
29
- i18n.setLang(Lang.En);
30
- configureYdbUiComponents({lang: Lang.En});
31
- configureUiKit({lang: Lang.En});
32
- }
33
-
34
23
  componentDidMount() {
35
24
  const {isAuthenticated, getUser} = this.props;
36
25
  if (isAuthenticated) {
@@ -1,6 +1,5 @@
1
1
  @import url('https://fonts.googleapis.com/css2?family=Rubik&display=swap');
2
2
  @import '../../styles/mixins.scss';
3
- @import './NodesTable';
4
3
 
5
4
  * {
6
5
  // FIXME: this is an overkill, potentially could break external components, needs refactoring
@@ -2,7 +2,7 @@ import cn from 'bem-cn-lite';
2
2
  //@ts-ignore
3
3
  import Tenants from '../Tenants/Tenants';
4
4
  //@ts-ignore
5
- import Nodes from '../Nodes/Nodes';
5
+ import {Nodes} from '../Nodes/Nodes';
6
6
  //@ts-ignore
7
7
  import Storage from '../Storage/Storage';
8
8
  import routes, {CLUSTER_PAGES} from '../../routes';
@@ -28,7 +28,7 @@ function Cluster(props: ClusterProps) {
28
28
  return <Tenants {...props} />;
29
29
  }
30
30
  case CLUSTER_PAGES.nodes.id: {
31
- return <Nodes {...props} />;
31
+ return <Nodes additionalNodesInfo={props.additionalNodesInfo} />;
32
32
  }
33
33
  case CLUSTER_PAGES.storage.id: {
34
34
  //@ts-ignore
@@ -1,7 +1,11 @@
1
1
  @import '../../styles/mixins.scss';
2
2
 
3
- .cluster-nodes {
3
+ .ydb-nodes {
4
4
  overflow: auto;
5
+ flex-grow: 1;
6
+
7
+ height: 100%;
8
+
5
9
  @include flex-container();
6
10
 
7
11
  &__search {
@@ -0,0 +1,196 @@
1
+ import {useCallback, useEffect} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+ import {useDispatch} from 'react-redux';
4
+
5
+ import DataTable from '@yandex-cloud/react-data-table';
6
+
7
+ import {AccessDenied} from '../../components/Errors/403';
8
+ import {Illustration} from '../../components/Illustration';
9
+ import {Loader} from '../../components/Loader';
10
+
11
+ import {Search} from '../../components/Search';
12
+ import {ProblemFilter} from '../../components/ProblemFilter';
13
+ import {UptimeFilter} from '../../components/UptimeFIlter';
14
+ import {EntitiesCount} from '../../components/EntitiesCount';
15
+
16
+ import routes, {CLUSTER_PAGES, createHref} from '../../routes';
17
+
18
+ import {
19
+ ALL,
20
+ DEFAULT_TABLE_SETTINGS,
21
+ USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
22
+ } from '../../utils/constants';
23
+ import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
24
+ import {NodesUptimeFilterValues} from '../../utils/nodes';
25
+
26
+ import {setHeader} from '../../store/reducers/header';
27
+ import {
28
+ getNodes,
29
+ getFilteredPreparedNodesList,
30
+ setNodesUptimeFilter,
31
+ setSearchValue,
32
+ resetNodesState,
33
+ getComputeNodes,
34
+ } from '../../store/reducers/nodes';
35
+ import {changeFilter, getSettingValue} from '../../store/reducers/settings';
36
+ import {hideTooltip, showTooltip} from '../../store/reducers/tooltip';
37
+
38
+ import {getNodesColumns} from './getNodesColumns';
39
+
40
+ import './Nodes.scss';
41
+
42
+ import i18n from './i18n';
43
+
44
+ const b = cn('ydb-nodes');
45
+
46
+ interface IAdditionalNodesInfo extends Record<string, unknown> {
47
+ getNodeRef?: Function;
48
+ }
49
+
50
+ interface NodesProps {
51
+ tenantPath?: string;
52
+ className?: string;
53
+ additionalNodesInfo?: IAdditionalNodesInfo;
54
+ }
55
+
56
+ export const Nodes = ({tenantPath, className, additionalNodesInfo = {}}: NodesProps) => {
57
+ const dispatch = useDispatch();
58
+
59
+ const isClusterNodes = !tenantPath;
60
+
61
+ // Since Nodes component is used in several places,
62
+ // we need to reset filters, searchValue and loading state
63
+ // in nodes reducer when path changes
64
+ useEffect(() => {
65
+ dispatch(resetNodesState());
66
+ }, [dispatch, tenantPath]);
67
+
68
+ const {wasLoaded, loading, error, nodesUptimeFilter, searchValue, totalNodes} =
69
+ useTypedSelector((state) => state.nodes);
70
+ const problemFilter = useTypedSelector((state) => state.settings.problemFilter);
71
+ const {autorefresh} = useTypedSelector((state) => state.schema);
72
+
73
+ const nodes = useTypedSelector(getFilteredPreparedNodesList);
74
+
75
+ const useNodesEndpoint = useTypedSelector((state) =>
76
+ getSettingValue(state, USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY),
77
+ );
78
+
79
+ const fetchNodes = useCallback(() => {
80
+ if (tenantPath && !JSON.parse(useNodesEndpoint)) {
81
+ dispatch(getComputeNodes(tenantPath));
82
+ } else {
83
+ dispatch(getNodes({tenant: tenantPath}));
84
+ }
85
+ }, [dispatch, tenantPath, useNodesEndpoint]);
86
+
87
+ useAutofetcher(fetchNodes, [fetchNodes], isClusterNodes ? true : autorefresh);
88
+
89
+ useEffect(() => {
90
+ if (isClusterNodes) {
91
+ dispatch(
92
+ setHeader([
93
+ {
94
+ text: CLUSTER_PAGES.nodes.title,
95
+ link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.nodes.id}),
96
+ },
97
+ ]),
98
+ );
99
+ }
100
+ }, [dispatch, isClusterNodes]);
101
+
102
+ const handleSearchQueryChange = (value: string) => {
103
+ dispatch(setSearchValue(value));
104
+ };
105
+
106
+ const handleProblemFilterChange = (value: string) => {
107
+ dispatch(changeFilter(value));
108
+ };
109
+
110
+ const handleUptimeFilterChange = (value: string) => {
111
+ dispatch(setNodesUptimeFilter(value as NodesUptimeFilterValues));
112
+ };
113
+
114
+ const renderControls = () => {
115
+ return (
116
+ <div className={b('controls')}>
117
+ <Search
118
+ onChange={handleSearchQueryChange}
119
+ placeholder="Host name"
120
+ className={b('search')}
121
+ value={searchValue}
122
+ />
123
+ <ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
124
+ <UptimeFilter value={nodesUptimeFilter} onChange={handleUptimeFilterChange} />
125
+ <EntitiesCount
126
+ total={totalNodes}
127
+ current={nodes?.length || 0}
128
+ label={'Nodes'}
129
+ loading={loading && !wasLoaded}
130
+ />
131
+ </div>
132
+ );
133
+ };
134
+
135
+ const onShowTooltip = (...args: Parameters<typeof showTooltip>) => {
136
+ dispatch(showTooltip(...args));
137
+ };
138
+
139
+ const onHideTooltip = () => {
140
+ dispatch(hideTooltip());
141
+ };
142
+
143
+ const renderTable = () => {
144
+ const columns = getNodesColumns({
145
+ showTooltip: onShowTooltip,
146
+ hideTooltip: onHideTooltip,
147
+ getNodeRef: additionalNodesInfo.getNodeRef,
148
+ });
149
+
150
+ if (nodes && nodes.length === 0) {
151
+ if (problemFilter !== ALL || nodesUptimeFilter !== NodesUptimeFilterValues.All) {
152
+ return <Illustration name="thumbsUp" width="200" />;
153
+ }
154
+ }
155
+
156
+ return (
157
+ <div className={b('table-wrapper')}>
158
+ <div className={b('table-content')}>
159
+ <DataTable
160
+ theme="yandex-cloud"
161
+ data={nodes || []}
162
+ columns={columns}
163
+ settings={DEFAULT_TABLE_SETTINGS}
164
+ initialSortOrder={{
165
+ columnId: 'NodeId',
166
+ order: DataTable.ASCENDING,
167
+ }}
168
+ emptyDataMessage={i18n('empty.default')}
169
+ />
170
+ </div>
171
+ </div>
172
+ );
173
+ };
174
+
175
+ const renderContent = () => {
176
+ return (
177
+ <div className={b(null, className)}>
178
+ {renderControls()}
179
+ {renderTable()}
180
+ </div>
181
+ );
182
+ };
183
+
184
+ if (loading && !wasLoaded) {
185
+ return <Loader size={isClusterNodes ? 'l' : 'm'} />;
186
+ }
187
+
188
+ if (error) {
189
+ if (error.status === 403) {
190
+ return <AccessDenied />;
191
+ }
192
+ return <div>{error.statusText}</div>;
193
+ }
194
+
195
+ return renderContent();
196
+ };
@@ -1,4 +1,4 @@
1
- .kv-nodes {
1
+ .ydb-nodes-table {
2
2
  &__host-name-wrapper {
3
3
  display: flex;
4
4
  }
@@ -20,7 +20,7 @@
20
20
  }
21
21
 
22
22
  .data-table__row:hover {
23
- .kv-nodes__external-button {
23
+ .ydb-nodes-table__external-button {
24
24
  display: inline-flex;
25
25
  }
26
26
  }