ydb-embedded-ui 4.19.2 → 4.20.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 (76) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/components/LinkToSchemaObject/LinkToSchemaObject.tsx +20 -0
  3. package/dist/components/ProgressViewer/ProgressViewer.tsx +2 -1
  4. package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +3 -2
  5. package/dist/components/UsageLabel/UsageLabel.scss +6 -0
  6. package/dist/components/UsageLabel/UsageLabel.tsx +22 -0
  7. package/dist/containers/AsideNavigation/AsideNavigation.tsx +13 -8
  8. package/dist/containers/AsideNavigation/i18n/en.json +13 -0
  9. package/dist/containers/AsideNavigation/i18n/index.ts +11 -0
  10. package/dist/containers/AsideNavigation/i18n/ru.json +13 -0
  11. package/dist/containers/Node/NodeStructure/Pdisk.tsx +74 -68
  12. package/dist/containers/Node/NodeStructure/Vdisk.tsx +9 -33
  13. package/dist/containers/Nodes/Nodes.tsx +10 -2
  14. package/dist/containers/Nodes/getNodesColumns.tsx +206 -122
  15. package/dist/containers/Storage/Storage.tsx +9 -2
  16. package/dist/containers/Storage/utils/index.ts +1 -22
  17. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +2 -3
  18. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +0 -1
  19. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +4 -2
  20. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +1 -0
  21. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx +8 -1
  22. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx +11 -1
  23. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/IssuesViewer/IssueTree.tsx +0 -1
  24. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +3 -0
  25. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx +21 -0
  26. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx +53 -0
  27. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx +53 -0
  28. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx +85 -0
  29. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx +53 -0
  30. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TenantMemory.tsx +9 -0
  31. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TopNodesByMemory.tsx +55 -0
  32. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +40 -0
  33. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +35 -19
  34. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverviewTableLayout.tsx +53 -0
  35. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +15 -7
  36. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +3 -3
  37. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +3 -3
  38. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.scss +0 -2
  39. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +13 -61
  40. package/dist/containers/Tenant/Diagnostics/TopQueries/getTopQueriesColumns.tsx +82 -0
  41. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +2 -2
  42. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +18 -97
  43. package/dist/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx +138 -0
  44. package/dist/containers/Tenant/Info/ExternalTable/ExternalTable.tsx +2 -4
  45. package/dist/containers/Tenant/Query/ExecuteResult/{ExecuteResult.js → ExecuteResult.tsx} +51 -31
  46. package/dist/containers/Tenant/Query/Issues/Issues.tsx +4 -6
  47. package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.tsx +1 -1
  48. package/dist/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx +1 -1
  49. package/dist/routes.ts +6 -0
  50. package/dist/store/reducers/{executeTopQueries.ts → executeTopQueries/executeTopQueries.ts} +23 -75
  51. package/dist/{types/store/executeTopQueries.ts → store/reducers/executeTopQueries/types.ts} +3 -7
  52. package/dist/store/reducers/executeTopQueries/utils.ts +36 -0
  53. package/dist/store/reducers/index.ts +12 -2
  54. package/dist/store/reducers/nodes/types.ts +1 -0
  55. package/dist/store/reducers/nodes/utils.ts +16 -6
  56. package/dist/store/reducers/{shardsWorkload.ts → shardsWorkload/shardsWorkload.ts} +5 -11
  57. package/dist/{types/store/shardsWorkload.ts → store/reducers/shardsWorkload/types.ts} +3 -7
  58. package/dist/store/reducers/storage/storage.ts +1 -1
  59. package/dist/store/reducers/tenantOverview/topNodesByCpu/topNodesByCpu.ts +87 -0
  60. package/dist/store/reducers/tenantOverview/topNodesByCpu/types.ts +29 -0
  61. package/dist/store/reducers/tenantOverview/topNodesByLoad/topNodesByLoad.ts +87 -0
  62. package/dist/store/reducers/tenantOverview/topNodesByLoad/types.ts +29 -0
  63. package/dist/store/reducers/tenantOverview/topNodesByMemory/topNodesByMemory.ts +87 -0
  64. package/dist/store/reducers/tenantOverview/topNodesByMemory/types.ts +29 -0
  65. package/dist/store/reducers/tenantOverview/topQueries/tenantOverviewTopQueries.ts +93 -0
  66. package/dist/store/reducers/tenantOverview/topQueries/types.ts +14 -0
  67. package/dist/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts +103 -0
  68. package/dist/store/reducers/tenantOverview/topShards/types.ts +14 -0
  69. package/dist/store/reducers/tenantOverview/topShards/utils.ts +3 -0
  70. package/dist/styles/mixins.scss +4 -0
  71. package/dist/types/additionalProps.ts +3 -1
  72. package/dist/types/api/compute.ts +1 -1
  73. package/dist/types/react-json-inspector.d.ts +21 -0
  74. package/dist/utils/diagnostics.ts +11 -0
  75. package/dist/utils/generateEvaluator.ts +21 -0
  76. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.20.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.19.3...v4.20.0) (2023-10-19)
