ydb-embedded-ui 4.20.4 → 4.21.1

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 (46) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/EmptyState/EmptyState.scss +0 -1
  3. package/dist/components/NodeHostWrapper/NodeHostWrapper.scss +2 -1
  4. package/dist/components/ProgressViewer/ProgressViewer.scss +1 -0
  5. package/dist/components/QueryResultTable/QueryResultTable.tsx +7 -3
  6. package/dist/components/TableWithControlsLayout/TableWithControlsLayout.scss +4 -0
  7. package/dist/components/VirtualTable/TableChunk.tsx +84 -0
  8. package/dist/components/VirtualTable/TableHead.tsx +139 -0
  9. package/dist/components/VirtualTable/TableRow.tsx +91 -0
  10. package/dist/components/VirtualTable/VirtualTable.scss +146 -0
  11. package/dist/components/VirtualTable/VirtualTable.tsx +277 -0
  12. package/dist/components/VirtualTable/constants.ts +17 -0
  13. package/dist/components/VirtualTable/i18n/en.json +3 -0
  14. package/dist/components/VirtualTable/i18n/index.ts +11 -0
  15. package/dist/components/VirtualTable/i18n/ru.json +3 -0
  16. package/dist/components/VirtualTable/index.ts +3 -0
  17. package/dist/components/VirtualTable/reducer.ts +143 -0
  18. package/dist/components/VirtualTable/shared.ts +3 -0
  19. package/dist/components/VirtualTable/types.ts +60 -0
  20. package/dist/components/VirtualTable/useIntersectionObserver.ts +42 -0
  21. package/dist/components/VirtualTable/utils.ts +3 -0
  22. package/dist/containers/App/App.scss +2 -1
  23. package/dist/containers/Cluster/Cluster.tsx +17 -4
  24. package/dist/containers/Node/Node.tsx +9 -17
  25. package/dist/containers/Node/NodeStructure/NodeStructure.tsx +8 -30
  26. package/dist/containers/Node/NodeStructure/Pdisk.tsx +29 -18
  27. package/dist/containers/Node/i18n/en.json +4 -0
  28. package/dist/containers/Node/i18n/index.ts +11 -0
  29. package/dist/containers/Node/i18n/ru.json +4 -0
  30. package/dist/containers/Nodes/Nodes.tsx +7 -28
  31. package/dist/containers/Nodes/VirtualNodes.tsx +146 -0
  32. package/dist/containers/Nodes/getNodes.ts +26 -0
  33. package/dist/containers/Nodes/getNodesColumns.tsx +49 -39
  34. package/dist/containers/Tablets/Tablets.tsx +3 -8
  35. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +0 -1
  36. package/dist/containers/UserSettings/i18n/en.json +4 -4
  37. package/dist/containers/UserSettings/i18n/ru.json +4 -4
  38. package/dist/containers/UserSettings/settings.ts +5 -6
  39. package/dist/store/reducers/nodes/nodes.ts +2 -2
  40. package/dist/store/reducers/nodes/types.ts +1 -0
  41. package/dist/store/reducers/settings/settings.ts +3 -3
  42. package/dist/utils/developerUI.ts +32 -0
  43. package/dist/utils/hooks/useNodesRequestParams.ts +4 -8
  44. package/dist/utils/nodes.ts +12 -0
  45. package/dist/utils/query.ts +8 -1
  46. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
- import DataTable, {type Column} from '@gravity-ui/react-data-table';
1
+ import DataTable, {type Column as DataTableColumn} from '@gravity-ui/react-data-table';
2
2
 
3
+ import type {Column as VirtualTableColumn} from '../../components/VirtualTable';
3
4
  import {PoolsGraph} from '../../components/PoolsGraph/PoolsGraph';
4
5
  import {ProgressViewer} from '../../components/ProgressViewer/ProgressViewer';
5
6
  import {TabletsStatistic} from '../../components/TabletsStatistic';
@@ -34,46 +35,52 @@ interface GetNodesColumnsProps {
34
35
  getNodeRef?: GetNodeRefFunc;
35
36
  }
36
37
 
