ydb-embedded-ui 4.9.0 → 4.10.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 (64) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +7 -4
  3. package/dist/components/EntityStatus/EntityStatus.js +3 -1
  4. package/dist/components/FormattedBytes/FormattedBytes.tsx +10 -0
  5. package/dist/components/FormattedBytes/utils.tsx +13 -0
  6. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +73 -0
  7. package/dist/components/InfoViewer/formatters/table.ts +6 -5
  8. package/dist/components/ProblemFilter/ProblemFilter.tsx +2 -2
  9. package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +4 -4
  10. package/dist/components/TruncatedQuery/{TruncatedQuery.js → TruncatedQuery.tsx} +10 -8
  11. package/dist/containers/AsideNavigation/AsideNavigation.tsx +6 -6
  12. package/dist/containers/Cluster/Cluster.tsx +10 -6
  13. package/dist/containers/Node/Node.tsx +3 -3
  14. package/dist/containers/Nodes/Nodes.tsx +2 -2
  15. package/dist/containers/Storage/PDisk/PDisk.tsx +2 -7
  16. package/dist/containers/Storage/Storage.tsx +240 -0
  17. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +45 -40
  18. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +12 -16
  19. package/dist/containers/Storage/UsageFilter/UsageFilter.scss +1 -0
  20. package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +17 -17
  21. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
  22. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -4
  23. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +0 -15
  24. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +10 -3
  25. package/dist/containers/Tenant/{Preview → Query/Preview}/Preview.scss +1 -1
  26. package/dist/containers/Tenant/Query/Preview/Preview.tsx +121 -0
  27. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +1 -1
  28. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +6 -8
  29. package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +1 -1
  30. package/dist/containers/Tenant/Query/i18n/en.json +8 -1
  31. package/dist/containers/Tenant/Query/i18n/ru.json +8 -1
  32. package/dist/containers/Tenants/Tenants.tsx +269 -0
  33. package/dist/services/api.ts +8 -3
  34. package/dist/store/reducers/nodes/nodes.ts +4 -4
  35. package/dist/store/reducers/partitions/types.ts +3 -3
  36. package/dist/store/reducers/settings/settings.ts +4 -2
  37. package/dist/store/reducers/settings/types.ts +3 -1
  38. package/dist/store/reducers/storage/selectors.ts +279 -0
  39. package/dist/store/reducers/storage/storage.ts +191 -0
  40. package/dist/store/reducers/storage/types.ts +80 -0
  41. package/dist/store/reducers/tenants/selectors.ts +46 -0
  42. package/dist/store/reducers/tenants/tenants.ts +21 -14
  43. package/dist/store/reducers/tenants/types.ts +20 -5
  44. package/dist/store/reducers/tenants/utils.ts +68 -0
  45. package/dist/types/additionalProps.ts +8 -0
  46. package/dist/types/api/storage.ts +1 -1
  47. package/dist/types/store/topic.ts +3 -3
  48. package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +38 -0
  49. package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +2 -2
  50. package/dist/utils/bytesParsers/formatBytes.ts +132 -0
  51. package/dist/utils/bytesParsers/i18n/en.json +1 -0
  52. package/dist/utils/bytesParsers/i18n/ru.json +1 -0
  53. package/dist/utils/bytesParsers/index.ts +1 -1
  54. package/dist/utils/index.js +5 -10
  55. package/dist/utils/numeral.ts +8 -0
  56. package/package.json +1 -1
  57. package/dist/components/FullNodeViewer/FullNodeViewer.js +0 -89
  58. package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
  59. package/dist/containers/Node/NodeOverview/NodeOverview.tsx +0 -21
  60. package/dist/containers/Storage/Storage.js +0 -350
  61. package/dist/containers/Tenant/Preview/Preview.js +0 -168
  62. package/dist/containers/Tenants/Tenants.js +0 -363
  63. package/dist/store/reducers/storage/storage.js +0 -404
  64. package/dist/utils/bytesParsers/formatBytesCustom.ts +0 -57
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.10.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.9.0...v4.10.0) (2023-07-07)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **AsideNavigation:** swap icons ([#465](https://github.com/ydb-platform/ydb-embedded-ui/issues/465)) ([13bc92a](https://github.com/ydb-platform/ydb-embedded-ui/commit/13bc92a0150ee8d809b3811b528f5d31f4999815))
9
+ * move sendQuery timeout to request query ([#464](https://github.com/ydb-platform/ydb-embedded-ui/issues/464)) ([6323038](https://github.com/ydb-platform/ydb-embedded-ui/commit/6323038b9e327a9e348812b43514008e9d07640c))
10
+ * **QueryEditor:** do not reset input on empty savedPath ([#451](https://github.com/ydb-platform/ydb-embedded-ui/issues/451)) ([7f98e44](https://github.com/ydb-platform/ydb-embedded-ui/commit/7f98e44834b54bfc1398bb418909fae21e22a3dc))
11
+ * show 5 digits size in table info ([#461](https://github.com/ydb-platform/ydb-embedded-ui/issues/461)) ([8c4ecc4](https://github.com/ydb-platform/ydb-embedded-ui/commit/8c4ecc41ed41cad34debaa6ff7f39f1f10f8d974))
12
+ * **TenantOverview:** add copy button to tenant name ([#459](https://github.com/ydb-platform/ydb-embedded-ui/issues/459)) ([2d8b380](https://github.com/ydb-platform/ydb-embedded-ui/commit/2d8b38049a038fb889e82d0135c026462107a124))
13
+ * **UsageFilter:** fix bar flashes ([#457](https://github.com/ydb-platform/ydb-embedded-ui/issues/457)) ([ae1965e](https://github.com/ydb-platform/ydb-embedded-ui/commit/ae1965ec894c7d012f0ebfc5949b73d4499b390e))
14
+
3
15
  ## [4.9.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.8.2...v4.9.0) (2023-06-30)
4
16
 
5
17
 
@@ -1,5 +1,8 @@
1
1
  import cn from 'bem-cn-lite';
2
2
 
3
+ import type {TSystemStateInfo} from '../../types/api/nodes';
4
+ import type {AdditionalNodesInfo} from '../../utils/nodes';
5
+
3
6
  import EntityStatus from '../EntityStatus/EntityStatus';
4
7
  import {Tags} from '../Tags';
5
8
  import {Icon} from '../Icon';
@@ -9,8 +12,8 @@ import './BasicNodeViewer.scss';
9
12
  const b = cn('basic-node-viewer');
10
13
 
11
14
  interface BasicNodeViewerProps {
12
- node: any;
13
- additionalNodesInfo?: any;
15
+ node: TSystemStateInfo;
16
+ additionalNodesInfo?: AdditionalNodesInfo;
14
17
  className?: string;
15
18
  }
16
19
 
@@ -41,8 +44,8 @@ export const BasicNodeViewer = ({node, additionalNodesInfo, className}: BasicNod
41
44
  <label>{node.NodeId}</label>
42
45
  </div>
43
46
 
44
- <Tags tags={[node.DataCenter]} />
45
- <Tags tags={node.Roles} tagsType="blue" />
47
+ {node.DataCenter && <Tags tags={[node.DataCenter]} />}
48
+ {node.Roles && <Tags tags={node.Roles} tagsType="blue" />}
46
49
  </>
47
50
  ) : (
48
51
  <div className="error">no data</div>
@@ -31,6 +31,7 @@ class EntityStatus extends React.Component {
31
31
  label: PropTypes.string,
32
32
  iconPath: PropTypes.string,
33
33
  hasClipboardButton: PropTypes.bool,
34
+ clipboardButtonAlwaysVisible: PropTypes.bool,
34
35
  showStatus: PropTypes.bool,
35
36
  externalLink: PropTypes.bool,
36
37
  className: PropTypes.string,
@@ -47,6 +48,7 @@ class EntityStatus extends React.Component {
47
48
  externalLink: false,
48
49
  mode: 'color',
49
50
  withLeftTrim: false,
51
+ clipboardButtonAlwaysVisible: false,
50
52
  };
51
53
  renderIcon() {
52
54
  const {status, size, showStatus, mode} = this.props;
@@ -123,7 +125,7 @@ class EntityStatus extends React.Component {
123
125
  component="span"
124
126
  size="s"
125
127
  className={b('clipboard-button', {
126
- visible: false,
128
+ visible: this.props.clipboardButtonAlwaysVisible,
127
129
  })}
128
130
  >
129
131
  <ClipboardButton text={name} size={16} />
@@ -0,0 +1,10 @@
1
+ import {FormatBytesArgs, formatBytes} from '../../utils/bytesParsers';
2
+
3
+ type FormattedBytesProps = FormatBytesArgs;
4
+
5
+ export const FormattedBytes = ({value, withSpeedLabel, ...params}: FormattedBytesProps) => {
6
+ const formatted = formatBytes({value, withSpeedLabel, ...params});
7
+ const bytes = formatBytes({value, withSpeedLabel, size: 'b'});
8
+
9
+ return <span title={bytes}>{formatted}</span>;
10
+ };
@@ -0,0 +1,13 @@
1
+ import type {FormatBytesArgs} from '../../utils/bytesParsers';
2
+ import {FormattedBytes} from './FormattedBytes';
3
+
4
+ export const toFormattedSize = (
5
+ value: number | string | undefined,
6
+ params?: Omit<FormatBytesArgs, 'value'>,
7
+ ) => {
8
+ if (!value) {
9
+ return null;
10
+ }
11
+
12
+ return <FormattedBytes value={value} significantDigits={2} {...params} />;
13
+ };
@@ -0,0 +1,73 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import type {TSystemStateInfo} from '../../types/api/nodes';
4
+
5
+ import {LOAD_AVERAGE_TIME_INTERVALS} from '../../utils/constants';
6
+ import {calcUptime} from '../../utils';
7
+
8
+ import InfoViewer from '../InfoViewer/InfoViewer';
9
+ import ProgressViewer from '../ProgressViewer/ProgressViewer';
10
+ import {PoolUsage} from '../PoolUsage/PoolUsage';
11
+
12
+ import './FullNodeViewer.scss';
13
+
14
+ const b = cn('full-node-viewer');
15
+
16
+ interface FullNodeViewerProps {
17
+ node: TSystemStateInfo | undefined;
18
+ className?: string;
19
+ }
20
+
21
+ export const FullNodeViewer = ({node, className}: FullNodeViewerProps) => {
22
+ const endpointsInfo = node?.Endpoints?.map(({Name, Address}) => ({
23
+ label: Name,
24
+ value: Address,
25
+ }));
26
+
27
+ const commonInfo = [
28
+ {label: 'Version', value: node?.Version},
29
+ {label: 'Uptime', value: calcUptime(node?.StartTime)},
30
+ {label: 'DC', value: node?.DataCenterDescription},
31
+ {label: 'Rack', value: node?.Rack},
32
+ ];
33
+
34
+ const averageInfo = node?.LoadAverage?.map((load, loadIndex) => ({
35
+ label: LOAD_AVERAGE_TIME_INTERVALS[loadIndex],
36
+ value: <ProgressViewer value={load} percents={true} colorizeProgress={true} />,
37
+ }));
38
+
39
+ return (
40
+ <div className={`${b()} ${className}`}>
41
+ {node ? (
42
+ <div className={b('common-info')}>
43
+ <div>
44
+ <div className={b('section-title')}>Pools</div>
45
+ <div className={b('section', {pools: true})}>
46
+ {node?.PoolStats?.map((pool, poolIndex) => (
47
+ <PoolUsage key={poolIndex} data={pool} />
48
+ ))}
49
+ </div>
50
+ </div>
51
+
52
+ {endpointsInfo && endpointsInfo.length && (
53
+ <InfoViewer
54
+ title="Endpoints"
55
+ className={b('section')}
56
+ info={endpointsInfo}
57
+ />
58
+ )}
59
+
60
+ <InfoViewer title="Common info" className={b('section')} info={commonInfo} />
61
+
62
+ <InfoViewer
63
+ title="Load average"
64
+ className={b('section', {average: true})}
65
+ info={averageInfo}
66
+ />
67
+ </div>
68
+ ) : (
69
+ <div className="error">no data</div>
70
+ )}
71
+ </div>
72
+ );
73
+ };
@@ -1,14 +1,15 @@
1
1
  import type {TFollowerGroup, TPartitionConfig, TTableStats} from '../../../types/api/schema';
2
2
  import type {TMetrics} from '../../../types/api/tenant';
3
- import {formatCPU, formatBytes, formatNumber, formatBps, formatDateTime} from '../../../utils';
3
+ import {formatCPU, formatNumber, formatBps, formatDateTime} from '../../../utils';
4
+ import {toFormattedSize} from '../../FormattedBytes/utils';
4
5
 
5
6
  import {createInfoFormatter} from '../utils';
6
7
 
7
8
  export const formatTabletMetricsItem = createInfoFormatter<TMetrics>({
8
9
  values: {
9
10
  CPU: formatCPU,
10
- Memory: formatBytes,
11
- Storage: formatBytes,
11
+ Memory: toFormattedSize,
12
+ Storage: toFormattedSize,
12
13
  Network: formatBps,
13
14
  ReadThroughput: formatBps,
14
15
  WriteThroughput: formatBps,
@@ -37,8 +38,8 @@ export const formatPartitionConfigItem = createInfoFormatter<TPartitionConfig>({
37
38
 
38
39
  export const formatTableStatsItem = createInfoFormatter<TTableStats>({
39
40
  values: {
40
- DataSize: formatBytes,
41
- IndexSize: formatBytes,
41
+ DataSize: toFormattedSize,
42
+ IndexSize: toFormattedSize,
42
43
  LastAccessTime: formatDateTime,
43
44
  LastUpdateTime: formatDateTime,
44
45
  },
@@ -1,10 +1,10 @@
1
1
  import {RadioButton} from '@gravity-ui/uikit';
2
2
 
3
- import type {ValueOf} from '../../types/common';
3
+ import type {ProblemFilterValue} from '../../store/reducers/settings/types';
4
4
  import {ProblemFilterValues} from '../../store/reducers/settings/settings';
5
5
 
6
6
  interface ProblemFilterProps {
7
- value: ValueOf<typeof ProblemFilterValues>;
7
+ value: ProblemFilterValue;
8
8
  onChange: (value: string) => void;
9
9
  className?: string;
10
10
  }
@@ -2,7 +2,7 @@ import {useState} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {Popover} from '@gravity-ui/uikit';
4
4
 
5
- import {formatBytesCustom, IBytesSizes, IProcessSpeedStats} from '../../utils/bytesParsers';
5
+ import {formatBytes, BytesSizes, ProcessSpeedStats} from '../../utils/bytesParsers';
6
6
 
7
7
  import './SpeedMultiMeter.scss';
8
8
 
@@ -11,8 +11,8 @@ import i18n from './i18n';
11
11
  const b = cn('speed-multimeter');
12
12
 
13
13
  interface SpeedMultiMeterProps {
14
- data?: IProcessSpeedStats;
15
- speedSize?: IBytesSizes;
14
+ data?: ProcessSpeedStats;
15
+ speedSize?: BytesSizes;
16
16
  withValue?: boolean;
17
17
  withPopover?: boolean;
18
18
  }
@@ -27,7 +27,7 @@ export const SpeedMultiMeter = ({
27
27
  const rawValues = [perMinute, perHour, perDay];
28
28
 
29
29
  const formatValue = (value: number) =>
30
- formatBytesCustom({value: value, size: speedSize, isSpeed: true});
30
+ formatBytes({value: value, size: speedSize, withSpeedLabel: true});
31
31
 
32
32
  const formattedValues = [
33
33
  {value: formatValue(perMinute), label: i18n('perMinute')},
@@ -1,11 +1,15 @@
1
- import React from 'react';
2
1
  import cn from 'bem-cn-lite';
3
2
 
4
3
  import './TruncatedQuery.scss';
5
4
 
6
5
  const b = cn('kv-truncated-query');
7
6
 
8
- function TruncatedQuery({value, maxQueryHeight}) {
7
+ interface TruncatedQueryProps {
8
+ value: string | undefined;
9
+ maxQueryHeight?: number;
10
+ }
11
+
12
+ export const TruncatedQuery = ({value = '', maxQueryHeight = 6}: TruncatedQueryProps) => {
9
13
  const lines = value.split('\n');
10
14
  const truncated = lines.length > maxQueryHeight;
11
15
 
@@ -14,13 +18,11 @@ function TruncatedQuery({value, maxQueryHeight}) {
14
18
  const message =
15
19
  '\n...\nThe request was truncated. Click on the line to show the full query on the query tab';
16
20
  return (
17
- <React.Fragment>
21
+ <>
18
22
  <span className={b()}>{content}</span>
19
23
  <span className={b('message', {color: 'secondary'})}>{message}</span>
20
- </React.Fragment>
24
+ </>
21
25
  );
22
26
  }
23
- return value;
24
- }
25
-
26
- export default TruncatedQuery;
27
+ return <>{value}</>;
28
+ };
@@ -139,23 +139,23 @@ export const useGetLeftNavigationItems = () => {
139
139
 
140
140
  const items: MenuItem[] = [
141
141
  {
142
- id: TENANT_PAGES_IDS.diagnostics,
143
- title: 'Diagnostics',
142
+ id: TENANT_PAGES_IDS.query,
143
+ title: 'Query',
144
144
  icon: squareChartBarIcon,
145
145
  iconSize: 20,
146
146
  location: getTenantPath({
147
147
  ...queryParams,
148
- [TENANT_PAGE]: TENANT_PAGES_IDS.diagnostics,
148
+ [TENANT_PAGE]: TENANT_PAGES_IDS.query,
149
149
  }),
150
150
  },
151
151
  {
152
- id: TENANT_PAGES_IDS.query,
153
- title: 'Query',
152
+ id: TENANT_PAGES_IDS.diagnostics,
153
+ title: 'Diagnostics',
154
154
  icon: pulseIcon,
155
155
  iconSize: 20,
156
156
  location: getTenantPath({
157
157
  ...queryParams,
158
- [TENANT_PAGE]: TENANT_PAGES_IDS.query,
158
+ [TENANT_PAGE]: TENANT_PAGES_IDS.diagnostics,
159
159
  }),
160
160
  },
161
161
  ];
@@ -6,7 +6,11 @@ import qs from 'qs';
6
6
 
7
7
  import {Tabs} from '@gravity-ui/uikit';
8
8
 
9
- import type {AdditionalClusterProps, AdditionalVersionsProps} from '../../types/additionalProps';
9
+ import type {
10
+ AdditionalClusterProps,
11
+ AdditionalTenantsProps,
12
+ AdditionalVersionsProps,
13
+ } from '../../types/additionalProps';
10
14
  import type {AdditionalNodesInfo} from '../../utils/nodes';
11
15
  import routes from '../../routes';
12
16
 
@@ -17,9 +21,9 @@ import {parseNodesToVersionsValues, parseVersionsToVersionToColorMap} from '../.
17
21
  import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
18
22
 
19
23
  import {InternalLink} from '../../components/InternalLink';
20
- import Tenants from '../Tenants/Tenants';
24
+ import {Tenants} from '../Tenants/Tenants';
21
25
  import {Nodes} from '../Nodes/Nodes';
22
- import Storage from '../Storage/Storage';
26
+ import {Storage} from '../Storage/Storage';
23
27
  import {Versions} from '../Versions/Versions';
24
28
 
25
29
  import {ClusterInfo} from './ClusterInfo/ClusterInfo';
@@ -30,7 +34,7 @@ import './Cluster.scss';
30
34
  const b = cn('cluster');
31
35
 
32
36
  interface ClusterProps {
33
- additionalTenantsInfo?: unknown;
37
+ additionalTenantsProps?: AdditionalTenantsProps;
34
38
  additionalNodesInfo?: AdditionalNodesInfo;
35
39
  additionalClusterProps?: AdditionalClusterProps;
36
40
  additionalVersionsProps?: AdditionalVersionsProps;
@@ -38,7 +42,7 @@ interface ClusterProps {
38
42
 
39
43
  function Cluster({
40
44
  additionalClusterProps,
41
- additionalTenantsInfo,
45
+ additionalTenantsProps,
42
46
  additionalNodesInfo,
43
47
  additionalVersionsProps,
44
48
  }: ClusterProps) {
@@ -97,7 +101,7 @@ function Cluster({
97
101
  const renderTab = () => {
98
102
  switch (activeTab) {
99
103
  case clusterTabsIds.tenants: {
100
- return <Tenants additionalTenantsInfo={additionalTenantsInfo} />;
104
+ return <Tenants additionalTenantsProps={additionalTenantsProps} />;
101
105
  }
102
106
  case clusterTabsIds.nodes: {
103
107
  return <Nodes additionalNodesInfo={additionalNodesInfo} />;
@@ -9,11 +9,11 @@ import {Link} from 'react-router-dom';
9
9
 
10
10
  import {TABLETS, STORAGE, NODE_PAGES, OVERVIEW, STRUCTURE} from './NodePages';
11
11
  import {Tablets} from '../Tablets';
12
- import Storage from '../Storage/Storage';
13
- import NodeOverview from './NodeOverview/NodeOverview';
12
+ import {Storage} from '../Storage/Storage';
14
13
  import NodeStructure from './NodeStructure/NodeStructure';
15
14
  import {Loader} from '../../components/Loader';
16
15
  import {BasicNodeViewer} from '../../components/BasicNodeViewer';
16
+ import {FullNodeViewer} from '../../components/FullNodeViewer/FullNodeViewer';
17
17
 
18
18
  import {getNodeInfo, resetNode} from '../../store/reducers/node/node';
19
19
  import routes, {createHref, parseQuery} from '../../routes';
@@ -128,7 +128,7 @@ function Node(props: NodeProps) {
128
128
  }
129
129
 
130
130
  case OVERVIEW: {
131
- return <NodeOverview node={node} className={b('overview-wrapper')} />;
131
+ return <FullNodeViewer node={node} className={b('overview-wrapper')} />;
132
132
  }
133
133
 
134
134
  case STRUCTURE: {
@@ -5,7 +5,7 @@ import {useDispatch} from 'react-redux';
5
5
  import DataTable from '@gravity-ui/react-data-table';
6
6
 
7
7
  import type {EPathType} from '../../types/api/schema';
8
- import type {ValueOf} from '../../types/common';
8
+ import type {ProblemFilterValue} from '../../store/reducers/settings/types';
9
9
 
10
10
  import {AccessDenied} from '../../components/Errors/403';
11
11
  import {Illustration} from '../../components/Illustration';
@@ -84,7 +84,7 @@ export const Nodes = ({path, type, className, additionalNodesInfo = {}}: NodesPr
84
84
  };
85
85
 
86
86
  const handleProblemFilterChange = (value: string) => {
87
- dispatch(changeFilter(value as ValueOf<typeof ProblemFilterValues>));
87
+ dispatch(changeFilter(value as ProblemFilterValue));
88
88
  };
89
89
 
90
90
  const handleUptimeFilterChange = (value: string) => {
@@ -5,9 +5,8 @@ import {InternalLink} from '../../../components/InternalLink';
5
5
  import {Stack} from '../../../components/Stack/Stack';
6
6
 
7
7
  import routes, {createHref} from '../../../routes';
8
- import {getVDisksForPDisk} from '../../../store/reducers/storage/storage';
8
+ import {selectVDisksForPDisk} from '../../../store/reducers/storage/selectors';
9
9
  import {TPDiskStateInfo, TPDiskState} from '../../../types/api/pdisk';
10
- import {TVDiskStateInfo} from '../../../types/api/vdisk';
11
10
  import {stringifyVdiskId} from '../../../utils';
12
11
  import {useTypedSelector} from '../../../utils/hooks';
13
12
  import {getPDiskType} from '../../../utils/pdisk';
@@ -58,11 +57,7 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => {
58
57
  // NodeId in data is required for the popup
59
58
  const data = useMemo(() => ({...rawData, NodeId: nodeId}), [rawData, nodeId]);
60
59
 
61
- const vdisks: TVDiskStateInfo[] | undefined = useTypedSelector((state) =>
62
- // @ts-expect-error selector is correct, but js infers broken type
63
- // unignore after rewriting reducer in ts
64
- getVDisksForPDisk(state, nodeId, data.PDiskId),
65
- );
60
+ const vdisks = useTypedSelector((state) => selectVDisksForPDisk(state, nodeId, data.PDiskId));
66
61
 
67
62
  const [severity, setSeverity] = useState(getStateSeverity(data.State));
68
63
  const [isPopupVisible, setIsPopupVisible] = useState(false);