4
+
5
+
6
+ ### Features
7
+
8
+ * add top nodes by memory table ([#562](https://github.com/ydb-platform/ydb-embedded-ui/issues/562)) ([0d4ccf2](https://github.com/ydb-platform/ydb-embedded-ui/commit/0d4ccf2a85251fadad66182ab7d6ccd54a58e6cf))
9
+ * add top tables links ([#564](https://github.com/ydb-platform/ydb-embedded-ui/issues/564)) ([e9b918f](https://github.com/ydb-platform/ydb-embedded-ui/commit/e9b918f0abace890cfafd9d7b219be5b69cac820))
10
+ * **Storage:** hide nodes table for node page ([#557](https://github.com/ydb-platform/ydb-embedded-ui/issues/557)) ([9a25a00](https://github.com/ydb-platform/ydb-embedded-ui/commit/9a25a002b28824f7e616ac8143dbde12de0b0fb7))
11
+ * **TenantOverview:** add cpu tab to tenant diagnostics ([#550](https://github.com/ydb-platform/ydb-embedded-ui/issues/550)) ([3048f84](https://github.com/ydb-platform/ydb-embedded-ui/commit/3048f8478d97249da4f7b66c26ed55f6f21e0f81))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * add loader for healthcheck ([#563](https://github.com/ydb-platform/ydb-embedded-ui/issues/563)) ([6caea3d](https://github.com/ydb-platform/ydb-embedded-ui/commit/6caea3dec8f901090b0f8f7c1796880d7dc90a99))
17
+ * **LinkToSchemaObject:** fix schema link ([#566](https://github.com/ydb-platform/ydb-embedded-ui/issues/566)) ([6ca8a70](https://github.com/ydb-platform/ydb-embedded-ui/commit/6ca8a705b6ddacb1f845aabb7761fd22c0c3b4e0))
18
+
19
+ ## [4.19.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.19.2...v4.19.3) (2023-10-13)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * fix ProgressViewer background ([#556](https://github.com/ydb-platform/ydb-embedded-ui/issues/556)) ([6234462](https://github.com/ydb-platform/ydb-embedded-ui/commit/62344629713059fdfb191d3b8a57742f864dad66))
25
+ * **Storage:** display all groups by default ([#554](https://github.com/ydb-platform/ydb-embedded-ui/issues/554)) ([1da83f1](https://github.com/ydb-platform/ydb-embedded-ui/commit/1da83f19661ed8e49dd7c8a0930ce89a7c8c0185))
26
+
3
27
  ## [4.19.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.19.1...v4.19.2) (2023-10-12)
4
28
 
5
29
 
@@ -0,0 +1,20 @@
1
+ import type {Location} from 'history';
2
+
3
+ import {Link, type LinkProps} from '@gravity-ui/uikit';
4
+
5
+ import {createExternalUILink, parseQuery} from '../../routes';
6
+
7
+ interface LinkToSchemaObjectProps extends LinkProps {
8
+ path: string;
9
+ location: Location;
10
+ }
11
+
12
+ export function LinkToSchemaObject({path, location, ...props}: LinkToSchemaObjectProps) {
13
+ const queryParams = parseQuery(location);
14
+ const pathToSchemaObject = createExternalUILink({
15
+ ...queryParams,
16
+ schema: path,
17
+ });
18
+
19
+ return <Link view="normal" href={pathToSchemaObject} {...props} />;
20
+ }
@@ -66,7 +66,8 @@ export function ProgressViewer({
66
66
  warningThreshold = 60,
67
67
  dangerThreshold = 80,
68
68
  }: ProgressViewerProps) {
69
- let fillWidth = Math.round((parseFloat(String(value)) / parseFloat(String(capacity))) * 100);
69
+ let fillWidth =
70
+ Math.round((parseFloat(String(value)) / parseFloat(String(capacity))) * 100) || 0;
70
71
  fillWidth = fillWidth > 100 ? 100 : fillWidth;
71
72
  let valueText: number | string | undefined = value,
72
73
  capacityText: number | string | undefined = capacity,
@@ -14,14 +14,15 @@ const b = cn('kv-query-execution-status');
14
14
 
15
15
  interface QueryExecutionStatusProps {
16
16
  className?: string;
17
- error?: AxiosError | Record<string, any>;
17
+ // TODO: Remove Record<string, any> when ECONNABORTED error case is fully typed
18
+ error?: AxiosError | Record<string, any> | string;
18
19
  }
19
20
 
20
21
  export const QueryExecutionStatus = ({className, error}: QueryExecutionStatusProps) => {
21
22
  let icon: ReactNode;
22
23
  let label: string;
23
24
 
24
- if (error?.code === 'ECONNABORTED') {
25
+ if (typeof error === 'object' && error?.code === 'ECONNABORTED') {
25
26
  icon = <UiKitIcon data={questionIcon} size={16} />;
26
27
  label = 'Connection aborted';
27
28
  } else {
@@ -0,0 +1,6 @@
1
+ .ydb-usage-label {
2
+ &_overload {
3
+ color: var(--yc-color-text-light-primary);
4
+ background-color: var(--yc-color-base-danger-heavy);
5
+ }
6
+ }
@@ -0,0 +1,22 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import {Label, type LabelProps} from '@gravity-ui/uikit';
4
+
5
+ const b = cn('ydb-usage-label');
6
+
7
+ interface UsageLabelProps extends LabelProps {
8
+ value: number | string;
9
+ overloadThreshold?: number;
10
+ }
11
+
12
+ export function UsageLabel({value, overloadThreshold = 90, theme, ...props}: UsageLabelProps) {
13
+ return (
14
+ <Label
15
+ theme={theme}
16
+ className={b({overload: Number(value) >= overloadThreshold})}
17
+ {...props}
18
+ >
19
+ {value || 0}%
20
+ </Label>
21
+ );
22
+ }
@@ -28,6 +28,7 @@ import {ASIDE_HEADER_COMPACT_KEY, TENANT_INITIAL_PAGE_KEY} from '../../utils/con
28
28
  import {getTenantPath} from '../Tenant/TenantPages';
29
29
  import {UserSettings} from '../UserSettings/UserSettings';
30
30
 
31
+ import i18n from './i18n';
31
32
  import './AsideNavigation.scss';
32
33
 
33
34
  const b = cn('kv-navigation');
@@ -58,15 +59,19 @@ function YbdInternalUser({ydbUser, logout}: YbdInternalUserProps) {
58
59
  return (
59
60
  <div className={b('internal-user')}>
60
61
  <div className={b('user-info-wrapper')}>
61
- <div className={b('ydb-internal-user-title')}>YDB user</div>
62
+ <div className={b('ydb-internal-user-title')}>{i18n('account.user')}</div>
62
63
  {ydbUser && <div className={b('username')}>{ydbUser}</div>}
63
64
  </div>
64
65
  {ydbUser ? (
65
- <Button view="flat-secondary" onClick={logout} title="logout">
66
+ <Button view="flat-secondary" title={i18n('account.logout')} onClick={logout}>
66
67
  <Icon data={signOutIcon} size={16} />
67
68
  </Button>
68
69
  ) : (
69
- <Button view="flat-secondary" title="login" onClick={handleLoginClick}>
70
+ <Button
71
+ view="flat-secondary"
72
+ title={i18n('account.login')}
73
+ onClick={handleLoginClick}
74
+ >
70
75
  <Icon data={signInIcon} size={16} />
71
76
  </Button>
72
77
  )}
@@ -91,7 +96,7 @@ function YdbUserDropdown({isCompact, popupAnchor, ydbUser}: YdbUserDropdownProps
91
96
  compact={isCompact}
92
97
  item={{
93
98
  id: 'user-popup',
94
- title: ydbUser?.login ?? 'Account',
99
+ title: ydbUser?.login ?? i18n('navigation-item.account'),
95
100
  current: isUserDropdownVisible,
96
101
  icon: iconData,
97
102
  iconSize: 22,
@@ -142,7 +147,7 @@ export const useGetLeftNavigationItems = () => {
142
147
  const items: MenuItem[] = [
143
148
  {
144
149
  id: TENANT_PAGES_IDS.query,
145
- title: 'Query',
150
+ title: i18n('pages.query'),
146
151
  icon: terminalIcon,
147
152
  iconSize: 20,
148
153
  location: getTenantPath({
@@ -152,7 +157,7 @@ export const useGetLeftNavigationItems = () => {
152
157
  },
153
158
  {
154
159
  id: TENANT_PAGES_IDS.diagnostics,
155
- title: 'Diagnostics',
160
+ title: i18n('pages.diagnostics'),
156
161
  icon: pulseIcon,
157
162
  iconSize: 20,
158
163
  location: getTenantPath({
@@ -212,7 +217,7 @@ function AsideNavigation(props: AsideNavigationProps) {
212
217
  compact={compact}
213
218
  item={{
214
219
  id: 'documentation',
215
- title: 'Documentation',
220
+ title: i18n('navigation-item.documentation'),
216
221
  icon: supportIcon,
217
222
  iconSize: 24,
218
223
  onItemClick: () => {
@@ -224,7 +229,7 @@ function AsideNavigation(props: AsideNavigationProps) {
224
229
  <FooterItem
225
230
  item={{
226
231
  id: 'user-settings',
227
- title: 'Settings',
232
+ title: i18n('navigation-item.settings'),
228
233
  icon: settingsIcon,
229
234
  iconSize: 24,
230
235
  current: visiblePanel === Panel.UserSettings,
@@ -0,0 +1,13 @@
1
+ {
2
+ "pages.query": "Query",
3
+ "pages.diagnostics": "Diagnostics",
4
+
5
+ "navigation-item.documentation": "Documentation",
6
+ "navigation-item.settings": "Settings",
7
+ "navigation-item.account": "Account",
8
+
9
+ "account.user": "YDB User",
10
+
11
+ "account.login": "Login",
12
+ "account.logout": "Logout"
13
+ }
@@ -0,0 +1,11 @@
1
+ import {i18n, Lang} from '../../../utils/i18n';
2
+
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+
6
+ const COMPONENT = 'ydb-aside-navigation';
7
+
8
+ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
+ i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
+
11
+ export default i18n.keyset(COMPONENT);
@@ -0,0 +1,13 @@
1
+ {
2
+ "pages.query": "Редактор запросов",
3
+ "pages.diagnostics": "Диагностика",
4
+
5
+ "navigation-item.documentation": "Документация",
6
+ "navigation-item.settings": "Настройки",
7
+ "navigation-item.account": "Аккаунт",
8
+
9
+ "account.user": "Пользователь YDB",
10
+
11
+ "account.login": "Войти",
12
+ "account.logout": "Выйти"
13
+ }
@@ -1,29 +1,34 @@
1
1
  import {useState} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
- import _ from 'lodash';
3
+ import {isEmpty} from 'lodash/fp';
4
4
 
5
5
  import {ArrowToggle, Button, Popover} from '@gravity-ui/uikit';
6
6
 
7
- import DataTable, {Column, Settings} from '@gravity-ui/react-data-table';
8
-
9
- import EntityStatus from '../../../components/EntityStatus/EntityStatus';
10
- import InfoViewer from '../../../components/InfoViewer/InfoViewer';
11
- import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
12
- import {Icon} from '../../../components/Icon';
13
- import {Vdisk} from './Vdisk';
7
+ import DataTable, {type Column} from '@gravity-ui/react-data-table';
14
8
 
9
+ import type {ValueOf} from '../../../types/common';
10
+ import type {
11
+ PreparedStructurePDisk,
12
+ PreparedStructureVDisk,
13
+ } from '../../../store/reducers/node/types';
14
+ import {EVDiskState} from '../../../types/api/vdisk';
15
15
  import {bytesToGB, pad9} from '../../../utils/utils';
16
16
  import {formatStorageValuesToGb} from '../../../utils/dataFormatters/dataFormatters';
17
17
  import {getPDiskType} from '../../../utils/pdisk';
18
-
19
18
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
19
+ import EntityStatus from '../../../components/EntityStatus/EntityStatus';
20
+ import InfoViewer, {type InfoViewerItem} from '../../../components/InfoViewer/InfoViewer';
21
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
22
+ import {Icon} from '../../../components/Icon';
23
+
24
+ import {Vdisk} from './Vdisk';
20
25
  import {valueIsDefined} from './NodeStructure';
21
26
  import {PDiskTitleBadge} from './PDiskTitleBadge';
22
27
 
23
28
  const b = cn('kv-node-structure');
24
29
 
25
30
  interface PDiskProps {
26
- data: Record<string, any>;
31
+ data: PreparedStructurePDisk;
27
32
  unfolded?: boolean;
28
33
  id: string;
29
34
  selectedVdiskId?: string;
@@ -37,8 +42,7 @@ enum VDiskTableColumnsIds {
37
42
  Info = 'Info',
38
43
  }
39
44
 
40
- type VDiskTableColumnsIdsKeys = keyof typeof VDiskTableColumnsIds;
41
- type VDiskTableColumnsIdsValues = typeof VDiskTableColumnsIds[VDiskTableColumnsIdsKeys];
45
+ type VDiskTableColumnsIdsValues = ValueOf<typeof VDiskTableColumnsIds>;
42
46
 
43
47
  const vDiskTableColumnsNames: Record<VDiskTableColumnsIdsValues, string> = {
44
48
  VDiskSlotId: 'Slot id',
@@ -47,39 +51,35 @@ const vDiskTableColumnsNames: Record<VDiskTableColumnsIdsValues, string> = {
47
51
  Info: '',
48
52
  };
49
53
 
50
- interface RowType {
51
- id: string;
52
- [VDiskTableColumnsIds.slotId]: number;
53
- [VDiskTableColumnsIds.VDiskState]: string;
54
- AllocatedSize: string;
55
- AvailableSize: string;
56
- }
57
-
58
54
  function getColumns({
59
55
  pDiskId,
60
56
  selectedVdiskId,
61
57
  nodeHref,
62
58
  }: {
63
- pDiskId: number;
59
+ pDiskId: number | undefined;
64
60
  selectedVdiskId?: string;
65
61
  nodeHref?: string | null;
66
62
  }) {
67
- const columns: Column<RowType>[] = [
63
+ const columns: Column<PreparedStructureVDisk>[] = [
68
64
  {
69
- name: VDiskTableColumnsIds.slotId as string,
65
+ name: VDiskTableColumnsIds.slotId,
70
66
  header: vDiskTableColumnsNames[VDiskTableColumnsIds.slotId],
71
67
  width: 100,
72
- render: ({value, row}) => {
68
+ render: ({row}) => {
73
69
  let vdiskInternalViewerLink = '';
74
70
 
75
- if (nodeHref && value !== undefined) {
71
+ if (nodeHref && pDiskId !== undefined && row.VDiskSlotId !== undefined) {
76
72
  vdiskInternalViewerLink +=
77
- nodeHref + 'actors/vdisks/vdisk' + pad9(pDiskId) + '_' + pad9(value);
73
+ nodeHref +
74
+ 'actors/vdisks/vdisk' +
75
+ pad9(pDiskId) +
76
+ '_' +
77
+ pad9(row.VDiskSlotId);
78
78
  }
79
79
 
80
80
  return (
81
81
  <div className={b('vdisk-id', {selected: row.id === selectedVdiskId})}>
82
- <span>{value as number}</span>
82
+ <span>{row.VDiskSlotId}</span>
83
83
  {vdiskInternalViewerLink && (
84
84
  <Button
85
85
  size="s"
@@ -96,17 +96,19 @@ function getColumns({
96
96
  align: DataTable.LEFT,
97
97
  },
98
98
  {
99
- name: VDiskTableColumnsIds.VDiskState as string,
99
+ name: VDiskTableColumnsIds.VDiskState,
100
100
  header: vDiskTableColumnsNames[VDiskTableColumnsIds.VDiskState],
101
101
  width: 70,
102
- render: ({value}) => {
103
- return <EntityStatus status={value === 'OK' ? 'green' : 'red'} />;
102
+ render: ({row}) => {
103
+ return (
104
+ <EntityStatus status={row.VDiskState === EVDiskState.OK ? 'green' : 'red'} />
105
+ );
104
106
  },
105
- sortAccessor: (row) => (row[VDiskTableColumnsIds.VDiskState] === 'OK' ? 1 : 0),
107
+ sortAccessor: (row) => (row.VDiskState === EVDiskState.OK ? 1 : 0),
106
108
  align: DataTable.CENTER,
107
109
  },
108
110
  {
109
- name: VDiskTableColumnsIds.Size as string,
111
+ name: VDiskTableColumnsIds.Size,
110
112
  header: vDiskTableColumnsNames[VDiskTableColumnsIds.Size],
111
113
  width: 100,
112
114
  render: ({row}) => {
@@ -123,7 +125,7 @@ function getColumns({
123
125
  align: DataTable.CENTER,
124
126
  },
125
127
  {
126
- name: VDiskTableColumnsIds.Info as string,
128
+ name: VDiskTableColumnsIds.Info,
127
129
  header: vDiskTableColumnsNames[VDiskTableColumnsIds.Info],
128
130
  width: 70,
129
131
  render: ({row}) => {
@@ -150,10 +152,31 @@ function getColumns({
150
152
  return columns;
151
153
  }
152
154
 
153
- export function PDisk(props: PDiskProps) {
154
- const [unfolded, setUnfolded] = useState(props.unfolded ?? false);
155
+ export function PDisk({
156
+ id,
157
+ data,
158
+ selectedVdiskId,
159
+ nodeHref,
160
+ unfolded: unfoldedFromProps,
161
+ }: PDiskProps) {
162
+ const [unfolded, setUnfolded] = useState(unfoldedFromProps ?? false);
163
+
164
+ const {
165
+ TotalSize = 0,
166
+ AvailableSize = 0,
167
+ Device,
168
+ Guid,
169
+ PDiskId,
170
+ Path,
171
+ Realtime,
172
+ State,
173
+ Category,
174
+ SerialNumber,
175
+ vDisks,
176
+ } = data;
155
177
 
156
- const data = props.data ?? {};
178
+ const total = Number(TotalSize);
179
+ const available = Number(AvailableSize);
157
180
 
158
181
  const onOpenPDiskDetails = () => {
159
182
  setUnfolded(true);
@@ -163,15 +186,12 @@ export function PDisk(props: PDiskProps) {
163
186
  };
164
187
 
165
188
  const renderVDisks = () => {
166
- const {selectedVdiskId, data, nodeHref} = props;
167
- const {vDisks} = data;
168
-
169
189
  return (
170
190
  <DataTable
171
191
  theme="yandex-cloud"
172
192
  data={vDisks}
173
- columns={getColumns({nodeHref, pDiskId: data.PDiskId, selectedVdiskId})}
174
- settings={{...DEFAULT_TABLE_SETTINGS, dynamicRender: false} as Settings}
193
+ columns={getColumns({nodeHref, pDiskId: PDiskId, selectedVdiskId})}
194
+ settings={{...DEFAULT_TABLE_SETTINGS, dynamicRender: false}}
175
195
  rowClassName={(row) => {
176
196
  return row.id === selectedVdiskId ? b('selected-vdisk') : '';
177
197
  }}
@@ -180,30 +200,16 @@ export function PDisk(props: PDiskProps) {
180
200
  };
181
201
 
182
202
  const renderPDiskDetails = () => {
183
- if (_.isEmpty(data)) {
203
+ if (isEmpty(data)) {
184
204
  return <div>No information about PDisk</div>;
185
205
  }
186
- const {nodeHref} = props;
187
- const {
188
- TotalSize,
189
- AvailableSize,
190
- Device,
191
- Guid,
192
- PDiskId,
193
- Path,
194
- Realtime,
195
- State,
196
- Category,
197
- SerialNumber,
198
- } = data;
199
-
200
206
  let pDiskInternalViewerLink = '';
201
207
 
202
208
  if (nodeHref) {
203
209
  pDiskInternalViewerLink += nodeHref + 'actors/pdisks/pdisk' + pad9(PDiskId);
204
210
  }
205
211
 
206
- const pdiskInfo: any = [
212
+ const pdiskInfo: InfoViewerItem[] = [
207
213
  {
208
214
  label: 'PDisk Id',
209
215
  value: (
@@ -236,19 +242,19 @@ export function PDisk(props: PDiskProps) {
236
242
  }
237
243
  pdiskInfo.push({
238
244
  label: 'Allocated Size',
239
- value: bytesToGB(TotalSize - AvailableSize),
245
+ value: bytesToGB(total - available),
240
246
  });
241
247
  pdiskInfo.push({
242
248
  label: 'Available Size',
243
- value: bytesToGB(AvailableSize),
249
+ value: bytesToGB(available),
244
250
  });
245
- if (Number(TotalSize) >= 0 && Number(AvailableSize) >= 0) {
251
+ if (total >= 0 && available >= 0) {
246
252
  pdiskInfo.push({
247
253
  label: 'Size',
248
254
  value: (
249
255
  <ProgressViewer
250
- value={TotalSize - AvailableSize}
251
- capacity={TotalSize}
256
+ value={total - available}
257
+ capacity={total}
252
258
  formatValues={formatStorageValuesToGb}
253
259
  colorizeProgress={true}
254
260
  className={b('size')}
@@ -286,24 +292,24 @@ export function PDisk(props: PDiskProps) {
286
292
  };
287
293
 
288
294
  return (
289
- <div className={b('pdisk')} id={props.id}>
295
+ <div className={b('pdisk')} id={id}>
290
296
  <div className={b('pdisk-header')}>
291
297
  <div className={b('pdisk-title-wrapper')}>
292
- <EntityStatus status={data.Device} />
298
+ <EntityStatus status={Device} />
293
299
  <PDiskTitleBadge
294
300
  label="PDiskID"
295
- value={data.PDiskId}
301
+ value={PDiskId}
296
302
  className={b('pdisk-title-id')}
297
303
  />
298
304
  <PDiskTitleBadge value={getPDiskType(data)} className={b('pdisk-title-type')} />
299
305
  <ProgressViewer
300
- value={data.TotalSize - data.AvailableSize}
301
- capacity={data.TotalSize}
306
+ value={total - available}
307
+ capacity={total}
302
308
  formatValues={formatStorageValuesToGb}
303
309
  colorizeProgress={true}
304
310
  className={b('pdisk-title-size')}
305
311
  />
306
- <PDiskTitleBadge label="VDisks" value={data.vDisks.length} />
312
+ <PDiskTitleBadge label="VDisks" value={vDisks.length} />
307
313
  </div>
308
314
  <Button
309
315
  onClick={unfolded ? onClosePDiskDetails : onOpenPDiskDetails}
@@ -1,43 +1,19 @@
1
1
  import React from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
- import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
4
+ import type {TVDiskStateInfo} from '../../../types/api/vdisk';
5
5
  import {
6
6
  formatStorageValuesToGb,
7
7
  stringifyVdiskId,
8
8
  } from '../../../utils/dataFormatters/dataFormatters';
9
9
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
10
10
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
11
- import {valueIsDefined} from './NodeStructure';
12
11
  import InfoViewer from '../../../components/InfoViewer/InfoViewer';
12
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
13
13
 
14
- const b = cn('kv-node-structure');
14
+ import {valueIsDefined} from './NodeStructure';
15
15
 
16
- interface VdiskProps {
17
- AllocatedSize?: string;
18
- DiskSpace?: string;
19
- FrontQueues?: string;
20
- Guid?: string;
21
- Replicated?: boolean;
22
- VDiskState?: string;
23
- VDiskId?: {
24
- GroupId: number;
25
- GroupGeneration: number;
26
- Ring: number;
27
- Domain: number;
28
- VDisk: number;
29
- };
30
- VDiskSlotId?: number;
31
- Kind?: string;
32
- SatisfactionRank?: {FreshRank: {Flag: string}; LevelRank: {Flag: string}};
33
- AvailableSize?: string;
34
- HasUnreadableBlobs?: boolean;
35
- IncarnationGuid?: string;
36
- InstanceGuid?: string;
37
- StoragePoolName?: string;
38
- ReadThroughput?: string;
39
- WriteThroughput?: string;
40
- }
16
+ const b = cn('kv-node-structure');
41
17
 
42
18
  export function Vdisk({
43
19
  AllocatedSize,
@@ -57,7 +33,7 @@ export function Vdisk({
57
33
  StoragePoolName,
58
34
  ReadThroughput,
59
35
  WriteThroughput,
60
- }: VdiskProps) {
36
+ }: TVDiskStateInfo) {
61
37
  const vdiskInfo = [];
62
38
 
63
39
  if (valueIsDefined(VDiskSlotId)) {
@@ -81,16 +57,16 @@ export function Vdisk({
81
57
  value: <EntityStatus status={DiskSpace} />,
82
58
  });
83
59
  }
84
- if (valueIsDefined(SatisfactionRank?.FreshRank.Flag)) {
60
+ if (valueIsDefined(SatisfactionRank?.FreshRank?.Flag)) {
85
61
  vdiskInfo.push({
86
62
  label: 'Fresh Rank Satisfaction',
87
- value: <EntityStatus status={SatisfactionRank?.FreshRank.Flag} />,
63
+ value: <EntityStatus status={SatisfactionRank?.FreshRank?.Flag} />,
88
64
  });
89
65
  }
90
- if (valueIsDefined(SatisfactionRank?.LevelRank.Flag)) {
66
+ if (valueIsDefined(SatisfactionRank?.LevelRank?.Flag)) {
91
67
  vdiskInfo.push({
92
68
  label: 'Level Rank Satisfaction',
93
- value: <EntityStatus status={SatisfactionRank?.LevelRank.Flag} />,
69
+ value: <EntityStatus status={SatisfactionRank?.LevelRank?.Flag} />,
94
70
  });
95
71
  }
96
72
  vdiskInfo.push({label: 'Replicated', value: Replicated ? 'Yes' : 'No'});
@@ -26,7 +26,11 @@ import {
26
26
  useNodesRequestParams,
27
27
  useTableSort,
28
28
  } from '../../utils/hooks';
29
- import {isUnavailableNode, NodesUptimeFilterValues} from '../../utils/nodes';
29
+ import {
30
+ isSortableNodesProperty,
31
+ isUnavailableNode,
32
+ NodesUptimeFilterValues,
33
+ } from '../../utils/nodes';
30
34
 
31
35
  import {
32
36
  getNodes,
@@ -153,10 +157,14 @@ export const Nodes = ({path, type, additionalNodesProps = {}}: NodesProps) => {
153
157
  };
154
158
 
155
159
  const renderTable = () => {
156
- const columns = getNodesColumns({
160
+ const rawColumns = getNodesColumns({
157
161
  getNodeRef: additionalNodesProps.getNodeRef,
158
162
  });
159
163
 
164
+ const columns = rawColumns.map((column) => {
165
+ return {...column, sortable: isSortableNodesProperty(column.name)};
166
+ });
167
+
160
168
  if (nodes && nodes.length === 0) {
161
169
  if (
162
170
  problemFilter !== ProblemFilterValues.ALL ||