37
- const nodeIdColumn: Column<NodesPreparedEntity> = {
38
+ type NodesColumn = VirtualTableColumn<NodesPreparedEntity> & DataTableColumn<NodesPreparedEntity>;
39
+
40
+ const nodeIdColumn: NodesColumn = {
38
41
  name: NODES_COLUMNS_IDS.NodeId,
39
42
  header: '#',
40
- width: '80px',
43
+ width: 80,
44
+ render: ({row}) => row.NodeId,
41
45
  align: DataTable.RIGHT,
42
46
  sortable: false,
43
47
  };
44
48
 
45
- const getHostColumn = (
46
- getNodeRef?: GetNodeRefFunc,
47
- fixedWidth = false,
48
- ): Column<NodesPreparedEntity> => ({
49
+ const getHostColumn = (getNodeRef?: GetNodeRefFunc): NodesColumn => ({
49
50
  name: NODES_COLUMNS_IDS.Host,
50
51
  render: ({row}) => {
51
52
  return <NodeHostWrapper node={row} getNodeRef={getNodeRef} />;
52
53
  },
53
- width: fixedWidth ? '350px' : undefined,
54
+ width: 350,
54
55
  align: DataTable.LEFT,
55
56
  sortable: false,
56
57
  });
57
58
 
58
- const dataCenterColumn: Column<NodesPreparedEntity> = {
59
+ const getHostColumnWithUndefinedWidth = (
60
+ getNodeRef?: GetNodeRefFunc,
61
+ ): DataTableColumn<NodesPreparedEntity> => {
62
+ return {...getHostColumn(getNodeRef), width: undefined};
63
+ };
64
+
65
+ const dataCenterColumn: NodesColumn = {
59
66
  name: NODES_COLUMNS_IDS.DC,
60
67
  header: 'DC',
61
68
  align: DataTable.LEFT,
62
69
  render: ({row}) => (row.DataCenter ? row.DataCenter : '—'),
63
- width: '60px',
70
+ width: 60,
64
71
  };
65
72
 
66
- const rackColumn: Column<NodesPreparedEntity> = {
73
+ const rackColumn: NodesColumn = {
67
74
  name: NODES_COLUMNS_IDS.Rack,
68
75
  header: 'Rack',
69
76
  align: DataTable.LEFT,
70
77
  render: ({row}) => (row.Rack ? row.Rack : '—'),
71
- width: '80px',
78
+ width: 80,
72
79
  };
73
80
 
74
- const versionColumn: Column<NodesPreparedEntity> = {
81
+ const versionColumn: NodesColumn = {
75
82
  name: NODES_COLUMNS_IDS.Version,
76
- width: '200px',
83
+ width: 200,
77
84
  align: DataTable.LEFT,
78
85
  render: ({row}) => {
79
86
  return <CellWithPopover content={row.Version}>{row.Version}</CellWithPopover>;
@@ -81,16 +88,17 @@ const versionColumn: Column<NodesPreparedEntity> = {
81
88
  sortable: false,
82
89
  };
83
90
 
84
- const uptimeColumn: Column<NodesPreparedEntity> = {
91
+ const uptimeColumn: NodesColumn = {
85
92
  name: NODES_COLUMNS_IDS.Uptime,
86
93
  header: 'Uptime',
87
94
  sortAccessor: ({StartTime}) => StartTime && -StartTime,
95
+ render: ({row}) => row.Uptime,
88
96
  align: DataTable.RIGHT,
89
- width: '110px',
97
+ width: 110,
90
98
  sortable: false,
91
99
  };
92
100
 
93
- const memoryColumn: Column<NodesPreparedEntity> = {
101
+ const memoryColumn: NodesColumn = {
94
102
  name: NODES_COLUMNS_IDS.Memory,
95
103
  header: 'Memory',
96
104
  sortAccessor: ({MemoryUsed = 0}) => Number(MemoryUsed),
@@ -103,21 +111,21 @@ const memoryColumn: Column<NodesPreparedEntity> = {
103
111
  }
104
112
  },
105
113
  align: DataTable.RIGHT,
106
- width: '120px',
114
+ width: 120,
107
115
  };
108
116
 
109
- const cpuColumn: Column<NodesPreparedEntity> = {
117
+ const cpuColumn: NodesColumn = {
110
118
  name: NODES_COLUMNS_IDS.CPU,
111
119
  header: 'CPU',
112
120
  sortAccessor: ({PoolStats = []}) => Math.max(...PoolStats.map(({Usage}) => Number(Usage))),
113
121
  defaultOrder: DataTable.DESCENDING,
114
122
  render: ({row}) => (row.PoolStats ? <PoolsGraph pools={row.PoolStats} /> : '—'),
115
123
  align: DataTable.LEFT,
116
- width: '80px',
124
+ width: 80,
117
125
  sortable: false,
118
126
  };
119
127
 
120
- const loadAverageColumn: Column<NodesPreparedEntity> = {
128
+ const loadAverageColumn: NodesColumn = {
121
129
  name: NODES_COLUMNS_IDS.LoadAverage,
122
130
  header: 'Load average',
123
131
  sortAccessor: ({LoadAverage = []}) =>
@@ -135,13 +143,13 @@ const loadAverageColumn: Column<NodesPreparedEntity> = {
135
143
  '—'
136
144
  ),
137
145
  align: DataTable.LEFT,
138
- width: '140px',
146
+ width: 140,
139
147
  sortable: false,
140
148
  };
141
149
 
142
- const getTabletsColumn = (tabletsPath?: string): Column<NodesPreparedEntity> => ({
150
+ const getTabletsColumn = (tabletsPath?: string): NodesColumn => ({
143
151
  name: NODES_COLUMNS_IDS.Tablets,
144
- width: '430px',
152
+ width: 430,
145
153
  render: ({row}) => {
146
154
  return row.Tablets ? (
147
155
  <TabletsStatistic
@@ -157,7 +165,7 @@ const getTabletsColumn = (tabletsPath?: string): Column<NodesPreparedEntity> =>
157
165
  sortable: false,
158
166
  });
159
167
 
160
- const topNodesLoadAverageColumn: Column<NodesPreparedEntity> = {
168
+ const topNodesLoadAverageColumn: NodesColumn = {
161
169
  name: NODES_COLUMNS_IDS.TopNodesLoadAverage,
162
170
  header: 'Load',
163
171
  render: ({row}) =>
@@ -170,11 +178,11 @@ const topNodesLoadAverageColumn: Column<NodesPreparedEntity> = {
170
178
  '—'
171
179
  ),
172
180
  align: DataTable.LEFT,
173
- width: '80px',
181
+ width: 80,
174
182
  sortable: false,
175
183
  };
176
184
 
177
- const topNodesMemoryColumn: Column<NodesPreparedEntity> = {
185
+ const topNodesMemoryColumn: NodesColumn = {
178
186
  name: NODES_COLUMNS_IDS.TopNodesMemory,
179
187
  header: 'Memory',
180
188
  render: ({row}) =>
@@ -189,17 +197,14 @@ const topNodesMemoryColumn: Column<NodesPreparedEntity> = {
189
197
  '—'
190
198
  ),
191
199
  align: DataTable.LEFT,
192
- width: '140px',
200
+ width: 140,
193
201
  sortable: false,
194
202
  };
195
203
 
196
- export function getNodesColumns({
197
- tabletsPath,
198
- getNodeRef,
199
- }: GetNodesColumnsProps): Column<NodesPreparedEntity>[] {
204
+ export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps): NodesColumn[] {
200
205
  return [
201
206
  nodeIdColumn,
202
- getHostColumn(getNodeRef, true),
207
+ getHostColumn(getNodeRef),
203
208
  dataCenterColumn,
204
209
  rackColumn,
205
210
  versionColumn,
@@ -213,23 +218,28 @@ export function getNodesColumns({
213
218
 
214
219
  export function getTopNodesByLoadColumns(
215
220
  getNodeRef?: GetNodeRefFunc,
216
- ): Column<NodesPreparedEntity>[] {
217
- return [topNodesLoadAverageColumn, nodeIdColumn, getHostColumn(getNodeRef), versionColumn];
221
+ ): DataTableColumn<NodesPreparedEntity>[] {
222
+ return [
223
+ topNodesLoadAverageColumn,
224
+ nodeIdColumn,
225
+ getHostColumnWithUndefinedWidth(getNodeRef),
226
+ versionColumn,
227
+ ];
218
228
  }
219
229
 
220
230
  export function getTopNodesByCpuColumns(
221
231
  getNodeRef?: GetNodeRefFunc,
222
- ): Column<NodesPreparedEntity>[] {
223
- return [cpuColumn, nodeIdColumn, getHostColumn(getNodeRef)];
232
+ ): DataTableColumn<NodesPreparedEntity>[] {
233
+ return [cpuColumn, nodeIdColumn, getHostColumnWithUndefinedWidth(getNodeRef)];
224
234
  }
225
235
 
226
236
  export function getTopNodesByMemoryColumns({
227
237
  tabletsPath,
228
238
  getNodeRef,
229
- }: GetNodesColumnsProps): Column<NodesPreparedEntity>[] {
239
+ }: GetNodesColumnsProps): NodesColumn[] {
230
240
  return [
231
241
  nodeIdColumn,
232
- getHostColumn(getNodeRef, true),
242
+ getHostColumn(getNodeRef),
233
243
  uptimeColumn,
234
244
  topNodesMemoryColumn,
235
245
  topNodesLoadAverageColumn,
@@ -34,14 +34,9 @@ interface TabletsProps {
34
34
  export const Tablets = ({path, nodeId, className}: TabletsProps) => {
35
35
  const dispatch = useDispatch();
36
36
 
37
- const {
38
- data = {},
39
- wasLoaded,
40
- loading,
41
- error,
42
- stateFilter,
43
- typeFilter,
44
- } = useTypedSelector((state) => state.tablets);
37
+ const {data, wasLoaded, loading, error, stateFilter, typeFilter} = useTypedSelector(
38
+ (state) => state.tablets,
39
+ );
45
40
  const {autorefresh} = useTypedSelector((state) => state.schema);
46
41
 
47
42
  const tablets = useMemo(() => data?.TabletStateInfo || [], [data]);
@@ -123,7 +123,6 @@ function Diagnostics(props: DiagnosticsProps) {
123
123
  return (
124
124
  <Nodes
125
125
  path={currentSchemaPath}
126
- type={type}
127
126
  additionalNodesProps={props.additionalNodesProps}
128
127
  />
129
128
  );
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "page.general": "General",
3
- "section.general": "General",
3
+ "section.appearance": "Appearance",
4
4
 
5
5
  "page.experiments": "Experiments",
6
6
  "section.experiments": "Experiments",
@@ -17,10 +17,10 @@
17
17
  "settings.invertedDisks.title": "Inverted disks space indicators",
18
18
 
19
19
  "settings.useNodesEndpoint.title": "Break the Nodes tab in Diagnostics",
20
- "settings.useNodesEndpoint.popover": "Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It returns incorrect data on versions before 23-1",
20
+ "settings.useNodesEndpoint.popover": "Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It could return incorrect data on some versions",
21
21
 
22
- "settings.useBackendParamsForTables.title": "Offload tables filters and sorting to backend",
23
- "settings.useBackendParamsForTables.popover": "Filter and sort Nodes and Storage tables with request params. May increase performance, but could causes additional fetches and longer loading time on older versions",
22
+ "settings.useBackendParamsForTables.title": "Use virtual table for cluster Nodes tab",
23
+ "settings.useBackendParamsForTables.popover": "Use table with data load on scroll. It will increase performance, but could work unstable",
24
24
 
25
25
  "settings.enableAdditionalQueryModes.title": "Enable additional query modes",
26
26
  "settings.enableAdditionalQueryModes.popover": "Adds 'Data', 'YQL - QueryService' and 'PostgreSQL' modes. May not work on some versions",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "page.general": "Общие",
3
- "section.general": "Общие",
3
+ "section.appearance": "Внешний вид",
4
4
 
5
5
  "page.experiments": "Эксперименты",
6
6
  "section.experiments": "Эксперименты",
@@ -17,10 +17,10 @@
17
17
  "settings.invertedDisks.title": "Инвертированные индикаторы места на дисках",
18
18
 
19
19
  "settings.useNodesEndpoint.title": "Сломать вкладку Nodes в диагностике",
20
- "settings.useNodesEndpoint.popover": "Использовать эндпоинт /viewer/json/nodes для вкладки Nodes в диагностике. Может возвращать некорректные данные на версиях до 23-1",
20
+ "settings.useNodesEndpoint.popover": "Использовать эндпоинт /viewer/json/nodes для вкладки Nodes в диагностике. Может возвращать некорректные данные на некоторых версиях",
21
21
 
22
- "settings.useBackendParamsForTables.title": "Перенести фильтры и сортировку таблиц на бэкенд",
23
- "settings.useBackendParamsForTables.popover": "Добавляет фильтрацию и сортировку таблиц Nodes и Storage с использованием параметров запроса. Может улушить производительность, но на старых версиях может привести к дополнительным запросам и большему времени ожидания загрузки",
22
+ "settings.useBackendParamsForTables.title": "Использовать виртуализированную таблицу для вкладки Nodes кластера",
23
+ "settings.useBackendParamsForTables.popover": "Использовать таблицу с загрузкой данных по скроллу. Это улучшит производительность, но может работать нестабильно",
24
24
 
25
25
  "settings.enableAdditionalQueryModes.title": "Включить дополнительные режимы выполнения запросов",
26
26
  "settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data', 'YQL - QueryService' и 'PostgreSQL'. Может работать некорректно на некоторых версиях",
@@ -101,16 +101,15 @@ export const enableNewTenantDiagnosticsDesign: SettingProps = {
101
101
  helpPopoverContent: i18n('settings.tenantDiagnostics.popover'),
102
102
  };
103
103
 
104
- export const generalSection: SettingsSection = {
105
- id: 'generalSection',
106
- title: i18n('section.general'),
107
- settings: [themeSetting],
104
+ export const appearanceSection: SettingsSection = {
105
+ id: 'appearanceSection',
106
+ title: i18n('section.appearance'),
107
+ settings: [themeSetting, invertedDisksSetting],
108
108
  };
109
109
  export const experimentsSection: SettingsSection = {
110
110
  id: 'experimentsSection',
111
111
  title: i18n('section.experiments'),
112
112
  settings: [
113
- invertedDisksSetting,
114
113
  useNodesEndpointSetting,
115
114
  useBackendParamsForTables,
116
115
  enableQueryModesForExplainSetting,
@@ -122,7 +121,7 @@ export const generalPage: SettingsPage = {
122
121
  id: 'generalPage',
123
122
  title: i18n('page.general'),
124
123
  icon: {data: favoriteFilledIcon, height: 14, width: 14},
125
- sections: [generalSection],
124
+ sections: [appearanceSection],
126
125
  };
127
126
  export const experimentsPage: SettingsPage = {
128
127
  id: 'experimentsPage',
@@ -99,9 +99,9 @@ const nodes: Reducer<NodesState, NodesAction> = (state = initialState, action) =
99
99
  };
100
100
  const concurrentId = 'getNodes';
101
101
 
102
- export function getNodes({type = 'any', ...params}: NodesApiRequestParams) {
102
+ export function getNodes({type = 'any', storage = false, ...params}: NodesApiRequestParams) {
103
103
  return createApiRequest({
104
- request: window.api.getNodes({type, ...params}, {concurrentId}),
104
+ request: window.api.getNodes({type, storage, ...params}, {concurrentId}),
105
105
  actions: FETCH_NODES,
106
106
  dataHandler: prepareNodesData,
107
107
  });
@@ -70,6 +70,7 @@ export interface NodesGeneralRequestParams extends NodesSortParams {
70
70
  }
71
71
 
72
72
  export interface NodesApiRequestParams extends NodesGeneralRequestParams {
73
+ path?: string;
73
74
  tenant?: string;
74
75
  type?: NodeType;
75
76
  visibleEntities?: VisibleEntities; // "with" param
@@ -50,15 +50,15 @@ export const initialState = {
50
50
  [INVERTED_DISKS_KEY]: readSavedSettingsValue(INVERTED_DISKS_KEY, 'false'),
51
51
  [USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY]: readSavedSettingsValue(
52
52
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
53
- 'true',
53
+ 'false',
54
54
  ),
55
55
  [ENABLE_ADDITIONAL_QUERY_MODES]: readSavedSettingsValue(
56
56
  ENABLE_ADDITIONAL_QUERY_MODES,
57
- 'false',
57
+ 'true',
58
58
  ),
59
59
  [DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS]: readSavedSettingsValue(
60
60
  DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
61
- 'false',
61
+ 'true',
62
62
  ),
63
63
  [SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
64
64
  [TENANT_INITIAL_PAGE_KEY]: readSavedSettingsValue(
@@ -0,0 +1,32 @@
1
+ import {backend} from '../store';
2
+ import {pad9} from './utils';
3
+
4
+ // Current node connects with target node by itself using nodeId
5
+ export const createDeveloperUILinkWithNodeId = (nodeId: number | string) => {
6
+ return `${backend}/node/${nodeId}/`;
7
+ };
8
+
9
+ interface PDiskDeveloperUILinkParams {
10
+ nodeId: number | string;
11
+ pDiskId: number | string;
12
+ }
13
+
14
+ export const createPDiskDeveloperUILink = ({nodeId, pDiskId}: PDiskDeveloperUILinkParams) => {
15
+ const pdiskPath = 'actors/pdisks/pdisk' + pad9(pDiskId);
16
+
17
+ return createDeveloperUILinkWithNodeId(nodeId) + pdiskPath;
18
+ };
19
+
20
+ interface VDiskDeveloperUILinkParams extends PDiskDeveloperUILinkParams {
21
+ vDiskSlotId: number | string;
22
+ }
23
+
24
+ export const createVDiskDeveloperUILink = ({
25
+ nodeId,
26
+ pDiskId,
27
+ vDiskSlotId,
28
+ }: VDiskDeveloperUILinkParams) => {
29
+ const vdiskPath = 'actors/vdisks/vdisk' + pad9(pDiskId) + '_' + pad9(vDiskSlotId);
30
+
31
+ return createDeveloperUILinkWithNodeId(nodeId) + vdiskPath;
32
+ };
@@ -2,10 +2,9 @@ import {useMemo} from 'react';
2
2
 
3
3
  import type {NodesGeneralRequestParams} from '../../store/reducers/nodes/types';
4
4
  import type {ProblemFilterValue} from '../../store/reducers/settings/types';
5
- import {ProblemFilterValues} from '../../store/reducers/settings/settings';
6
5
 
7
- import {HOUR_IN_SECONDS, USE_BACKEND_PARAMS_FOR_TABLES_KEY} from '../constants';
8
- import {NodesUptimeFilterValues} from '../nodes';
6
+ import {USE_BACKEND_PARAMS_FOR_TABLES_KEY} from '../constants';
7
+ import {NodesUptimeFilterValues, getProblemParamValue, getUptimeParamValue} from '../nodes';
9
8
  import {useSetting} from './useSetting';
10
9
 
11
10
  interface NodesRawRequestParams
@@ -27,11 +26,8 @@ export const useNodesRequestParams = ({
27
26
  // Otherwise no params will be updated, no hooks that depend on requestParams will be triggered
28
27
  return useMemo(() => {
29
28
  if (useBackendParamsForTables) {
30
- const problemsOnly = problemFilter === ProblemFilterValues.PROBLEMS;
31
- const uptime =
32
- nodesUptimeFilter === NodesUptimeFilterValues.SmallUptime
33
- ? HOUR_IN_SECONDS
34
- : undefined;
29
+ const problemsOnly = getProblemParamValue(problemFilter);
30
+ const uptime = getUptimeParamValue(nodesUptimeFilter);
35
31
 
36
32
  return {
37
33
  filter,
@@ -3,8 +3,12 @@ import type {TNodeInfo} from '../types/api/nodesList';
3
3
  import type {NodesPreparedEntity} from '../store/reducers/nodes/types';
4
4
  import type {NodesMap} from '../types/store/nodesList';
5
5
  import type {ValueOf} from '../types/common';
6
+ import type {ProblemFilterValue} from '../store/reducers/settings/types';
7
+ import {ProblemFilterValues} from '../store/reducers/settings/settings';
6
8
  import {EFlag} from '../types/api/enums';
7
9
 
10
+ import {HOUR_IN_SECONDS} from './constants';
11
+
8
12
  export enum NodesUptimeFilterValues {
9
13
  'All' = 'All',
10
14
  'SmallUptime' = 'SmallUptime',
@@ -27,6 +31,14 @@ export const prepareNodesMap = (nodesList?: TNodeInfo[]) => {
27
31
  }, new Map());
28
32
  };
29
33
 
34
+ export const getProblemParamValue = (problemFilter: ProblemFilterValue | undefined) => {
35
+ return problemFilter === ProblemFilterValues.PROBLEMS;
36
+ };
37
+
38
+ export const getUptimeParamValue = (nodesUptimeFilter: NodesUptimeFilterValues | undefined) => {
39
+ return nodesUptimeFilter === NodesUptimeFilterValues.SmallUptime ? HOUR_IN_SECONDS : undefined;
40
+ };
41
+
30
42
  /**
31
43
  * Values to sort /compute v2 and /nodes responses
32
44
  *
@@ -175,7 +175,14 @@ export const prepareQueryResponse = (data?: KeyValueRow[]) => {
175
175
  for (const field in row) {
176
176
  if (Object.prototype.hasOwnProperty.call(row, field)) {
177
177
  const type = typeof row[field];
178
- if (type === 'object' || type === 'boolean' || Array.isArray(row[field])) {
178
+
179
+ // Although typeof null == 'object'
180
+ // null result should be preserved
181
+ if (
182
+ (row[field] !== null && type === 'object') ||
183
+ type === 'boolean' ||
184
+ Array.isArray(row[field])
185
+ ) {
179
186
  formattedData[field] = JSON.stringify(row[field]);
180
187
  } else {
181
188
  formattedData[field] = row[field];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.20.4",
3
+ "version": "4.21.1",
4
4
  "files": [
5
5
  "dist"
6
6
  ],