ydb-embedded-ui 4.32.0 → 4.33.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.
- package/dist/components/CellWithPopover/CellWithPopover.scss +7 -0
- package/dist/components/Loader/Loader.tsx +3 -2
- package/dist/components/MetricChart/MetricChart.tsx +45 -4
- package/dist/components/MetricChart/index.ts +1 -1
- package/dist/components/MetricChart/types.ts +3 -0
- package/dist/components/VirtualTable/TableHead.tsx +127 -26
- package/dist/components/VirtualTable/TableRow.tsx +15 -2
- package/dist/components/VirtualTable/VirtualTable.scss +62 -60
- package/dist/components/VirtualTable/VirtualTable.tsx +11 -1
- package/dist/components/VirtualTable/types.ts +1 -0
- package/dist/containers/Nodes/VirtualNodes.tsx +5 -1
- package/dist/containers/Nodes/getNodesColumns.tsx +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +2 -2
- package/dist/containers/Tenant/Diagnostics/TenantOverview/DefaultOverviewContent/DefaultOverviewContent.tsx +6 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/{DefaultDashboard.tsx → DefaultOverviewContent/defaultDashboardConfig.ts} +3 -7
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx +3 -2
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/{CpuDashboard.tsx → cpuDashboardConfig.ts} +2 -6
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantDashboard/TenantDashboard.tsx +27 -9
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TenantMemory.tsx +3 -2
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/{MemoryDashboard.tsx → memoryDashboardConfig.ts} +2 -6
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +3 -5
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverviewTableLayout.tsx +1 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx +6 -6
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/{StorageDashboard.tsx → storageDashboardConfig.ts} +2 -6
- package/dist/containers/Tenant/Diagnostics/TenantOverview/utils.ts +3 -0
- package/dist/containers/UserSettings/i18n/en.json +1 -4
- package/dist/containers/UserSettings/i18n/ru.json +1 -4
- package/dist/containers/UserSettings/settings.ts +1 -12
- package/dist/services/settings.ts +0 -2
- package/dist/utils/constants.ts +0 -2
- package/dist/utils/hooks/useTableResize.ts +53 -0
- package/package.json +1 -1
@@ -1,13 +1,20 @@
|
|
1
1
|
.ydb-cell-with-popover {
|
2
2
|
display: flex;
|
3
3
|
|
4
|
+
max-width: 100%;
|
5
|
+
|
4
6
|
&__popover {
|
5
7
|
display: inline-block;
|
6
8
|
overflow: hidden;
|
7
9
|
|
8
10
|
max-width: 100%;
|
9
11
|
|
12
|
+
vertical-align: middle;
|
10
13
|
white-space: nowrap;
|
11
14
|
text-overflow: ellipsis;
|
15
|
+
|
16
|
+
.yc-popover__handler {
|
17
|
+
display: inline;
|
18
|
+
}
|
12
19
|
}
|
13
20
|
}
|
@@ -7,11 +7,12 @@ const b = cn('ydb-loader');
|
|
7
7
|
|
8
8
|
interface LoaderProps {
|
9
9
|
size?: LoaderSize;
|
10
|
+
className?: string;
|
10
11
|
}
|
11
12
|
|
12
|
-
export const Loader = ({size = 'm'}: LoaderProps) => {
|
13
|
+
export const Loader = ({size = 'm', className}: LoaderProps) => {
|
13
14
|
return (
|
14
|
-
<div className={b()}>
|
15
|
+
<div className={b(null, className)}>
|
15
16
|
<KitLoader size={size} />
|
16
17
|
</div>
|
17
18
|
);
|
@@ -6,13 +6,19 @@ import ChartKit, {settings} from '@gravity-ui/chartkit';
|
|
6
6
|
import type {IResponseError} from '../../types/api/error';
|
7
7
|
import type {TimeFrame} from '../../utils/timeframes';
|
8
8
|
import {useAutofetcher} from '../../utils/hooks';
|
9
|
+
|
9
10
|
import {COLORS} from '../../utils/versions';
|
10
11
|
import {cn} from '../../utils/cn';
|
11
12
|
|
12
13
|
import {Loader} from '../Loader';
|
13
14
|
import {ResponseError} from '../Errors/ResponseError';
|
14
15
|
|
15
|
-
import type {
|
16
|
+
import type {
|
17
|
+
ChartOptions,
|
18
|
+
MetricDescription,
|
19
|
+
OnChartDataStatusChange,
|
20
|
+
PreparedMetricsData,
|
21
|
+
} from './types';
|
16
22
|
import {convertResponse} from './convertReponse';
|
17
23
|
import {getDefaultDataFormatter} from './getDefaultDataFormatter';
|
18
24
|
import {getChartData} from './getChartData';
|
@@ -102,6 +108,15 @@ interface DiagnosticsChartProps {
|
|
102
108
|
width?: number;
|
103
109
|
|
104
110
|
chartOptions?: ChartOptions;
|
111
|
+
|
112
|
+
onChartDataStatusChange?: OnChartDataStatusChange;
|
113
|
+
|
114
|
+
/**
|
115
|
+
* YAGR charts don't render correctly inside not visible elements\
|
116
|
+
* So if chart is used inside component with 'display:none', it will be empty, when visibility change\
|
117
|
+
* Pass isChartVisible prop to ensure proper chart render
|
118
|
+
*/
|
119
|
+
isChartVisible?: boolean;
|
105
120
|
}
|
106
121
|
|
107
122
|
export const MetricChart = ({
|
@@ -112,6 +127,8 @@ export const MetricChart = ({
|
|
112
127
|
width = 400,
|
113
128
|
height = width / 1.5,
|
114
129
|
chartOptions,
|
130
|
+
onChartDataStatusChange,
|
131
|
+
isChartVisible,
|
115
132
|
}: DiagnosticsChartProps) => {
|
116
133
|
const mounted = useRef(false);
|
117
134
|
|
@@ -127,6 +144,20 @@ export const MetricChart = ({
|
|
127
144
|
initialChartState,
|
128
145
|
);
|
129
146
|
|
147
|
+
useEffect(() => {
|
148
|
+
if (error) {
|
149
|
+
return onChartDataStatusChange?.('error');
|
150
|
+
}
|
151
|
+
if (loading && !wasLoaded) {
|
152
|
+
return onChartDataStatusChange?.('loading');
|
153
|
+
}
|
154
|
+
if (!loading && wasLoaded) {
|
155
|
+
return onChartDataStatusChange?.('success');
|
156
|
+
}
|
157
|
+
|
158
|
+
return undefined;
|
159
|
+
}, [loading, wasLoaded, error, onChartDataStatusChange]);
|
160
|
+
|
130
161
|
const fetchChartData = useCallback(
|
131
162
|
async (isBackground: boolean) => {
|
132
163
|
dispatch(setChartDataLoading());
|
@@ -146,7 +177,9 @@ export const MetricChart = ({
|
|
146
177
|
});
|
147
178
|
|
148
179
|
// Hack to prevent setting value to state, if component unmounted
|
149
|
-
if (!mounted.current)
|
180
|
+
if (!mounted.current) {
|
181
|
+
return;
|
182
|
+
}
|
150
183
|
|
151
184
|
// In some cases error could be in response with 200 status code
|
152
185
|
// It happens when request is OK, but chart data cannot be returned due to some reason
|
@@ -155,10 +188,14 @@ export const MetricChart = ({
|
|
155
188
|
const preparedData = convertResponse(response, metrics);
|
156
189
|
dispatch(setChartData(preparedData));
|
157
190
|
} else {
|
158
|
-
|
191
|
+
const err = {statusText: response.error};
|
192
|
+
|
193
|
+
throw err;
|
159
194
|
}
|
160
195
|
} catch (err) {
|
161
|
-
if (!mounted.current)
|
196
|
+
if (!mounted.current) {
|
197
|
+
return;
|
198
|
+
}
|
162
199
|
|
163
200
|
dispatch(setChartError(err as IResponseError));
|
164
201
|
}
|
@@ -175,6 +212,10 @@ export const MetricChart = ({
|
|
175
212
|
return <Loader />;
|
176
213
|
}
|
177
214
|
|
215
|
+
if (!isChartVisible) {
|
216
|
+
return null;
|
217
|
+
}
|
218
|
+
|
178
219
|
return (
|
179
220
|
<div className={b('chart')}>
|
180
221
|
<ChartKit type="yagr" data={convertedData} />
|
@@ -1,2 +1,2 @@
|
|
1
|
-
export
|
1
|
+
export * from './types';
|
2
2
|
export {MetricChart} from './MetricChart';
|
@@ -1,14 +1,21 @@
|
|
1
|
-
import {useState} from 'react';
|
1
|
+
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
2
|
+
|
3
|
+
import type {
|
4
|
+
HandleTableColumnsResize,
|
5
|
+
TableColumnsWidthSetup,
|
6
|
+
} from '../../utils/hooks/useTableResize';
|
2
7
|
|
3
8
|
import type {Column, OnSort, SortOrderType, SortParams} from './types';
|
4
9
|
import {ASCENDING, DEFAULT_SORT_ORDER, DEFAULT_TABLE_ROW_HEIGHT, DESCENDING} from './constants';
|
5
10
|
import {b} from './shared';
|
6
11
|
|
12
|
+
const COLUMN_NAME_HTML_ATTRIBUTE = 'data-columnname';
|
13
|
+
|
7
14
|
// Icon similar to original DataTable icons to keep the same tables across diferent pages and tabs
|
8
15
|
const SortIcon = ({order}: {order?: SortOrderType}) => {
|
9
16
|
return (
|
10
17
|
<svg
|
11
|
-
className={b('icon', {desc: order === DESCENDING})}
|
18
|
+
className={b('sort-icon', {desc: order === DESCENDING})}
|
12
19
|
viewBox="0 0 10 6"
|
13
20
|
width="10"
|
14
21
|
height="6"
|
@@ -27,7 +34,7 @@ interface ColumnSortIconProps {
|
|
27
34
|
const ColumnSortIcon = ({sortOrder, sortable, defaultSortOrder}: ColumnSortIconProps) => {
|
28
35
|
if (sortable) {
|
29
36
|
return (
|
30
|
-
<span className={b('sort-icon', {shadow: !sortOrder})}>
|
37
|
+
<span className={b('sort-icon-container', {shadow: !sortOrder})}>
|
31
38
|
<SortIcon order={sortOrder || defaultSortOrder} />
|
32
39
|
</span>
|
33
40
|
);
|
@@ -36,9 +43,84 @@ const ColumnSortIcon = ({sortOrder, sortable, defaultSortOrder}: ColumnSortIconP
|
|
36
43
|
}
|
37
44
|
};
|
38
45
|
|
46
|
+
interface TableHeadCellProps<T> {
|
47
|
+
column: Column<T>;
|
48
|
+
sortOrder?: SortOrderType;
|
49
|
+
defaultSortOrder: SortOrderType;
|
50
|
+
onSort?: (columnName: string) => void;
|
51
|
+
rowHeight: number;
|
52
|
+
onCellMount?: (element: Element) => void;
|
53
|
+
onCellUnMount?: (element: Element) => void;
|
54
|
+
}
|
55
|
+
|
56
|
+
export const TableHeadCell = <T,>({
|
57
|
+
column,
|
58
|
+
sortOrder,
|
59
|
+
defaultSortOrder,
|
60
|
+
onSort,
|
61
|
+
rowHeight,
|
62
|
+
onCellMount,
|
63
|
+
onCellUnMount,
|
64
|
+
}: TableHeadCellProps<T>) => {
|
65
|
+
const cellWrapperRef = useRef<HTMLDivElement>(null);
|
66
|
+
|
67
|
+
useEffect(() => {
|
68
|
+
const cellWrapper = cellWrapperRef.current;
|
69
|
+
if (cellWrapper) {
|
70
|
+
onCellMount?.(cellWrapper);
|
71
|
+
}
|
72
|
+
return () => {
|
73
|
+
if (cellWrapper) {
|
74
|
+
onCellUnMount?.(cellWrapper);
|
75
|
+
}
|
76
|
+
};
|
77
|
+
}, [onCellMount, onCellUnMount]);
|
78
|
+
|
79
|
+
const content = column.header ?? column.name;
|
80
|
+
|
81
|
+
return (
|
82
|
+
<th>
|
83
|
+
<div
|
84
|
+
ref={cellWrapperRef}
|
85
|
+
className={b('head-cell-wrapper', {
|
86
|
+
resizeable: column.resizeable,
|
87
|
+
})}
|
88
|
+
style={{
|
89
|
+
height: `${rowHeight}px`,
|
90
|
+
width: `${column.width}px`,
|
91
|
+
}}
|
92
|
+
{...{
|
93
|
+
[COLUMN_NAME_HTML_ATTRIBUTE]: column.name,
|
94
|
+
}}
|
95
|
+
>
|
96
|
+
<div
|
97
|
+
className={b(
|
98
|
+
'head-cell',
|
99
|
+
{align: column.align, sortable: column.sortable},
|
100
|
+
column.className,
|
101
|
+
)}
|
102
|
+
onClick={() => {
|
103
|
+
if (column.sortable) {
|
104
|
+
onSort?.(column.name);
|
105
|
+
}
|
106
|
+
}}
|
107
|
+
>
|
108
|
+
<div className={b('head-cell-content')}>{content}</div>
|
109
|
+
<ColumnSortIcon
|
110
|
+
sortOrder={sortOrder}
|
111
|
+
sortable={column.sortable}
|
112
|
+
defaultSortOrder={defaultSortOrder}
|
113
|
+
/>
|
114
|
+
</div>
|
115
|
+
</div>
|
116
|
+
</th>
|
117
|
+
);
|
118
|
+
};
|
119
|
+
|
39
120
|
interface TableHeadProps<T> {
|
40
121
|
columns: Column<T>[];
|
41
122
|
onSort?: OnSort;
|
123
|
+
onColumnsResize?: HandleTableColumnsResize;
|
42
124
|
defaultSortOrder?: SortOrderType;
|
43
125
|
rowHeight?: number;
|
44
126
|
}
|
@@ -46,11 +128,44 @@ interface TableHeadProps<T> {
|
|
46
128
|
export const TableHead = <T,>({
|
47
129
|
columns,
|
48
130
|
onSort,
|
131
|
+
onColumnsResize,
|
49
132
|
defaultSortOrder = DEFAULT_SORT_ORDER,
|
50
133
|
rowHeight = DEFAULT_TABLE_ROW_HEIGHT,
|
51
134
|
}: TableHeadProps<T>) => {
|
52
135
|
const [sortParams, setSortParams] = useState<SortParams>({});
|
53
136
|
|
137
|
+
const isTableResizeable = Boolean(onColumnsResize);
|
138
|
+
|
139
|
+
const resizeObserver: ResizeObserver | undefined = useMemo(() => {
|
140
|
+
if (!isTableResizeable) {
|
141
|
+
return undefined;
|
142
|
+
}
|
143
|
+
|
144
|
+
return new ResizeObserver((entries) => {
|
145
|
+
const columnsWidth: TableColumnsWidthSetup = {};
|
146
|
+
entries.forEach((entry) => {
|
147
|
+
// @ts-ignore ignore custrom property usage
|
148
|
+
const id = entry.target.attributes[COLUMN_NAME_HTML_ATTRIBUTE]?.value;
|
149
|
+
columnsWidth[id] = entry.contentRect.width;
|
150
|
+
});
|
151
|
+
|
152
|
+
onColumnsResize?.(columnsWidth);
|
153
|
+
});
|
154
|
+
}, [onColumnsResize, isTableResizeable]);
|
155
|
+
|
156
|
+
const handleCellMount = useCallback(
|
157
|
+
(element: Element) => {
|
158
|
+
resizeObserver?.observe(element);
|
159
|
+
},
|
160
|
+
[resizeObserver],
|
161
|
+
);
|
162
|
+
const handleCellUnMount = useCallback(
|
163
|
+
(element: Element) => {
|
164
|
+
resizeObserver?.unobserve(element);
|
165
|
+
},
|
166
|
+
[resizeObserver],
|
167
|
+
);
|
168
|
+
|
54
169
|
const handleSort = (columnId: string) => {
|
55
170
|
let newSortParams: SortParams = {};
|
56
171
|
|
@@ -95,34 +210,20 @@ export const TableHead = <T,>({
|
|
95
210
|
<thead className={b('head')}>
|
96
211
|
<tr>
|
97
212
|
{columns.map((column) => {
|
98
|
-
const content = column.header ?? column.name;
|
99
213
|
const sortOrder =
|
100
214
|
sortParams.columnId === column.name ? sortParams.sortOrder : undefined;
|
101
215
|
|
102
216
|
return (
|
103
|
-
<
|
217
|
+
<TableHeadCell
|
104
218
|
key={column.name}
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
onClick={() => {
|
114
|
-
handleSort(column.name);
|
115
|
-
}}
|
116
|
-
>
|
117
|
-
<div className={b('head-cell')}>
|
118
|
-
{content}
|
119
|
-
<ColumnSortIcon
|
120
|
-
sortOrder={sortOrder}
|
121
|
-
sortable={column.sortable}
|
122
|
-
defaultSortOrder={defaultSortOrder}
|
123
|
-
/>
|
124
|
-
</div>
|
125
|
-
</th>
|
219
|
+
column={column}
|
220
|
+
sortOrder={sortOrder}
|
221
|
+
defaultSortOrder={defaultSortOrder}
|
222
|
+
onSort={handleSort}
|
223
|
+
rowHeight={rowHeight}
|
224
|
+
onCellMount={handleCellMount}
|
225
|
+
onCellUnMount={handleCellUnMount}
|
226
|
+
/>
|
126
227
|
);
|
127
228
|
})}
|
128
229
|
</tr>
|
@@ -8,14 +8,25 @@ import {b} from './shared';
|
|
8
8
|
|
9
9
|
interface TableCellProps {
|
10
10
|
height: number;
|
11
|
+
width: number;
|
11
12
|
align?: AlignType;
|
12
13
|
children: ReactNode;
|
13
14
|
className?: string;
|
14
15
|
}
|
15
16
|
|
16
|
-
const TableRowCell = ({
|
17
|
+
const TableRowCell = ({
|
18
|
+
children,
|
19
|
+
className,
|
20
|
+
height,
|
21
|
+
width,
|
22
|
+
align = DEFAULT_ALIGN,
|
23
|
+
}: TableCellProps) => {
|
24
|
+
// Additional maxWidth to ensure overflow hidden for <td>
|
17
25
|
return (
|
18
|
-
<td
|
26
|
+
<td
|
27
|
+
className={b('row-cell', {align: align}, className)}
|
28
|
+
style={{height: `${height}px`, width: `${width}px`, maxWidth: `${width}px`}}
|
29
|
+
>
|
19
30
|
{children}
|
20
31
|
</td>
|
21
32
|
);
|
@@ -35,6 +46,7 @@ export const LoadingTableRow = <T,>({index, columns, height}: LoadingTableRowPro
|
|
35
46
|
<TableRowCell
|
36
47
|
key={`${column.name}${index}`}
|
37
48
|
height={height}
|
49
|
+
width={column.width}
|
38
50
|
align={column.align}
|
39
51
|
className={column.className}
|
40
52
|
>
|
@@ -64,6 +76,7 @@ export const TableRow = <T,>({row, index, columns, getRowClassName, height}: Tab
|
|
64
76
|
<TableRowCell
|
65
77
|
key={`${column.name}${index}`}
|
66
78
|
height={height}
|
79
|
+
width={column.width}
|
67
80
|
align={column.align}
|
68
81
|
className={column.className}
|
69
82
|
>
|
@@ -6,8 +6,6 @@
|
|
6
6
|
--virtual-table-cell-vertical-padding: 5px;
|
7
7
|
--virtual-table-cell-horizontal-padding: 10px;
|
8
8
|
|
9
|
-
--virtual-table-sort-icon-space: 18px;
|
10
|
-
|
11
9
|
--virtual-table-border-color: var(--g-color-base-generic-hover);
|
12
10
|
--virtual-table-hover-color: var(--g-color-base-float-hover);
|
13
11
|
|
@@ -21,6 +19,10 @@
|
|
21
19
|
table-layout: fixed;
|
22
20
|
border-spacing: 0;
|
23
21
|
border-collapse: separate;
|
22
|
+
|
23
|
+
th {
|
24
|
+
padding: 0;
|
25
|
+
}
|
24
26
|
}
|
25
27
|
|
26
28
|
&__row {
|
@@ -40,96 +42,96 @@
|
|
40
42
|
@include sticky-top();
|
41
43
|
}
|
42
44
|
|
43
|
-
&
|
44
|
-
|
45
|
-
|
46
|
-
padding: var(--virtual-table-cell-vertical-padding)
|
47
|
-
var(--virtual-table-cell-horizontal-padding);
|
48
|
-
|
49
|
-
font-weight: bold;
|
50
|
-
cursor: default;
|
51
|
-
text-align: left;
|
45
|
+
&__sort-icon-container {
|
46
|
+
display: flex;
|
47
|
+
justify-content: center;
|
52
48
|
|
53
|
-
|
49
|
+
color: inherit;
|
54
50
|
|
55
|
-
&
|
56
|
-
|
51
|
+
&_shadow {
|
52
|
+
opacity: 0.15;
|
53
|
+
}
|
54
|
+
}
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
&__sort-icon {
|
57
|
+
&_desc {
|
58
|
+
transform: rotate(180deg);
|
59
|
+
}
|
60
|
+
}
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
padding-left: var(--virtual-table-sort-icon-space);
|
66
|
-
}
|
62
|
+
&__head-cell-wrapper {
|
63
|
+
display: flex;
|
64
|
+
overflow-x: hidden;
|
67
65
|
|
68
|
-
|
69
|
-
right: auto;
|
70
|
-
left: 0;
|
66
|
+
border-bottom: $cell-border;
|
71
67
|
|
72
|
-
|
73
|
-
|
74
|
-
}
|
68
|
+
&_resizeable {
|
69
|
+
resize: horizontal;
|
75
70
|
}
|
76
71
|
}
|
77
72
|
|
78
73
|
&__head-cell {
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
overflow: hidden;
|
74
|
+
display: flex;
|
75
|
+
flex-direction: row;
|
76
|
+
align-items: center;
|
83
77
|
|
84
|
-
|
78
|
+
width: 100%;
|
85
79
|
max-width: 100%;
|
80
|
+
padding: var(--virtual-table-cell-vertical-padding)
|
81
|
+
var(--virtual-table-cell-horizontal-padding);
|
86
82
|
|
87
|
-
|
88
|
-
|
89
|
-
|
83
|
+
&_align {
|
84
|
+
&_left {
|
85
|
+
justify-content: left;
|
86
|
+
}
|
87
|
+
&_center {
|
88
|
+
justify-content: center;
|
89
|
+
}
|
90
|
+
&_right {
|
91
|
+
justify-content: right;
|
92
|
+
}
|
93
|
+
}
|
90
94
|
}
|
91
95
|
|
92
|
-
&
|
93
|
-
|
94
|
-
top: 50%;
|
95
|
-
right: 0;
|
96
|
-
|
97
|
-
display: inline-flex;
|
96
|
+
&__head-cell {
|
97
|
+
gap: 8px;
|
98
98
|
|
99
|
-
|
99
|
+
font-weight: bold;
|
100
|
+
cursor: default;
|
100
101
|
|
101
|
-
|
102
|
+
&_sortable {
|
103
|
+
cursor: pointer;
|
102
104
|
|
103
|
-
|
104
|
-
|
105
|
+
&#{$block}__head-cell_align_right {
|
106
|
+
flex-direction: row-reverse;
|
107
|
+
}
|
105
108
|
}
|
106
109
|
}
|
107
110
|
|
108
|
-
|
109
|
-
|
111
|
+
// Separate head cell content class for correct text ellipsis overflow
|
112
|
+
&__head-cell-content {
|
113
|
+
overflow: hidden;
|
110
114
|
|
111
|
-
|
112
|
-
|
113
|
-
|
115
|
+
width: min-content;
|
116
|
+
|
117
|
+
white-space: nowrap;
|
118
|
+
text-overflow: ellipsis;
|
114
119
|
}
|
115
120
|
|
116
|
-
&
|
117
|
-
|
121
|
+
&__row-cell {
|
122
|
+
display: table-cell;
|
123
|
+
overflow-x: hidden;
|
118
124
|
|
125
|
+
width: 100%;
|
126
|
+
max-width: 100%;
|
119
127
|
padding: var(--virtual-table-cell-vertical-padding)
|
120
128
|
var(--virtual-table-cell-horizontal-padding);
|
121
129
|
|
130
|
+
vertical-align: middle;
|
122
131
|
white-space: nowrap;
|
123
132
|
text-overflow: ellipsis;
|
124
133
|
|
125
134
|
border-bottom: $cell-border;
|
126
|
-
}
|
127
|
-
|
128
|
-
&__td,
|
129
|
-
&__th {
|
130
|
-
height: 40px;
|
131
|
-
|
132
|
-
vertical-align: middle;
|
133
135
|
|
134
136
|
&_align {
|
135
137
|
&_left {
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import {useState, useReducer, useRef, useCallback, useEffect} from 'react';
|
2
2
|
|
3
|
+
import type {HandleTableColumnsResize} from '../../utils/hooks/useTableResize';
|
4
|
+
|
3
5
|
import type {IResponseError} from '../../types/api/error';
|
4
6
|
import {getArray} from '../../utils';
|
5
7
|
|
@@ -45,9 +47,12 @@ interface VirtualTableProps<T> {
|
|
45
47
|
rowHeight?: number;
|
46
48
|
parentContainer?: Element | null;
|
47
49
|
initialSortParams?: SortParams;
|
50
|
+
onColumnsResize?: HandleTableColumnsResize;
|
51
|
+
|
48
52
|
renderControls?: RenderControls;
|
49
53
|
renderEmptyDataMessage?: RenderEmptyDataMessage;
|
50
54
|
renderErrorMessage?: RenderErrorMessage;
|
55
|
+
|
51
56
|
dependencyArray?: unknown[]; // Fully reload table on params change
|
52
57
|
}
|
53
58
|
|
@@ -59,6 +64,7 @@ export const VirtualTable = <T,>({
|
|
59
64
|
rowHeight = DEFAULT_TABLE_ROW_HEIGHT,
|
60
65
|
parentContainer,
|
61
66
|
initialSortParams,
|
67
|
+
onColumnsResize,
|
62
68
|
renderControls,
|
63
69
|
renderEmptyDataMessage,
|
64
70
|
renderErrorMessage,
|
@@ -258,7 +264,11 @@ export const VirtualTable = <T,>({
|
|
258
264
|
const renderTable = () => {
|
259
265
|
return (
|
260
266
|
<table className={b('table')}>
|
261
|
-
<TableHead
|
267
|
+
<TableHead
|
268
|
+
columns={columns}
|
269
|
+
onSort={handleSort}
|
270
|
+
onColumnsResize={onColumnsResize}
|
271
|
+
/>
|
262
272
|
{renderData()}
|
263
273
|
</table>
|
264
274
|
);
|
@@ -13,6 +13,7 @@ import {
|
|
13
13
|
isSortableNodesProperty,
|
14
14
|
isUnavailableNode,
|
15
15
|
} from '../../utils/nodes';
|
16
|
+
import {updateColumnsWidth, useTableResize} from '../../utils/hooks/useTableResize';
|
16
17
|
|
17
18
|
import {Search} from '../../components/Search';
|
18
19
|
import {ProblemFilter} from '../../components/ProblemFilter';
|
@@ -50,6 +51,8 @@ export const VirtualNodes = ({path, parentContainer, additionalNodesProps}: Node
|
|
50
51
|
NodesUptimeFilterValues.All,
|
51
52
|
);
|
52
53
|
|
54
|
+
const [tableColumnsWidthSetup, setTableColumnsWidth] = useTableResize('nodesTableColumnsWidth');
|
55
|
+
|
53
56
|
const filters = useMemo(() => {
|
54
57
|
return [path, searchValue, problemFilter, uptimeFilter];
|
55
58
|
}, [path, searchValue, problemFilter, uptimeFilter]);
|
@@ -118,7 +121,7 @@ export const VirtualNodes = ({path, parentContainer, additionalNodesProps}: Node
|
|
118
121
|
getNodeRef: additionalNodesProps?.getNodeRef,
|
119
122
|
});
|
120
123
|
|
121
|
-
const columns = rawColumns.map((column) => {
|
124
|
+
const columns = updateColumnsWidth(rawColumns, tableColumnsWidthSetup).map((column) => {
|
122
125
|
return {...column, sortable: isSortableNodesProperty(column.name)};
|
123
126
|
});
|
124
127
|
|
@@ -133,6 +136,7 @@ export const VirtualNodes = ({path, parentContainer, additionalNodesProps}: Node
|
|
133
136
|
renderEmptyDataMessage={renderEmptyDataMessage}
|
134
137
|
dependencyArray={filters}
|
135
138
|
getRowClassName={getRowClassName}
|
139
|
+
onColumnsResize={setTableColumnsWidth}
|
136
140
|
/>
|
137
141
|
);
|
138
142
|
};
|
@@ -100,14 +100,14 @@ const prepareTableGeneralInfo = (PartitionConfig: TPartitionConfig, TTLSettings?
|
|
100
100
|
{label: 'Partitioning by load', value: partitioningByLoad},
|
101
101
|
{
|
102
102
|
label: 'Min number of partitions',
|
103
|
-
value: PartitioningPolicy.MinPartitionsCount || 0,
|
103
|
+
value: formatNumber(PartitioningPolicy.MinPartitionsCount || 0),
|
104
104
|
},
|
105
105
|
);
|
106
106
|
|
107
107
|
if (PartitioningPolicy.MaxPartitionsCount) {
|
108
108
|
generalTableInfo.push({
|
109
109
|
label: 'Max number of partitions',
|
110
|
-
value: PartitioningPolicy.MaxPartitionsCount
|
110
|
+
value: formatNumber(PartitioningPolicy.MaxPartitionsCount),
|
111
111
|
});
|
112
112
|
}
|
113
113
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import
|
2
|
-
import i18n from '
|
1
|
+
import type {ChartConfig} from '../TenantDashboard/TenantDashboard';
|
2
|
+
import i18n from '../i18n';
|
3
3
|
|
4
|
-
const defaultDashboardConfig: ChartConfig[] = [
|
4
|
+
export const defaultDashboardConfig: ChartConfig[] = [
|
5
5
|
{
|
6
6
|
title: i18n('charts.queries-per-second'),
|
7
7
|
metrics: [
|
@@ -44,7 +44,3 @@ const defaultDashboardConfig: ChartConfig[] = [
|
|
44
44
|
},
|
45
45
|
},
|
46
46
|
];
|
47
|
-
|
48
|
-
export const DefaultDashboard = () => {
|
49
|
-
return <TenantDashboard charts={defaultDashboardConfig} />;
|
50
|
-
};
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import type {AdditionalNodesProps} from '../../../../../types/additionalProps';
|
2
|
-
import {
|
2
|
+
import {TenantDashboard} from '../TenantDashboard/TenantDashboard';
|
3
|
+
import {cpuDashboardConfig} from './cpuDashboardConfig';
|
3
4
|
import {TopNodesByLoad} from './TopNodesByLoad';
|
4
5
|
import {TopNodesByCpu} from './TopNodesByCpu';
|
5
6
|
import {TopShards} from './TopShards';
|
@@ -13,7 +14,7 @@ interface TenantCpuProps {
|
|
13
14
|
export function TenantCpu({path, additionalNodesProps}: TenantCpuProps) {
|
14
15
|
return (
|
15
16
|
<>
|
16
|
-
<
|
17
|
+
<TenantDashboard charts={cpuDashboardConfig} />
|
17
18
|
<TopNodesByLoad path={path} additionalNodesProps={additionalNodesProps} />
|
18
19
|
<TopNodesByCpu path={path} additionalNodesProps={additionalNodesProps} />
|
19
20
|
<TopShards path={path} />
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import
|
1
|
+
import type {ChartConfig} from '../TenantDashboard/TenantDashboard';
|
2
2
|
import i18n from '../i18n';
|
3
3
|
|
4
|
-
const cpuDashboardConfig: ChartConfig[] = [
|
4
|
+
export const cpuDashboardConfig: ChartConfig[] = [
|
5
5
|
{
|
6
6
|
title: i18n('charts.cpu-usage'),
|
7
7
|
metrics: [
|
@@ -12,7 +12,3 @@ const cpuDashboardConfig: ChartConfig[] = [
|
|
12
12
|
],
|
13
13
|
},
|
14
14
|
];
|
15
|
-
|
16
|
-
export const CpuDashboard = () => {
|
17
|
-
return <TenantDashboard charts={cpuDashboardConfig} />;
|
18
|
-
};
|
package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantDashboard/TenantDashboard.tsx
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
+
import {useState} from 'react';
|
1
2
|
import {StringParam, useQueryParam} from 'use-query-params';
|
2
3
|
|
3
4
|
import {cn} from '../../../../../utils/cn';
|
4
5
|
import type {TimeFrame} from '../../../../../utils/timeframes';
|
5
|
-
import {
|
6
|
-
import {DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY} from '../../../../../utils/constants';
|
6
|
+
import {useTypedSelector} from '../../../../../utils/hooks';
|
7
7
|
import {TimeFrameSelector} from '../../../../../components/TimeFrameSelector/TimeFrameSelector';
|
8
8
|
import {
|
9
9
|
type ChartOptions,
|
10
10
|
MetricChart,
|
11
11
|
type MetricDescription,
|
12
|
+
type ChartDataStatus,
|
12
13
|
} from '../../../../../components/MetricChart';
|
13
14
|
|
14
15
|
import './TenantDashboard.scss';
|
@@ -29,15 +30,29 @@ interface TenantDashboardProps {
|
|
29
30
|
}
|
30
31
|
|
31
32
|
export const TenantDashboard = ({charts}: TenantDashboardProps) => {
|
33
|
+
const [isDashboardHidden, setIsDashboardHidden] = useState<boolean>(true);
|
34
|
+
|
32
35
|
const [timeFrame = '1h', setTimeframe] = useQueryParam('timeframe', StringParam);
|
33
36
|
|
34
37
|
const {autorefresh} = useTypedSelector((state) => state.schema);
|
35
38
|
|
36
|
-
|
39
|
+
// Refetch data only if dashboard successfully loaded
|
40
|
+
const shouldRefresh = autorefresh && !isDashboardHidden;
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
/**
|
43
|
+
* Charts should be hidden, if they are not enabled:
|
44
|
+
* 1. GraphShard is not enabled
|
45
|
+
* 2. ydb version does not have /viewer/json/render endpoint (400, 404, CORS error, etc.)
|
46
|
+
*
|
47
|
+
* If at least one chart successfully loaded, dashboard should be shown
|
48
|
+
* @link https://github.com/ydb-platform/ydb-embedded-ui/issues/659
|
49
|
+
* @todo disable only for specific errors ('GraphShard is not enabled') after ydb-stable-24 is generally used
|
50
|
+
*/
|
51
|
+
const handleChartDataStatusChange = (chartStatus: ChartDataStatus) => {
|
52
|
+
if (chartStatus === 'success') {
|
53
|
+
setIsDashboardHidden(false);
|
54
|
+
}
|
55
|
+
};
|
41
56
|
|
42
57
|
// If there is only one chart, display it with full width
|
43
58
|
const chartWidth = charts.length === 1 ? CHART_WIDTH_FULL : CHART_WIDTH;
|
@@ -45,23 +60,26 @@ export const TenantDashboard = ({charts}: TenantDashboardProps) => {
|
|
45
60
|
|
46
61
|
const renderContent = () => {
|
47
62
|
return charts.map((chartConfig) => {
|
63
|
+
const chartId = chartConfig.metrics.map(({target}) => target).join('&');
|
48
64
|
return (
|
49
65
|
<MetricChart
|
50
|
-
key={
|
66
|
+
key={chartId}
|
51
67
|
title={chartConfig.title}
|
52
68
|
metrics={chartConfig.metrics}
|
53
69
|
timeFrame={timeFrame as TimeFrame}
|
54
70
|
chartOptions={chartConfig.options}
|
55
|
-
autorefresh={
|
71
|
+
autorefresh={shouldRefresh}
|
56
72
|
width={chartWidth}
|
57
73
|
height={chartHeight}
|
74
|
+
onChartDataStatusChange={handleChartDataStatusChange}
|
75
|
+
isChartVisible={!isDashboardHidden}
|
58
76
|
/>
|
59
77
|
);
|
60
78
|
});
|
61
79
|
};
|
62
80
|
|
63
81
|
return (
|
64
|
-
<div className={b(null)}>
|
82
|
+
<div className={b(null)} style={{display: isDashboardHidden ? 'none' : undefined}}>
|
65
83
|
<div className={b('controls')}>
|
66
84
|
<TimeFrameSelector value={timeFrame as TimeFrame} onChange={setTimeframe} />
|
67
85
|
</div>
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import {TenantDashboard} from '../TenantDashboard/TenantDashboard';
|
2
|
+
import {memoryDashboardConfig} from './memoryDashboardConfig';
|
2
3
|
import {TopNodesByMemory} from './TopNodesByMemory';
|
3
4
|
|
4
5
|
interface TenantMemoryProps {
|
@@ -8,7 +9,7 @@ interface TenantMemoryProps {
|
|
8
9
|
export function TenantMemory({path}: TenantMemoryProps) {
|
9
10
|
return (
|
10
11
|
<>
|
11
|
-
<
|
12
|
+
<TenantDashboard charts={memoryDashboardConfig} />
|
12
13
|
<TopNodesByMemory path={path} />
|
13
14
|
</>
|
14
15
|
);
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import
|
1
|
+
import type {ChartConfig} from '../TenantDashboard/TenantDashboard';
|
2
2
|
import i18n from '../i18n';
|
3
3
|
|
4
|
-
const memoryDashboardConfig: ChartConfig[] = [
|
4
|
+
export const memoryDashboardConfig: ChartConfig[] = [
|
5
5
|
{
|
6
6
|
title: i18n('charts.memory-usage'),
|
7
7
|
metrics: [
|
@@ -15,7 +15,3 @@ const memoryDashboardConfig: ChartConfig[] = [
|
|
15
15
|
},
|
16
16
|
},
|
17
17
|
];
|
18
|
-
|
19
|
-
export const MemoryDashboard = () => {
|
20
|
-
return <TenantDashboard charts={memoryDashboardConfig} />;
|
21
|
-
};
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import cn from 'bem-cn-lite';
|
2
1
|
import {useCallback} from 'react';
|
3
2
|
import {useDispatch} from 'react-redux';
|
4
3
|
|
@@ -17,12 +16,11 @@ import {HealthcheckDetails} from './Healthcheck/HealthcheckDetails';
|
|
17
16
|
import {MetricsCards, type TenantMetrics} from './MetricsCards/MetricsCards';
|
18
17
|
import {TenantStorage} from './TenantStorage/TenantStorage';
|
19
18
|
import {TenantMemory} from './TenantMemory/TenantMemory';
|
20
|
-
import {
|
19
|
+
import {DefaultOverviewContent} from './DefaultOverviewContent/DefaultOverviewContent';
|
21
20
|
import {useHealthcheck} from './useHealthcheck';
|
22
21
|
|
23
22
|
import './TenantOverview.scss';
|
24
|
-
|
25
|
-
const b = cn('tenant-overview');
|
23
|
+
import {b} from './utils';
|
26
24
|
|
27
25
|
interface TenantOverviewProps {
|
28
26
|
tenantName: string;
|
@@ -141,7 +139,7 @@ export function TenantOverview({
|
|
141
139
|
);
|
142
140
|
}
|
143
141
|
default: {
|
144
|
-
return <
|
142
|
+
return <DefaultOverviewContent />;
|
145
143
|
}
|
146
144
|
}
|
147
145
|
};
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import type {ReactNode} from 'react';
|
2
|
-
import cn from 'bem-cn-lite';
|
3
2
|
|
4
3
|
import DataTable from '@gravity-ui/react-data-table';
|
5
4
|
import type {DataTableProps} from '@gravity-ui/react-data-table';
|
@@ -11,8 +10,7 @@ import {
|
|
11
10
|
import type {IResponseError} from '../../../../types/api/error';
|
12
11
|
import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton';
|
13
12
|
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
14
|
-
|
15
|
-
const b = cn('tenant-overview');
|
13
|
+
import {b} from './utils';
|
16
14
|
|
17
15
|
interface TenantOverviewTableLayoutProps<T> extends Omit<DataTableProps<T>, 'theme'> {
|
18
16
|
title: ReactNode;
|
@@ -1,17 +1,16 @@
|
|
1
|
-
import cn from 'bem-cn-lite';
|
2
|
-
|
3
1
|
import InfoViewer from '../../../../../components/InfoViewer/InfoViewer';
|
4
2
|
import {ProgressViewer} from '../../../../../components/ProgressViewer/ProgressViewer';
|
5
3
|
import {formatStorageValues} from '../../../../../utils/dataFormatters/dataFormatters';
|
6
4
|
import {getSizeWithSignificantDigits} from '../../../../../utils/bytesParsers';
|
7
5
|
|
6
|
+
import {TenantDashboard} from '../TenantDashboard/TenantDashboard';
|
7
|
+
|
8
8
|
import '../TenantOverview.scss';
|
9
9
|
|
10
|
-
import {
|
10
|
+
import {storageDashboardConfig} from './storageDashboardConfig';
|
11
11
|
import {TopTables} from './TopTables';
|
12
12
|
import {TopGroups} from './TopGroups';
|
13
|
-
|
14
|
-
const b = cn('tenant-overview');
|
13
|
+
import {b} from '../utils';
|
15
14
|
|
16
15
|
export interface TenantStorageMetrics {
|
17
16
|
blobStorageUsed?: number;
|
@@ -61,9 +60,10 @@ export function TenantStorage({tenantName, metrics}: TenantStorageProps) {
|
|
61
60
|
),
|
62
61
|
},
|
63
62
|
];
|
63
|
+
|
64
64
|
return (
|
65
65
|
<>
|
66
|
-
<
|
66
|
+
<TenantDashboard charts={storageDashboardConfig} />
|
67
67
|
<InfoViewer className={b('storage-info')} title="Storage details" info={info} />
|
68
68
|
<TopTables path={tenantName} />
|
69
69
|
<TopGroups tenant={tenantName} />
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import
|
1
|
+
import type {ChartConfig} from '../TenantDashboard/TenantDashboard';
|
2
2
|
import i18n from '../i18n';
|
3
3
|
|
4
|
-
const storageDashboardConfig: ChartConfig[] = [
|
4
|
+
export const storageDashboardConfig: ChartConfig[] = [
|
5
5
|
{
|
6
6
|
title: i18n('charts.storage-usage'),
|
7
7
|
metrics: [
|
@@ -15,7 +15,3 @@ const storageDashboardConfig: ChartConfig[] = [
|
|
15
15
|
},
|
16
16
|
},
|
17
17
|
];
|
18
|
-
|
19
|
-
export const StorageDashboard = () => {
|
20
|
-
return <TenantDashboard charts={storageDashboardConfig} />;
|
21
|
-
};
|
@@ -23,8 +23,5 @@
|
|
23
23
|
"settings.useVirtualTables.popover": "It will increase performance, but could work unstable",
|
24
24
|
|
25
25
|
"settings.queryUseMultiSchema.title": "Allow queries with multiple result sets",
|
26
|
-
"settings.queryUseMultiSchema.popover": "Use 'multi' schema for queries that enables queries with multiple result sets. Returns nothing on versions 23-3 and older"
|
27
|
-
|
28
|
-
"settings.displayChartsInDbDiagnostics.title": "Display charts in database diagnostics",
|
29
|
-
"settings.displayChartsInDbDiagnostics.popover": "Incorrect data may be displayed (shows data only for the database related to the current node), does not work well on static nodes"
|
26
|
+
"settings.queryUseMultiSchema.popover": "Use 'multi' schema for queries that enables queries with multiple result sets. Returns nothing on versions 23-3 and older"
|
30
27
|
}
|
@@ -23,8 +23,5 @@
|
|
23
23
|
"settings.useVirtualTables.popover": "Это улучшит производительность, но может работать нестабильно",
|
24
24
|
|
25
25
|
"settings.queryUseMultiSchema.title": "Разрешить запросы с несколькими результатами",
|
26
|
-
"settings.queryUseMultiSchema.popover": "Использовать для запросов схему 'multi', которая позволяет выполнять запросы с несколькими результатами. На версиях 23-3 и старше результат не возвращается вообще"
|
27
|
-
|
28
|
-
"settings.displayChartsInDbDiagnostics.title": "Показывать графики в диагностике базы данных",
|
29
|
-
"settings.displayChartsInDbDiagnostics.popover": "Могут отображаться неправильные данные (показывает данные только для базы, относящейся к текущей ноде), плохо работает на статических нодах"
|
26
|
+
"settings.queryUseMultiSchema.popover": "Использовать для запросов схему 'multi', которая позволяет выполнять запросы с несколькими результатами. На версиях 23-3 и старше результат не возвращается вообще"
|
30
27
|
}
|
@@ -10,7 +10,6 @@ import {
|
|
10
10
|
USE_BACKEND_PARAMS_FOR_TABLES_KEY,
|
11
11
|
USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
|
12
12
|
QUERY_USE_MULTI_SCHEMA_KEY,
|
13
|
-
DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY,
|
14
13
|
} from '../../utils/constants';
|
15
14
|
import {Lang, defaultLang} from '../../utils/i18n';
|
16
15
|
|
@@ -95,11 +94,6 @@ export const queryUseMultiSchemaSetting: SettingProps = {
|
|
95
94
|
title: i18n('settings.queryUseMultiSchema.title'),
|
96
95
|
helpPopoverContent: i18n('settings.queryUseMultiSchema.popover'),
|
97
96
|
};
|
98
|
-
export const displayChartsInDbDiagnosticsSetting: SettingProps = {
|
99
|
-
settingKey: DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY,
|
100
|
-
title: i18n('settings.displayChartsInDbDiagnostics.title'),
|
101
|
-
helpPopoverContent: i18n('settings.displayChartsInDbDiagnostics.popover'),
|
102
|
-
};
|
103
97
|
|
104
98
|
export const appearanceSection: SettingsSection = {
|
105
99
|
id: 'appearanceSection',
|
@@ -109,12 +103,7 @@ export const appearanceSection: SettingsSection = {
|
|
109
103
|
export const experimentsSection: SettingsSection = {
|
110
104
|
id: 'experimentsSection',
|
111
105
|
title: i18n('section.experiments'),
|
112
|
-
settings: [
|
113
|
-
useNodesEndpointSetting,
|
114
|
-
useVirtualTables,
|
115
|
-
queryUseMultiSchemaSetting,
|
116
|
-
displayChartsInDbDiagnosticsSetting,
|
117
|
-
],
|
106
|
+
settings: [useNodesEndpointSetting, useVirtualTables, queryUseMultiSchemaSetting],
|
118
107
|
};
|
119
108
|
|
120
109
|
export const generalPage: SettingsPage = {
|
@@ -3,7 +3,6 @@ import {TENANT_PAGES_IDS} from '../store/reducers/tenant/constants';
|
|
3
3
|
import {
|
4
4
|
ASIDE_HEADER_COMPACT_KEY,
|
5
5
|
CLUSTER_INFO_HIDDEN_KEY,
|
6
|
-
DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY,
|
7
6
|
INVERTED_DISKS_KEY,
|
8
7
|
LANGUAGE_KEY,
|
9
8
|
LAST_USED_QUERY_ACTION_KEY,
|
@@ -38,7 +37,6 @@ export const DEFAULT_USER_SETTINGS: SettingsObject = {
|
|
38
37
|
[PARTITIONS_HIDDEN_COLUMNS_KEY]: [],
|
39
38
|
[CLUSTER_INFO_HIDDEN_KEY]: true,
|
40
39
|
[USE_BACKEND_PARAMS_FOR_TABLES_KEY]: false,
|
41
|
-
[DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY]: false,
|
42
40
|
};
|
43
41
|
|
44
42
|
class SettingsManager {
|
package/dist/utils/constants.ts
CHANGED
@@ -127,5 +127,3 @@ export const USE_BACKEND_PARAMS_FOR_TABLES_KEY = 'useBackendParamsForTables';
|
|
127
127
|
|
128
128
|
// Enable schema that supports multiple resultsets
|
129
129
|
export const QUERY_USE_MULTI_SCHEMA_KEY = 'queryUseMultiSchema';
|
130
|
-
|
131
|
-
export const DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY = 'displayChartsInDbDiagnostics';
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import {useCallback, useState} from 'react';
|
2
|
+
import type {Column as DataTableColumn} from '@gravity-ui/react-data-table';
|
3
|
+
import type {Column as VirtualTableColumn} from '../../components/VirtualTable';
|
4
|
+
import {settingsManager} from '../../services/settings';
|
5
|
+
|
6
|
+
export type Column<T> = VirtualTableColumn<T> & DataTableColumn<T>;
|
7
|
+
|
8
|
+
export type TableColumnsWidthSetup = Record<string, number>;
|
9
|
+
|
10
|
+
export type HandleTableColumnsResize = (newSetup: TableColumnsWidthSetup) => void;
|
11
|
+
|
12
|
+
export const updateColumnsWidth = <T>(
|
13
|
+
columns: Column<T>[],
|
14
|
+
columnsWidthSetup: TableColumnsWidthSetup,
|
15
|
+
) => {
|
16
|
+
return columns.map((column) => {
|
17
|
+
if (!column.resizeable) {
|
18
|
+
return column;
|
19
|
+
}
|
20
|
+
return {...column, width: columnsWidthSetup[column.name] ?? column.width};
|
21
|
+
});
|
22
|
+
};
|
23
|
+
|
24
|
+
export const useTableResize = (
|
25
|
+
localStorageKey: string,
|
26
|
+
): [TableColumnsWidthSetup, HandleTableColumnsResize] => {
|
27
|
+
const [tableColumnsWidthSetup, setTableColumnsWidth] = useState<TableColumnsWidthSetup>(() => {
|
28
|
+
const setupFromLS = settingsManager.readUserSettingsValue(
|
29
|
+
localStorageKey,
|
30
|
+
{},
|
31
|
+
) as TableColumnsWidthSetup;
|
32
|
+
|
33
|
+
return setupFromLS;
|
34
|
+
});
|
35
|
+
|
36
|
+
const handleSetupChange: HandleTableColumnsResize = useCallback(
|
37
|
+
(newSetup) => {
|
38
|
+
setTableColumnsWidth((previousSetup) => {
|
39
|
+
// ResizeObserver callback may be triggered only for currently resized column
|
40
|
+
// or for the whole set of columns
|
41
|
+
const setup = {
|
42
|
+
...previousSetup,
|
43
|
+
...newSetup,
|
44
|
+
};
|
45
|
+
settingsManager.setUserSettingsValue(localStorageKey, setup);
|
46
|
+
return setup;
|
47
|
+
});
|
48
|
+
},
|
49
|
+
[localStorageKey],
|
50
|
+
);
|
51
|
+
|
52
|
+
return [tableColumnsWidthSetup, handleSetupChange];
|
53
|
+
};
|