ydb-embedded-ui 3.0.1 → 3.2.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 (55) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +2 -0
  3. package/dist/components/DateRange/DateRange.scss +11 -0
  4. package/dist/components/DateRange/DateRange.tsx +75 -0
  5. package/dist/components/DateRange/index.ts +1 -0
  6. package/dist/components/Illustration/Illustration.tsx +4 -11
  7. package/dist/components/InfoViewer/InfoViewer.scss +2 -0
  8. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +1 -1
  9. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +16 -0
  10. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -5
  11. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +7 -7
  12. package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.scss +27 -0
  13. package/dist/containers/Tenant/Diagnostics/{TopShards/TopShards.tsx → OverloadedShards/OverloadedShards.tsx} +75 -20
  14. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +4 -0
  15. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/index.ts +11 -0
  16. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +4 -0
  17. package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +1 -0
  18. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.scss +16 -19
  19. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +202 -0
  20. package/dist/containers/Tenant/Diagnostics/TopQueries/i18n/en.json +4 -0
  21. package/dist/containers/Tenant/Diagnostics/TopQueries/i18n/index.ts +11 -0
  22. package/dist/containers/Tenant/Diagnostics/TopQueries/i18n/ru.json +4 -0
  23. package/dist/containers/Tenant/Diagnostics/TopQueries/index.ts +1 -0
  24. package/dist/containers/UserSettings/UserSettings.tsx +1 -1
  25. package/dist/services/api.d.ts +7 -0
  26. package/dist/store/reducers/describe.ts +4 -1
  27. package/dist/store/reducers/executeTopQueries.ts +170 -0
  28. package/dist/store/reducers/settings.js +1 -1
  29. package/dist/store/reducers/shardsWorkload.ts +91 -25
  30. package/dist/store/reducers/storage.js +2 -0
  31. package/dist/store/reducers/{tablets.js → tablets.ts} +30 -17
  32. package/dist/store/state-url-mapping.js +16 -0
  33. package/dist/types/api/compute.ts +52 -0
  34. package/dist/types/api/consumer.ts +257 -0
  35. package/dist/types/api/enums.ts +2 -2
  36. package/dist/types/api/nodes.ts +5 -2
  37. package/dist/types/api/pdisk.ts +3 -0
  38. package/dist/types/api/schema.ts +1 -0
  39. package/dist/types/api/storage.ts +31 -28
  40. package/dist/types/api/tablet.ts +18 -2
  41. package/dist/types/api/tenant.ts +4 -1
  42. package/dist/types/api/topic.ts +157 -0
  43. package/dist/types/api/vdisk.ts +3 -0
  44. package/dist/types/store/executeTopQueries.ts +29 -0
  45. package/dist/types/store/schema.ts +3 -3
  46. package/dist/types/store/shardsWorkload.ts +11 -2
  47. package/dist/types/store/tablets.ts +42 -0
  48. package/dist/utils/getNodesColumns.js +8 -1
  49. package/dist/utils/query.ts +1 -1
  50. package/package.json +3 -3
  51. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +0 -188
  52. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.scss +0 -7
  53. package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +0 -1
  54. package/dist/store/reducers/executeTopQueries.js +0 -66
  55. package/dist/types/api/consumers.ts +0 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.2.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.1.0...v3.2.0) (2023-01-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Nodes:** display rack in table ([3b8cdd5](https://github.com/ydb-platform/ydb-embedded-ui/commit/3b8cdd5b472f98132b2faaa9b71b8911750545a6))
9
+ * **StorageNodes:** display datacenter in table ([4507bfd](https://github.com/ydb-platform/ydb-embedded-ui/commit/4507bfde839b0aafa3722828b7528885c6ac8f84))
10
+ * **TopQueries:** date range filter ([b9a8e95](https://github.com/ydb-platform/ydb-embedded-ui/commit/b9a8e9504fa68556a724b214ee91b73ec900d37e))
11
+ * **TopQueries:** filter by query text ([2c8ea97](https://github.com/ydb-platform/ydb-embedded-ui/commit/2c8ea97dd215ea59165cf05315bc5809cf7fafd7))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * **InfoViewer:** min width for values ([64a4fd4](https://github.com/ydb-platform/ydb-embedded-ui/commit/64a4fd4de16738a9e2fac9cb4fba94eafc938762))
17
+ * **Nodes:** open external link in new tab ([b7c3ddd](https://github.com/ydb-platform/ydb-embedded-ui/commit/b7c3ddd1e611f2b61466e3eda51f3341f8407588))
18
+ * **TopQueries:** proper table dynamic render type ([9add6ca](https://github.com/ydb-platform/ydb-embedded-ui/commit/9add6ca9fbfe0475caf1586070a800210320cee6))
19
+ * **TopShards:** rename to overloaded shards ([d9978bd](https://github.com/ydb-platform/ydb-embedded-ui/commit/d9978bdd84b9a883e4eefcac7f85f856da55d770))
20
+ * **UserSettings:** treat invertedDisks settings as string ([ad7742a](https://github.com/ydb-platform/ydb-embedded-ui/commit/ad7742a6bf0be59c2b9cbbf947aaa66f79d748be))
21
+
22
+ ## [3.1.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.0.1...v3.1.0) (2022-12-13)
23
+
24
+
25
+ ### Features
26
+
27
+ * **TopShards:** date range filter ([aab4396](https://github.com/ydb-platform/ydb-embedded-ui/commit/aab439600ec28d30799c4a7ef7a9c68fcacc148c))
28
+
3
29
  ## [3.0.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.0.0...v3.0.1) (2022-12-12)
4
30
 
5
31
 
package/README.md CHANGED
@@ -15,6 +15,8 @@ Local viewer for YDB clusters
15
15
  3) Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.\
16
16
  You will also see any lint errors in the console.
17
17
 
18
+ For API reference, open Swagger UI on http://localhost:8765/viewer/api/.
19
+
18
20
  ### Making a production bundle.
19
21
 
20
22
  Base command `npm run build` builds the app for production to the `build` folder.\
@@ -0,0 +1,11 @@
1
+ .date-range {
2
+ &__input {
3
+ min-width: 190px;
4
+ padding: 5px 8px;
5
+
6
+ color: var(--yc-color-text-primary);
7
+ border: 1px solid var(--yc-color-line-generic);
8
+ border-radius: var(--yc-border-radius-m);
9
+ background: transparent;
10
+ }
11
+ }
@@ -0,0 +1,75 @@
1
+ import {ChangeEventHandler} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ import './DateRange.scss';
5
+
6
+ const b = cn('date-range');
7
+
8
+ export interface DateRangeValues {
9
+ /** ms from epoch */
10
+ from?: number;
11
+ /** ms from epoch */
12
+ to?: number;
13
+ }
14
+
15
+ interface DateRangeProps extends DateRangeValues {
16
+ className?: string;
17
+ onChange?: (value: DateRangeValues) => void;
18
+ }
19
+
20
+ const toTimezonelessISOString = (timestamp?: number) => {
21
+ if (!timestamp || isNaN(timestamp)) {
22
+ return undefined;
23
+ }
24
+
25
+ // shift by local offset to treat toISOString output as local time
26
+ const shiftedTimestamp = timestamp - new Date().getTimezoneOffset() * 60 * 1000;
27
+ return new Date(shiftedTimestamp).toISOString().substring(0, 'yyyy-MM-DDThh:mm'.length);
28
+ };
29
+
30
+ export const DateRange = ({from, to, className, onChange}: DateRangeProps) => {
31
+ const handleFromChange: ChangeEventHandler<HTMLInputElement> = ({target: {value}}) => {
32
+ let newFrom = value ? new Date(value).getTime() : undefined;
33
+
34
+ // some browsers allow selecting time after the boundary specified in `max`
35
+ if (newFrom && to && newFrom > to) {
36
+ newFrom = to;
37
+ }
38
+
39
+ onChange?.({from: newFrom, to});
40
+ };
41
+
42
+ const handleToChange: ChangeEventHandler<HTMLInputElement> = ({target: {value}}) => {
43
+ let newTo = value ? new Date(value).getTime() : undefined;
44
+
45
+ // some browsers allow selecting time before the boundary specified in `min`
46
+ if (from && newTo && from > newTo) {
47
+ newTo = from;
48
+ }
49
+
50
+ onChange?.({from, to: newTo});
51
+ };
52
+
53
+ const startISO = toTimezonelessISOString(from);
54
+ const endISO = toTimezonelessISOString(to);
55
+
56
+ return (
57
+ <div className={b(null, className)}>
58
+ <input
59
+ type="datetime-local"
60
+ value={startISO || ''}
61
+ max={endISO}
62
+ onChange={handleFromChange}
63
+ className={b('input')}
64
+ />
65
+
66
+ <input
67
+ type="datetime-local"
68
+ min={startISO}
69
+ value={endISO || ''}
70
+ onChange={handleToChange}
71
+ className={b('input')}
72
+ />
73
+ </div>
74
+ );
75
+ };
@@ -0,0 +1 @@
1
+ export * from './DateRange';
@@ -1,8 +1,8 @@
1
- import {useEffect, useState} from 'react';
1
+ import {ImgHTMLAttributes, useEffect, useState} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {useThemeValue} from '@gravity-ui/uikit';
4
4
 
5
- export interface IllustrationProps {
5
+ export interface IllustrationProps extends ImgHTMLAttributes<HTMLImageElement> {
6
6
  name: string;
7
7
  className?: string;
8
8
  }
@@ -38,12 +38,5 @@ export const Illustration = ({name, className, ...props}: IllustrationProps) =>
38
38
  }
39
39
  }, [srcGetter]);
40
40
 
41
- return (
42
- <img
43
- alt={name}
44
- src={src}
45
- className={b(null, className)}
46
- {...props}
47
- />
48
- );
49
- }
41
+ return <img alt={name} src={src} className={b(null, className)} {...props} />;
42
+ };
@@ -54,6 +54,8 @@
54
54
  &__value {
55
55
  display: flex;
56
56
 
57
+ min-width: 120px;
58
+
57
59
  word-break: break-all;
58
60
  }
59
61
 
@@ -27,7 +27,7 @@ function DiskStateProgressBar({
27
27
  diskAllocatedPercent = -1,
28
28
  severity,
29
29
  }: DiskStateProgressBarProps) {
30
- const inverted = useSelector((state) => getSettingValue(state, INVERTED_DISKS_KEY));
30
+ const inverted = useSelector((state) => JSON.parse(getSettingValue(state, INVERTED_DISKS_KEY)));
31
31
 
32
32
  const renderAllocatedPercent = () => {
33
33
  return (
@@ -16,6 +16,8 @@ import './StorageNodes.scss';
16
16
  enum TableColumnsIds {
17
17
  NodeId = 'NodeId',
18
18
  FQDN = 'FQDN',
19
+ DataCenter = 'DataCenter',
20
+ Rack = 'Rack',
19
21
  uptime = 'uptime',
20
22
  PDisks = 'PDisks',
21
23
  Missing = 'Missing',
@@ -36,6 +38,8 @@ interface StorageNodesProps {
36
38
  const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
37
39
  NodeId: 'Node ID',
38
40
  FQDN: 'FQDN',
41
+ DataCenter: 'DC',
42
+ Rack: 'Rack',
39
43
  uptime: 'Uptime',
40
44
  PDisks: 'PDisks',
41
45
  Missing: 'Missing',
@@ -96,6 +100,18 @@ function StorageNodes({
96
100
  },
97
101
  align: DataTable.LEFT,
98
102
  },
103
+ {
104
+ name: TableColumnsIds.DataCenter,
105
+ header: tableColumnsNames[TableColumnsIds.DataCenter],
106
+ render: ({row}) => row.DataCenter || '—',
107
+ align: DataTable.LEFT,
108
+ },
109
+ {
110
+ name: TableColumnsIds.Rack,
111
+ header: tableColumnsNames[TableColumnsIds.Rack],
112
+ render: ({row}) => row.Rack || '—',
113
+ align: DataTable.LEFT,
114
+ },
99
115
  {
100
116
  name: TableColumnsIds.uptime,
101
117
  header: tableColumnsNames[TableColumnsIds.uptime],
@@ -9,11 +9,10 @@ import {Switch, Tabs} from '@gravity-ui/uikit';
9
9
 
10
10
  import {Loader} from '../../../components/Loader';
11
11
 
12
- //@ts-ignore
13
- import TopQueries from './TopQueries/TopQueries';
12
+ import {TopQueries} from './TopQueries';
14
13
  //@ts-ignore
15
14
  import DetailedOverview from './DetailedOverview/DetailedOverview';
16
- import {TopShards} from './TopShards';
15
+ import {OverloadedShards} from './OverloadedShards';
17
16
  //@ts-ignore
18
17
  import Storage from '../../Storage/Storage';
19
18
  //@ts-ignore
@@ -130,8 +129,8 @@ function Diagnostics(props: DiagnosticsProps) {
130
129
  />
131
130
  );
132
131
  }
133
- case GeneralPagesIds.topShards: {
134
- return <TopShards tenantPath={tenantNameString} type={type} />;
132
+ case GeneralPagesIds.overloadedShards: {
133
+ return <OverloadedShards tenantPath={tenantNameString} type={type} />;
135
134
  }
136
135
  case GeneralPagesIds.nodes: {
137
136
  return (
@@ -3,7 +3,7 @@ import {EPathType} from '../../../types/api/schema';
3
3
  export enum GeneralPagesIds {
4
4
  'overview' = 'Overview',
5
5
  'topQueries' = 'topQueries',
6
- 'topShards' = 'topShards',
6
+ 'overloadedShards' = 'overloadedShards',
7
7
  'nodes' = 'Nodes',
8
8
  'tablets' = 'Tablets',
9
9
  'storage' = 'Storage',
@@ -29,9 +29,9 @@ const topQueries = {
29
29
  title: 'Top queries',
30
30
  };
31
31
 
32
- const topShards = {
33
- id: GeneralPagesIds.topShards,
34
- title: 'Top shards',
32
+ const overloadedShards = {
33
+ id: GeneralPagesIds.overloadedShards,
34
+ title: 'Overloaded shards',
35
35
  };
36
36
 
37
37
  const nodes = {
@@ -75,7 +75,7 @@ const consumers = {
75
75
  export const DATABASE_PAGES = [
76
76
  overview,
77
77
  topQueries,
78
- topShards,
78
+ overloadedShards,
79
79
  nodes,
80
80
  tablets,
81
81
  storage,
@@ -83,9 +83,9 @@ export const DATABASE_PAGES = [
83
83
  describe,
84
84
  ];
85
85
 
86
- export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, describe];
86
+ export const TABLE_PAGES = [overview, overloadedShards, graph, tablets, hotKeys, describe];
87
87
 
88
- export const DIR_PAGES = [overview, topShards, describe];
88
+ export const DIR_PAGES = [overview, overloadedShards, describe];
89
89
 
90
90
  export const CDC_STREAM_PAGES = [overview, consumers, describe];
91
91
  export const TOPIC_PAGES = [overview, consumers, describe];
@@ -0,0 +1,27 @@
1
+ .overloaded-shards {
2
+ display: flex;
3
+ flex-direction: column;
4
+
5
+ height: 100%;
6
+
7
+ background-color: var(--yc-color-base-background);
8
+
9
+ &__loader {
10
+ display: flex;
11
+ justify-content: center;
12
+ }
13
+
14
+ &__controls {
15
+ display: flex;
16
+ flex-wrap: wrap;
17
+ align-items: baseline;
18
+ gap: 16px;
19
+
20
+ margin-bottom: 10px;
21
+ }
22
+
23
+ &__table {
24
+ overflow: auto;
25
+ flex-grow: 1;
26
+ }
27
+ }
@@ -5,27 +5,36 @@ import cn from 'bem-cn-lite';
5
5
  import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
6
6
  import {Loader} from '@gravity-ui/uikit';
7
7
 
8
+ import {DateRange, DateRangeValues} from '../../../../components/DateRange';
8
9
  import InternalLink from '../../../../components/InternalLink/InternalLink';
9
10
 
10
11
  import HistoryContext from '../../../../contexts/HistoryContext';
11
12
 
12
13
  import routes, {createHref} from '../../../../routes';
13
14
 
14
- import {sendShardQuery, setShardQueryOptions} from '../../../../store/reducers/shardsWorkload';
15
+ import {
16
+ sendShardQuery,
17
+ setShardsState,
18
+ setShardsQueryFilters,
19
+ } from '../../../../store/reducers/shardsWorkload';
15
20
  import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
21
+ import type {IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
16
22
 
17
23
  import type {EPathType} from '../../../../types/api/schema';
18
24
 
19
- import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
25
+ import {formatDateTime, formatNumber} from '../../../../utils';
26
+ import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
20
27
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
21
- import {i18n} from '../../../../utils/i18n';
22
28
  import {prepareQueryError} from '../../../../utils/query';
23
29
 
30
+ import {getDefaultNodePath} from '../../../Node/NodePages';
31
+
24
32
  import {isColumnEntityType} from '../../utils/schema';
25
33
 
26
- import './TopShards.scss';
34
+ import i18n from './i18n';
35
+ import './OverloadedShards.scss';
27
36
 
28
- const b = cn('top-shards');
37
+ const b = cn('overloaded-shards');
29
38
  const bLink = cn('yc-link');
30
39
 
31
40
  const TABLE_SETTINGS: Settings = {
@@ -41,16 +50,15 @@ const tableColumnsNames = {
41
50
  CPUCores: 'CPUCores',
42
51
  DataSize: 'DataSize',
43
52
  Path: 'Path',
53
+ NodeId: 'NodeId',
54
+ PeakTime: 'PeakTime',
55
+ InFlightTxCount: 'InFlightTxCount',
44
56
  };
45
57
 
46
58
  function prepareCPUWorkloadValue(value: string) {
47
59
  return `${(Number(value) * 100).toFixed(2)}%`;
48
60
  }
49
61
 
50
- function prepareDateSizeValue(value: number) {
51
- return new Intl.NumberFormat(i18n.lang).format(value);
52
- }
53
-
54
62
  function stringToDataTableSortOrder(value: string): SortOrder[] | undefined {
55
63
  return value
56
64
  ? value.split(',').map((columnId) => ({
@@ -74,12 +82,12 @@ function dataTableToStringSortOrder(value: SortOrder | SortOrder[] = []) {
74
82
  return sortOrders.map(({columnId}) => columnId).join(',');
75
83
  }
76
84
 
77
- interface TopShardsProps {
85
+ interface OverloadedShardsProps {
78
86
  tenantPath: string;
79
87
  type?: EPathType;
80
88
  }
81
89
 
82
- export const TopShards = ({tenantPath, type}: TopShardsProps) => {
90
+ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
83
91
  const dispatch = useDispatch();
84
92
 
85
93
  const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
@@ -87,10 +95,24 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
87
95
  const {
88
96
  loading,
89
97
  data: {result: data = undefined} = {},
98
+ filters: storeFilters,
90
99
  error,
91
100
  wasLoaded,
92
101
  } = useTypedSelector((state) => state.shardsWorkload);
93
102
 
103
+ // default date range should be the last hour, but shouldn't propagate into URL until user interacts with the control
104
+ // redux initial value can't be used, as it synchronizes with URL
105
+ const [filters, setFilters] = useState<IShardsWorkloadFilters>(() => {
106
+ if (!storeFilters?.from && !storeFilters?.to) {
107
+ return {
108
+ from: Date.now() - HOUR_IN_SECONDS * 1000,
109
+ to: Date.now(),
110
+ };
111
+ }
112
+
113
+ return storeFilters;
114
+ });
115
+
94
116
  const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
95
117
 
96
118
  useAutofetcher(
@@ -100,32 +122,38 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
100
122
  database: tenantPath,
101
123
  path: currentSchemaPath,
102
124
  sortOrder: stringToQuerySortOrder(sortOrder),
125
+ filters,
103
126
  }),
104
127
  );
105
128
  },
106
- [dispatch, currentSchemaPath, tenantPath, sortOrder],
129
+ [dispatch, tenantPath, currentSchemaPath, sortOrder, filters],
107
130
  autorefresh,
108
131
  );
109
132
 
110
133
  // don't show loader for requests triggered by table sort, only for path change
111
134
  useEffect(() => {
112
135
  dispatch(
113
- setShardQueryOptions({
136
+ setShardsState({
114
137
  wasLoaded: false,
115
138
  data: undefined,
116
139
  }),
117
140
  );
118
- }, [dispatch, currentSchemaPath, tenantPath]);
141
+ }, [dispatch, currentSchemaPath, tenantPath, filters]);
119
142
 
120
143
  const history = useContext(HistoryContext);
121
144
 
122
145
  const onSort = (newSortOrder?: SortOrder | SortOrder[]) => {
123
- // omit information about sort order to disable ASC order, only DESC makes sense for top shards
146
+ // omit information about sort order to disable ASC order, only DESC makes sense for overloaded shards
124
147
  // use a string (and not the DataTable default format) to prevent reference change,
125
148
  // which would cause an excess state change, to avoid repeating requests
126
149
  setSortOrder(dataTableToStringSortOrder(newSortOrder));
127
150
  };
128
151
 
152
+ const handleDateRangeChange = (value: DateRangeValues) => {
153
+ dispatch(setShardsQueryFilters(value));
154
+ setFilters(value);
155
+ };
156
+
129
157
  const tableColumns: Column<any>[] = useMemo(() => {
130
158
  const onSchemaClick = (schemaPath: string) => {
131
159
  return () => {
@@ -161,7 +189,7 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
161
189
  name: tableColumnsNames.DataSize,
162
190
  header: 'DataSize (B)',
163
191
  render: ({value}) => {
164
- return prepareDateSizeValue(value as number);
192
+ return formatNumber(value as number);
165
193
  },
166
194
  align: DataTable.RIGHT,
167
195
  },
@@ -176,6 +204,29 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
176
204
  },
177
205
  sortable: false,
178
206
  },
207
+ {
208
+ name: tableColumnsNames.NodeId,
209
+ render: ({value: nodeId}) => {
210
+ return (
211
+ <InternalLink to={getDefaultNodePath(nodeId as string)}>
212
+ {nodeId as string}
213
+ </InternalLink>
214
+ );
215
+ },
216
+ align: DataTable.RIGHT,
217
+ sortable: false,
218
+ },
219
+ {
220
+ name: tableColumnsNames.PeakTime,
221
+ render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
222
+ sortable: false,
223
+ },
224
+ {
225
+ name: tableColumnsNames.InFlightTxCount,
226
+ render: ({value}) => formatNumber(value as number),
227
+ align: DataTable.RIGHT,
228
+ sortable: false,
229
+ },
179
230
  ];
180
231
  }, [dispatch, history, tenantPath]);
181
232
 
@@ -192,12 +243,12 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
192
243
  return renderLoader();
193
244
  }
194
245
 
195
- if (!data || data.length === 0 || isColumnEntityType(type)) {
196
- return 'No data';
246
+ if (error && !error.isCancelled) {
247
+ return <div className="error">{prepareQueryError(error)}</div>;
197
248
  }
198
249
 
199
- if (error && !error.isCancelled) {
200
- return prepareQueryError(error);
250
+ if (!data || isColumnEntityType(type)) {
251
+ return i18n('no-data');
201
252
  }
202
253
 
203
254
  return (
@@ -216,6 +267,10 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
216
267
 
217
268
  return (
218
269
  <div className={b()}>
270
+ <div className={b('controls')}>
271
+ {i18n('description')}
272
+ <DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
273
+ </div>
219
274
  {renderContent()}
220
275
  </div>
221
276
  );
@@ -0,0 +1,4 @@
1
+ {
2
+ "no-data": "No data",
3
+ "description": "Shards with CPU load over 70% are listed"
4
+ }
@@ -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-diagnostics-overloaded-shards';
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,4 @@
1
+ {
2
+ "no-data": "Нет данных",
3
+ "description": "Отображаются шарды с загрузкой CPU выше 70%"
4
+ }
@@ -0,0 +1 @@
1
+ export * from './OverloadedShards';
@@ -1,43 +1,40 @@
1
1
  @import '../../../../styles/mixins.scss';
2
2
 
3
3
  .kv-top-queries {
4
+ display: flex;
5
+ flex-direction: column;
6
+
4
7
  height: 100%;
5
8
 
6
9
  @include query-data-table;
7
- &__message-container {
8
- padding: 15px 0;
9
- }
10
10
 
11
11
  &__loader {
12
12
  display: flex;
13
13
  justify-content: center;
14
14
  }
15
15
 
16
- &__owner-container {
17
- margin: 10px;
16
+ &__controls {
17
+ display: flex;
18
+ flex-wrap: wrap;
19
+ gap: 16px;
18
20
 
19
- .yc-staff-card {
20
- display: inline-block;
21
- }
21
+ margin-bottom: 10px;
22
22
  }
23
23
 
24
- &__text {
25
- font-size: var(--yc-text-body-1-font-size);
26
- line-height: var(--yc-text-body-1-line-height);
27
-
28
- color: var(--yc-color-text-primary);
29
-
30
- &::first-letter {
31
- color: var(--yc-color-text-danger);
32
- }
24
+ &__search {
25
+ @include search();
33
26
  }
34
27
 
35
- &__result &__table-content {
36
- & .data-table {
28
+ &__result {
29
+ overflow: auto;
30
+ flex-grow: 1;
31
+
32
+ .data-table {
37
33
  &,
38
34
  &__table {
39
35
  width: 100%;
40
36
  }
37
+
41
38
  &__td {
42
39
  vertical-align: top;
43
40
  white-space: pre;