ydb-embedded-ui 3.4.4 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/components/Icon/Icon.tsx +6 -0
  3. package/dist/components/InfoViewer/InfoViewer.tsx +2 -2
  4. package/dist/components/InfoViewer/formatters/index.ts +0 -1
  5. package/dist/components/InfoViewer/formatters/table.ts +6 -0
  6. package/dist/components/LabelWithPopover/LabelWithPopover.tsx +3 -7
  7. package/dist/components/LagPopoverContent/LagPopoverContent.scss +13 -0
  8. package/dist/components/LagPopoverContent/LagPopoverContent.tsx +19 -0
  9. package/dist/components/LagPopoverContent/index.ts +1 -0
  10. package/dist/components/Tooltips/NodeEndpointsTooltip/NodeEndpointsTooltip.scss +5 -0
  11. package/dist/components/Tooltips/NodeEndpointsTooltip/NodeEndpointsTooltip.tsx +31 -0
  12. package/dist/components/TruncatedQuery/TruncatedQuery.js +1 -1
  13. package/dist/components/TruncatedQuery/TruncatedQuery.scss +7 -3
  14. package/dist/containers/Node/{NodePages.js → NodePages.ts} +1 -1
  15. package/dist/containers/Nodes/Nodes.tsx +2 -6
  16. package/dist/containers/Nodes/NodesTable.scss +11 -10
  17. package/dist/containers/Nodes/getNodesColumns.tsx +29 -24
  18. package/dist/containers/Storage/PDisk/PDisk.scss +2 -0
  19. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +6 -9
  20. package/dist/containers/Storage/Storage.js +12 -5
  21. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +3 -1
  22. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +20 -7
  23. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +43 -7
  24. package/dist/containers/Storage/VDisk/VDisk.tsx +3 -2
  25. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +4 -2
  26. package/dist/containers/Tablet/TabletControls/TabletControls.tsx +2 -2
  27. package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.scss +0 -8
  28. package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.tsx +3 -10
  29. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +1 -1
  30. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +11 -43
  31. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/TableInfo.tsx +19 -17
  32. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +192 -37
  33. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +51 -32
  34. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/en.json +2 -1
  35. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/ru.json +2 -1
  36. package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.scss +0 -8
  37. package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.tsx +7 -21
  38. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.scss +20 -14
  39. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +49 -12
  40. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +37 -18
  41. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +1 -0
  42. package/dist/routes.ts +1 -1
  43. package/dist/services/api.d.ts +4 -0
  44. package/dist/services/api.js +3 -3
  45. package/dist/store/reducers/{executeQuery.js → executeQuery.ts} +51 -21
  46. package/dist/store/reducers/executeTopQueries.ts +5 -1
  47. package/dist/store/reducers/{nodesList.js → nodesList.ts} +19 -7
  48. package/dist/store/reducers/{olapStats.js → olapStats.ts} +8 -18
  49. package/dist/store/reducers/settings.js +1 -1
  50. package/dist/store/reducers/storage.js +8 -18
  51. package/dist/types/api/nodesList.ts +25 -0
  52. package/dist/types/api/query.ts +4 -1
  53. package/dist/types/api/schema.ts +523 -3
  54. package/dist/types/common.ts +1 -0
  55. package/dist/types/store/executeQuery.ts +42 -0
  56. package/dist/types/store/nodesList.ts +24 -0
  57. package/dist/types/store/olapStats.ts +14 -0
  58. package/dist/utils/index.js +9 -1
  59. package/dist/utils/nodes.ts +4 -0
  60. package/dist/utils/query.test.ts +42 -29
  61. package/dist/utils/query.ts +34 -22
  62. package/dist/utils/timeParsers/formatDuration.ts +30 -12
  63. package/dist/utils/timeParsers/i18n/en.json +4 -0
  64. package/dist/utils/timeParsers/i18n/ru.json +4 -0
  65. package/dist/utils/tooltip.js +2 -28
  66. package/package.json +1 -1
  67. package/dist/components/InfoViewer/formatters/topicStats.tsx +0 -29
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.5.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.5...v3.5.0) (2023-04-18)
4
+
5
+
6
+ ### Features
7
+
8
+ * **TableInfo:** extend Table and ColumnTable info ([89e54aa](https://github.com/ydb-platform/ydb-embedded-ui/commit/89e54aa97d7bcbabfd5100daeb1dc0c03608e86e))
9
+ * **TopQueries:** add columns ([b49b98d](https://github.com/ydb-platform/ydb-embedded-ui/commit/b49b98db2da08c355b23f4a33bf05247530543db))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **settings:** use system theme by default ([726c9cb](https://github.com/ydb-platform/ydb-embedded-ui/commit/726c9cb14d7f87cc9248340d1ebebfc8bf0d0384))
15
+ * **Storage:** fix incorrect usage on zero available space ([2704cd7](https://github.com/ydb-platform/ydb-embedded-ui/commit/2704cd7c696d337cc8e3af68941cf444f8dfae81))
16
+ * **TableInfo:** add default format for FollowerGroup fields ([961334a](https://github.com/ydb-platform/ydb-embedded-ui/commit/961334aabe89672994f0f3440e20602e180b3394))
17
+ * **Tablet:** fix dialog type enum ([c477042](https://github.com/ydb-platform/ydb-embedded-ui/commit/c477042cacc2e777cae4bd6981381a8042c603ed))
18
+ * **TopQueries:** enable go back to TopQueries from Query tab ([bbdfe72](https://github.com/ydb-platform/ydb-embedded-ui/commit/bbdfe726c9081f01422dca787b83399ea44b3956))
19
+ * **TopShards:** fix table crash on undefined values ([604e99a](https://github.com/ydb-platform/ydb-embedded-ui/commit/604e99a9427021c61ceb8ea366e316e629032b84))
20
+ * **TruncatedQuery:** wrap message ([f41b7ff](https://github.com/ydb-platform/ydb-embedded-ui/commit/f41b7ff33ac0145446ca89aab031036247f3ddf8))
21
+
22
+ ## [3.4.5](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.4...v3.4.5) (2023-03-30)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **Consumers:** fix typo ([aaa9dbd](https://github.com/ydb-platform/ydb-embedded-ui/commit/aaa9dbda1f28702917793a61bae2813f6ef018bb))
28
+ * **PDisk:** add display block to content ([130dab2](https://github.com/ydb-platform/ydb-embedded-ui/commit/130dab20ffdc9da77225c94a6e6064f0308a1c2a))
29
+ * **Storage:** get nodes hosts from /nodelist ([cc82dd9](https://github.com/ydb-platform/ydb-embedded-ui/commit/cc82dd93808133b0d1dcd21b31ee3744df4f7383))
30
+ * **StorageNodes:** make fqdn similar to nodes page ([344298a](https://github.com/ydb-platform/ydb-embedded-ui/commit/344298a9a29380f1068b002fa304cdcc221ce0d4))
31
+ * **TopicInfo:** do not display /s when speed is undefined ([2d41832](https://github.com/ydb-platform/ydb-embedded-ui/commit/2d4183247ec33acdfa45be72a93f0dbd93b716e0))
32
+ * **TopicStats:** use prepared stats, update fields ([a614a8c](https://github.com/ydb-platform/ydb-embedded-ui/commit/a614a8caa2744b844d97f23f25e5385387367d6b))
33
+
3
34
  ## [3.4.4](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.3...v3.4.4) (2023-03-22)
4
35
 
5
36
 
@@ -31,3 +31,9 @@ export const Icon = ({
31
31
  />
32
32
  );
33
33
  };
34
+
35
+ // When used with uikit components Icon is considered as text element and corresponding styles are applied
36
+ // IconWrapper overrides displayName to 'Icon', so it will be considered as an icon with right styles
37
+ export const IconWrapper = (props: IconProps) => <Icon {...props} />;
38
+
39
+ IconWrapper.displayName = 'Icon';
@@ -4,7 +4,7 @@ import cn from 'bem-cn-lite';
4
4
  import './InfoViewer.scss';
5
5
 
6
6
  export interface InfoViewerItem {
7
- label: string;
7
+ label: ReactNode;
8
8
  value: ReactNode;
9
9
  }
10
10
 
@@ -39,7 +39,7 @@ const InfoViewer = ({
39
39
  {info && info.length > 0 ? (
40
40
  <div className={b('items')}>
41
41
  {info.map((data, infoIndex) => (
42
- <div className={b('row')} key={data.label + infoIndex}>
42
+ <div className={b('row')} key={infoIndex}>
43
43
  <div className={b('label')}>
44
44
  <div className={b('label-text', {multiline: multilineLabels})}>
45
45
  {data.label}
@@ -1,6 +1,5 @@
1
1
  export * from './common';
2
2
  export * from './schema';
3
- export * from './topicStats';
4
3
  export * from './pqGroup';
5
4
  export * from './cdcStream';
6
5
  export * from './table';
@@ -20,6 +20,12 @@ export const formatFollowerGroupItem = createInfoFormatter<TFollowerGroup>({
20
20
  values: {
21
21
  FollowerCount: formatNumber,
22
22
  },
23
+ labels: {
24
+ // Make it shorter to fit label width
25
+ FollowerCountPerDataCenter: 'FollowerCountPerDC',
26
+ },
27
+ // Most of the FollowerGroup fields are arrays or boolean
28
+ defaultValueFormatter: (value) => value && String(value),
23
29
  });
24
30
 
25
31
  export const formatPartitionConfigItem = createInfoFormatter<TPartitionConfig>({
@@ -3,18 +3,14 @@ import type {ReactNode} from 'react';
3
3
  import {HelpPopover} from '@gravity-ui/uikit';
4
4
 
5
5
  interface LabelWithPopoverProps {
6
- headerText: string;
6
+ text: string;
7
7
  popoverContent: ReactNode;
8
8
  className?: string;
9
9
  }
10
10
 
11
- export const LabelWithPopover = ({
12
- headerText,
13
- popoverContent,
14
- className,
15
- }: LabelWithPopoverProps) => (
11
+ export const LabelWithPopover = ({text, popoverContent, className}: LabelWithPopoverProps) => (
16
12
  <div className={className}>
17
- {headerText}
13
+ {text}
18
14
  <HelpPopover content={popoverContent} />
19
15
  </div>
20
16
  );
@@ -0,0 +1,13 @@
1
+ .ydb-lag-popover-content {
2
+ &__text {
3
+ margin-bottom: 10px;
4
+ }
5
+
6
+ &_type_read {
7
+ max-width: 280px;
8
+ }
9
+
10
+ &_type_write {
11
+ max-width: 220px;
12
+ }
13
+ }
@@ -0,0 +1,19 @@
1
+ import block from 'bem-cn-lite';
2
+
3
+ import {ReadLagImage, WriteLagImage} from '../LagImages';
4
+
5
+ import './LagPopoverContent.scss';
6
+
7
+ const b = block('ydb-lag-popover-content');
8
+
9
+ interface LagPopoverContentProps {
10
+ text: string;
11
+ type: 'read' | 'write';
12
+ }
13
+
14
+ export const LagPopoverContent = ({text, type}: LagPopoverContentProps) => (
15
+ <div className={b({type})}>
16
+ <div className={b('text')}>{text}</div>
17
+ <div>{type === 'read' ? <ReadLagImage /> : <WriteLagImage />}</div>
18
+ </div>
19
+ );
@@ -0,0 +1 @@
1
+ export * from './LagPopoverContent';
@@ -0,0 +1,5 @@
1
+ .ydb-node-endpoints-tooltip {
2
+ .info-viewer__value {
3
+ min-width: 70px;
4
+ }
5
+ }
@@ -0,0 +1,31 @@
1
+ import block from 'bem-cn-lite';
2
+
3
+ import type {TSystemStateInfo} from '../../../types/api/nodes';
4
+
5
+ import {InfoViewer, InfoViewerItem} from '../../InfoViewer';
6
+
7
+ import './NodeEndpointsTooltip.scss';
8
+
9
+ const b = block('ydb-node-endpoints-tooltip');
10
+
11
+ interface NodeEdpointsTooltipProps {
12
+ data?: TSystemStateInfo;
13
+ }
14
+
15
+ export const NodeEndpointsTooltip = ({data}: NodeEdpointsTooltipProps) => {
16
+ const info: InfoViewerItem[] = [];
17
+
18
+ if (data?.Rack) {
19
+ info.push({label: 'Rack', value: data.Rack});
20
+ }
21
+
22
+ if (data?.Endpoints && data.Endpoints.length) {
23
+ data.Endpoints.forEach(({Name, Address}) => {
24
+ if (Name && Address) {
25
+ info.push({label: Name, value: Address});
26
+ }
27
+ });
28
+ }
29
+
30
+ return <InfoViewer className={b(null)} info={info} dots={false} size={'s'} />;
31
+ };
@@ -16,7 +16,7 @@ function TruncatedQuery({value, maxQueryHeight}) {
16
16
  return (
17
17
  <React.Fragment>
18
18
  <span className={b()}>{content}</span>
19
- <span className={b({color: 'secondary'})}>{message}</span>
19
+ <span className={b('message', {color: 'secondary'})}>{message}</span>
20
20
  </React.Fragment>
21
21
  );
22
22
  }
@@ -5,9 +5,13 @@
5
5
  white-space: pre;
6
6
  word-break: break-word;
7
7
 
8
- &_color {
9
- &_secondary {
10
- color: var(--yc-color-text-secondary);
8
+ &__message {
9
+ white-space: pre-wrap;
10
+
11
+ &_color {
12
+ &_secondary {
13
+ color: var(--yc-color-text-secondary);
14
+ }
11
15
  }
12
16
  }
13
17
  }
@@ -21,7 +21,7 @@ export const NODE_PAGES = [
21
21
  },
22
22
  ];
23
23
 
24
- export function getDefaultNodePath(nodeId) {
24
+ export function getDefaultNodePath(nodeId: string | number) {
25
25
  return createHref(routes.node, {
26
26
  id: nodeId,
27
27
  activeTab: OVERVIEW,
@@ -22,7 +22,7 @@ import {
22
22
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
23
23
  } from '../../utils/constants';
24
24
  import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
25
- import {isUnavailableNode, NodesUptimeFilterValues} from '../../utils/nodes';
25
+ import {AdditionalNodesInfo, isUnavailableNode, NodesUptimeFilterValues} from '../../utils/nodes';
26
26
 
27
27
  import {setHeader} from '../../store/reducers/header';
28
28
  import {
@@ -46,15 +46,11 @@ import i18n from './i18n';
46
46
 
47
47
  const b = cn('ydb-nodes');
48
48
 
49
- interface IAdditionalNodesInfo extends Record<string, unknown> {
50
- getNodeRef?: Function;
51
- }
52
-
53
49
  interface NodesProps {
54
50
  path?: string;
55
51
  type?: EPathType;
56
52
  className?: string;
57
- additionalNodesInfo?: IAdditionalNodesInfo;
53
+ additionalNodesInfo?: AdditionalNodesInfo;
58
54
  }
59
55
 
60
56
  export const Nodes = ({path, type, className, additionalNodesInfo = {}}: NodesProps) => {
@@ -1,22 +1,23 @@
1
1
  .ydb-nodes-table {
2
- &__host-name-wrapper {
2
+ &__host-field-wrapper {
3
3
  display: flex;
4
4
  }
5
5
 
6
- &__external-button {
7
- display: none;
8
- align-items: center;
9
-
10
- margin-left: 4px;
6
+ &__host-wrapper {
7
+ display: flex;
11
8
 
12
- .yc-button__text {
13
- margin: 0 4px;
14
- }
9
+ max-width: 330px;
15
10
  }
16
11
 
17
- &__host-name {
12
+ &__host {
18
13
  overflow: hidden;
19
14
  }
15
+
16
+ &__external-button {
17
+ display: none;
18
+
19
+ margin-left: 4px;
20
+ }
20
21
  }
21
22
 
22
23
  .data-table__row:hover {
@@ -1,12 +1,13 @@
1
1
  import cn from 'bem-cn-lite';
2
2
  import DataTable, {Column} from '@gravity-ui/react-data-table';
3
- import {Button, Popover} from '@gravity-ui/uikit';
3
+ import {Button, Popover, PopoverBehavior} from '@gravity-ui/uikit';
4
4
 
5
- import {Icon} from '../../components/Icon';
5
+ import {IconWrapper} from '../../components/Icon';
6
6
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
7
7
  import PoolsGraph from '../../components/PoolsGraph/PoolsGraph';
8
8
  import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
9
9
  import {TabletsStatistic} from '../../components/TabletsStatistic';
10
+ import {NodeEndpointsTooltip} from '../../components/Tooltips/NodeEndpointsTooltip/NodeEndpointsTooltip';
10
11
 
11
12
  import {formatBytesToGigabyte} from '../../utils/index';
12
13
  import {INodesPreparedEntity} from '../../types/store/nodes';
@@ -46,28 +47,32 @@ export function getNodesColumns({
46
47
  return <span>—</span>;
47
48
  }
48
49
  return (
49
- <div className={b('host-name-wrapper')}>
50
- <EntityStatus
51
- name={row.Host}
52
- onNameMouseEnter={(e: MouseEvent) =>
53
- showTooltip(e.target, row, 'nodeEndpoints')
54
- }
55
- onNameMouseLeave={hideTooltip}
56
- status={row.SystemState}
57
- path={getDefaultNodePath(row.NodeId)}
58
- hasClipboardButton
59
- className={b('host-name')}
60
- />
61
- {nodeRef && (
62
- <Button
63
- size="s"
64
- href={nodeRef}
65
- className={b('external-button')}
66
- target="_blank"
67
- >
68
- <Icon name="external" />
69
- </Button>
70
- )}
50
+ <div className={b('host-field-wrapper')}>
51
+ <Popover
52
+ content={<NodeEndpointsTooltip data={row} />}
53
+ placement={['top', 'bottom']}
54
+ behavior={PopoverBehavior.Immediate}
55
+ >
56
+ <div className={b('host-wrapper')}>
57
+ <EntityStatus
58
+ name={row.Host}
59
+ status={row.SystemState}
60
+ path={getDefaultNodePath(row.NodeId)}
61
+ hasClipboardButton
62
+ className={b('host')}
63
+ />
64
+ {nodeRef && (
65
+ <Button
66
+ size="s"
67
+ href={nodeRef}
68
+ className={b('external-button')}
69
+ target="_blank"
70
+ >
71
+ <IconWrapper name="external" />
72
+ </Button>
73
+ )}
74
+ </div>
75
+ </Popover>
71
76
  </div>
72
77
  );
73
78
  },
@@ -6,6 +6,8 @@
6
6
  &__content {
7
7
  position: relative;
8
8
 
9
+ display: block;
10
+
9
11
  border-radius: 4px; // to match interactive area with disk shape
10
12
  }
11
13
 
@@ -3,6 +3,8 @@ import cn from 'bem-cn-lite';
3
3
 
4
4
  import {Popup, PopupProps} from '@gravity-ui/uikit';
5
5
 
6
+ import type {NodesMap} from '../../../types/store/nodesList';
7
+
6
8
  import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
7
9
 
8
10
  import {EFlag} from '../../../types/api/enums';
@@ -17,12 +19,7 @@ const b = cn('pdisk-storage-popup');
17
19
 
18
20
  const errorColors = [EFlag.Orange, EFlag.Red, EFlag.Yellow];
19
21
 
20
- export type NodesHosts = {
21
- // NodeId => Host
22
- [nodeId: number]: string;
23
- }
24
-
25
- export const preparePDiskData = (data: TPDiskStateInfo, nodes?: NodesHosts) => {
22
+ export const preparePDiskData = (data: TPDiskStateInfo, nodes?: NodesMap) => {
26
23
  const {AvailableSize, TotalSize, State, PDiskId, NodeId, Path, Realtime, Device} = data;
27
24
 
28
25
  const pdiskData: InfoViewerItem[] = [
@@ -35,8 +32,8 @@ export const preparePDiskData = (data: TPDiskStateInfo, nodes?: NodesHosts) => {
35
32
  pdiskData.push({label: 'Node Id', value: NodeId});
36
33
  }
37
34
 
38
- if (nodes && NodeId && nodes[NodeId]) {
39
- pdiskData.push({label: 'Host', value: nodes[NodeId]});
35
+ if (nodes && NodeId && nodes.get(NodeId)) {
36
+ pdiskData.push({label: 'Host', value: nodes.get(NodeId)});
40
37
  }
41
38
 
42
39
  if (Path) {
@@ -61,7 +58,7 @@ export const preparePDiskData = (data: TPDiskStateInfo, nodes?: NodesHosts) => {
61
58
 
62
59
  interface PDiskPopupProps extends PopupProps {
63
60
  data: TPDiskStateInfo;
64
- nodes?: NodesHosts;
61
+ nodes?: NodesMap;
65
62
  }
66
63
 
67
64
  export const PDiskPopup = ({data, nodes, ...props}: PDiskPopupProps) => {
@@ -22,7 +22,6 @@ import {
22
22
  setVisibleEntities,
23
23
  setStorageFilter,
24
24
  setUsageFilter,
25
- getNodesObject,
26
25
  StorageTypes,
27
26
  setStorageType,
28
27
  setNodesUptimeFilter,
@@ -32,7 +31,7 @@ import {
32
31
  getStorageNodesCount,
33
32
  getUsageFilterOptions,
34
33
  } from '../../store/reducers/storage';
35
- import {getNodesList} from '../../store/reducers/nodesList';
34
+ import {getNodesList, selectNodesMap} from '../../store/reducers/nodesList';
36
35
  import StorageGroups from './StorageGroups/StorageGroups';
37
36
  import StorageNodes from './StorageNodes/StorageNodes';
38
37
  import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
@@ -74,6 +73,7 @@ class Storage extends React.Component {
74
73
  nodesUptimeFilter: PropTypes.string,
75
74
  setNodesUptimeFilter: PropTypes.func,
76
75
  setDataWasNotLoaded: PropTypes.func,
76
+ additionalNodesInfo: PropTypes.object,
77
77
  };
78
78
 
79
79
  componentDidMount() {
@@ -181,8 +181,14 @@ class Storage extends React.Component {
181
181
  }
182
182
 
183
183
  renderDataTable() {
184
- const {flatListStorageEntities, visibleEntities, nodesUptimeFilter, nodes, storageType} =
185
- this.props;
184
+ const {
185
+ flatListStorageEntities,
186
+ visibleEntities,
187
+ nodesUptimeFilter,
188
+ nodes,
189
+ storageType,
190
+ additionalNodesInfo,
191
+ } = this.props;
186
192
 
187
193
  return (
188
194
  <div className={b('table-wrapper')}>
@@ -202,6 +208,7 @@ class Storage extends React.Component {
202
208
  data={flatListStorageEntities}
203
209
  tableSettings={tableSettings}
204
210
  onShowAll={this.onShowAllNodes}
211
+ additionalNodesInfo={additionalNodesInfo}
205
212
  />
206
213
  )}
207
214
  </div>
@@ -345,7 +352,7 @@ function mapStateToProps(state) {
345
352
  flatListStorageEntities: getFilteredEntities(state),
346
353
  groupsCount: getStoragePoolsGroupsCount(state),
347
354
  autorefresh: state.schema.autorefresh,
348
- nodes: getNodesObject(state),
355
+ nodes: selectNodesMap(state),
349
356
  nodesCount: getStorageNodesCount(state),
350
357
  loading,
351
358
  wasLoaded,
@@ -3,6 +3,8 @@ import cn from 'bem-cn-lite';
3
3
  import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
4
4
  import {Icon, Label, Popover, PopoverBehavior} from '@gravity-ui/uikit';
5
5
 
6
+ import type {NodesMap} from '../../../types/store/nodesList';
7
+
6
8
  import shieldIcon from '../../../assets/icons/shield.svg';
7
9
 
8
10
  import {Stack} from '../../../components/Stack/Stack';
@@ -45,7 +47,7 @@ type TableColumnsIdsValues = typeof TableColumnsIds[TableColumnsIdsKeys];
45
47
 
46
48
  interface StorageGroupsProps {
47
49
  data: any;
48
- nodes: any;
50
+ nodes: NodesMap;
49
51
  tableSettings: Settings;
50
52
  visibleEntities: keyof typeof VisibleEntities;
51
53
  onShowAll?: VoidFunction;
@@ -20,18 +20,19 @@
20
20
  margin-right: 0px;
21
21
  }
22
22
  }
23
- &__fqdn-wrapper {
23
+ &__fqdn-field-wrapper {
24
24
  width: 330px;
25
25
  }
26
- &__fqdn {
27
- display: inline-block;
28
- overflow: hidden;
26
+ &__fqdn-wrapper {
27
+ display: flex;
28
+ align-items: center;
29
29
 
30
30
  max-width: 330px;
31
-
32
- vertical-align: top;
33
- text-overflow: ellipsis;
34
31
  }
32
+ &__fqdn {
33
+ overflow: hidden;
34
+ }
35
+
35
36
  &__group-id {
36
37
  font-weight: 500;
37
38
  }
@@ -39,4 +40,16 @@
39
40
  &__node_unavailable {
40
41
  opacity: 0.6;
41
42
  }
43
+
44
+ &__external-button {
45
+ display: none;
46
+
47
+ margin-left: 4px;
48
+ }
49
+ }
50
+
51
+ .data-table__row:hover {
52
+ .global-storage-nodes__external-button {
53
+ display: inline-flex;
54
+ }
42
55
  }
@@ -2,10 +2,20 @@ import _ from 'lodash';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
4
  import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
5
- import {Popover, PopoverBehavior} from '@gravity-ui/uikit';
5
+ import {Button, Popover, PopoverBehavior} from '@gravity-ui/uikit';
6
+
7
+ import {NodeEndpointsTooltip} from '../../../components/Tooltips/NodeEndpointsTooltip/NodeEndpointsTooltip';
8
+ import EntityStatus from '../../../components/EntityStatus/EntityStatus';
9
+ import {IconWrapper} from '../../../components/Icon';
6
10
 
7
11
  import {VisibleEntities} from '../../../store/reducers/storage';
8
- import {isUnavailableNode, NodesUptimeFilterValues} from '../../../utils/nodes';
12
+ import {
13
+ AdditionalNodesInfo,
14
+ isUnavailableNode,
15
+ NodesUptimeFilterValues,
16
+ } from '../../../utils/nodes';
17
+
18
+ import {getDefaultNodePath} from '../../Node/NodePages';
9
19
 
10
20
  import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
11
21
  import {PDisk} from '../PDisk';
@@ -33,6 +43,7 @@ interface StorageNodesProps {
33
43
  visibleEntities: keyof typeof VisibleEntities;
34
44
  nodesUptimeFilter: keyof typeof NodesUptimeFilterValues;
35
45
  onShowAll?: VoidFunction;
46
+ additionalNodesInfo?: AdditionalNodesInfo;
36
47
  }
37
48
 
38
49
  const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
@@ -73,7 +84,10 @@ function StorageNodes({
73
84
  visibleEntities,
74
85
  onShowAll,
75
86
  nodesUptimeFilter,
87
+ additionalNodesInfo,
76
88
  }: StorageNodesProps) {
89
+ const getNodeRef = additionalNodesInfo?.getNodeRef;
90
+
77
91
  const allColumns: Column<any>[] = [
78
92
  {
79
93
  name: TableColumnsIds.NodeId,
@@ -85,15 +99,37 @@ function StorageNodes({
85
99
  name: TableColumnsIds.FQDN,
86
100
  header: tableColumnsNames[TableColumnsIds.FQDN],
87
101
  width: 350,
88
- render: ({value}) => {
102
+ render: ({row}) => {
103
+ const nodeRef = getNodeRef ? getNodeRef(row) + 'internal' : undefined;
104
+ if (!row.Host) {
105
+ return <span>—</span>;
106
+ }
89
107
  return (
90
- <div className={b('fqdn-wrapper')}>
108
+ <div className={b('fqdn-field-wrapper')}>
91
109
  <Popover
92
- content={value as string}
93
- placement={['right']}
110
+ content={<NodeEndpointsTooltip data={row} />}
111
+ placement={['top', 'bottom']}
94
112
  behavior={PopoverBehavior.Immediate}
95
113
  >
96
- <span className={b('fqdn')}>{value as string}</span>
114
+ <div className={b('fqdn-wrapper')}>
115
+ <EntityStatus
116
+ name={row.Host}
117
+ showStatus={false}
118
+ path={getDefaultNodePath(row.NodeId)}
119
+ hasClipboardButton
120
+ className={b('fqdn')}
121
+ />
122
+ {nodeRef && (
123
+ <Button
124
+ size="s"
125
+ href={nodeRef}
126
+ className={b('external-button')}
127
+ target="_blank"
128
+ >
129
+ <IconWrapper name="external" />
130
+ </Button>
131
+ )}
132
+ </div>
97
133
  </Popover>
98
134
  </div>
99
135
  );
@@ -1,6 +1,8 @@
1
1
  import React, {useEffect, useState, useRef, useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
+ import type {NodesMap} from '../../../types/store/nodesList';
5
+
4
6
  import {InternalLink} from '../../../components/InternalLink';
5
7
 
6
8
  import routes, {createHref} from '../../../routes';
@@ -12,7 +14,6 @@ import {isFullVDiskData} from '../../../utils/storage';
12
14
  import {STRUCTURE} from '../../Node/NodePages';
13
15
 
14
16
  import {DiskStateProgressBar, EDiskStateSeverity} from '../DiskStateProgressBar';
15
- import type {NodesHosts} from '../PDiskPopup';
16
17
  import {VDiskPopup} from '../VDiskPopup';
17
18
 
18
19
  import type {IUnavailableDonor} from '../utils/types';
@@ -50,7 +51,7 @@ const getColorSeverity = (color?: EFlag) => {
50
51
  interface VDiskProps {
51
52
  data?: TVDiskStateInfo | IUnavailableDonor;
52
53
  poolName?: string;
53
- nodes?: NodesHosts;
54
+ nodes?: NodesMap;
54
55
  compact?: boolean;
55
56
  }
56
57