ydb-embedded-ui 4.3.0 → 4.4.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.
- package/CHANGELOG.md +24 -0
- package/dist/containers/App/Content.js +2 -8
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +1 -1
- package/dist/containers/Cluster/Cluster.scss +4 -0
- package/dist/containers/Cluster/Cluster.tsx +14 -8
- package/dist/{components → containers}/ClusterInfo/ClusterInfo.scss +39 -0
- package/dist/containers/ClusterInfo/ClusterInfo.tsx +207 -0
- package/dist/containers/ClusterInfo/utils.ts +13 -0
- package/dist/containers/Header/Header.tsx +9 -16
- package/dist/containers/Nodes/Nodes.tsx +4 -6
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +2 -3
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.tsx +4 -4
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.scss +4 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsControls/PartitionsControls.tsx +20 -26
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/en.json +1 -1
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/ru.json +1 -1
- package/dist/containers/UserSettings/Setting.tsx +82 -0
- package/dist/containers/UserSettings/UserSettings.tsx +61 -102
- package/dist/containers/UserSettings/i18n/en.json +20 -0
- package/dist/containers/UserSettings/i18n/index.ts +11 -0
- package/dist/containers/UserSettings/i18n/ru.json +20 -0
- package/dist/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss +59 -0
- package/dist/containers/Versions/GroupedNodesTree/GroupedNodesTree.tsx +98 -0
- package/dist/containers/Versions/NodesTable/NodesTable.tsx +150 -0
- package/dist/containers/Versions/NodesTreeTitle/NodesTreeTitle.scss +55 -0
- package/dist/containers/Versions/NodesTreeTitle/NodesTreeTitle.tsx +62 -0
- package/dist/containers/Versions/Versions.scss +32 -0
- package/dist/containers/Versions/Versions.tsx +121 -0
- package/dist/containers/Versions/groupNodes.ts +124 -0
- package/dist/containers/Versions/types.ts +16 -0
- package/dist/routes.ts +0 -6
- package/dist/services/api.ts +3 -0
- package/dist/store/reducers/cluster/cluster.ts +4 -0
- package/dist/store/reducers/cluster/types.ts +3 -2
- package/dist/store/reducers/clusterNodes/clusterNodes.tsx +64 -0
- package/dist/store/reducers/clusterNodes/types.ts +22 -0
- package/dist/store/reducers/index.ts +2 -8
- package/dist/store/reducers/partitions/partitions.ts +2 -2
- package/dist/store/reducers/partitions/types.ts +1 -1
- package/dist/types/additionalProps.ts +5 -0
- package/dist/types/versions.ts +9 -0
- package/dist/utils/constants.ts +0 -11
- package/dist/utils/hooks/useSetting.ts +5 -3
- package/dist/utils/versions/getVersionsColors.ts +98 -0
- package/dist/utils/versions/index.ts +3 -0
- package/dist/utils/versions/parseNodesToVersionsValues.ts +28 -0
- package/dist/utils/versions/parseVersion.ts +23 -0
- package/package.json +1 -1
- package/dist/components/ClusterInfo/ClusterInfo.tsx +0 -239
- package/dist/components/FullGroupViewer/FullGroupViewer.js +0 -147
- package/dist/components/FullGroupViewer/FullGroupViewer.scss +0 -35
- package/dist/components/GroupTreeViewer/GroupTreeViewer.js +0 -87
- package/dist/components/GroupTreeViewer/GroupTreeViewer.scss +0 -16
- package/dist/components/GroupViewer/GroupViewer.js +0 -100
- package/dist/components/GroupViewer/GroupViewer.scss +0 -45
- package/dist/components/PDiskViewer/PDiskViewer.js +0 -79
- package/dist/components/PDiskViewer/PDiskViewer.scss +0 -46
- package/dist/components/TabletsViewer/TabletsViewer.js +0 -44
- package/dist/components/TabletsViewer/TabletsViewer.scss +0 -40
- package/dist/components/VerticalBars/VerticalBars.scss +0 -15
- package/dist/components/VerticalBars/VerticalBars.tsx +0 -38
- package/dist/components/VerticalBars/index.ts +0 -1
- package/dist/containers/Group/Group.js +0 -97
- package/dist/containers/Group/Group.scss +0 -6
- package/dist/containers/Header/Host/Host.js +0 -66
- package/dist/containers/Header/Host/Host.scss +0 -50
- package/dist/containers/Pdisk/Pdisk.js +0 -156
- package/dist/containers/Pdisk/Pdisk.scss +0 -42
- package/dist/containers/Pool/Pool.js +0 -170
- package/dist/containers/Pool/Pool.scss +0 -35
- package/dist/containers/Vdisk/Vdisk.js +0 -158
- package/dist/containers/Vdisk/Vdisk.scss +0 -42
- package/dist/containers/VdiskPdiskNode/VdiskPdiskNode.js +0 -526
- package/dist/containers/VdiskPdiskNode/VdiskPdiskNode.scss +0 -60
- package/dist/store/reducers/group.js +0 -49
- package/dist/store/reducers/pdisk.js +0 -51
- package/dist/store/reducers/pool.js +0 -42
- package/dist/store/reducers/vdisk.js +0 -49
package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsControls/PartitionsControls.tsx
CHANGED
@@ -2,7 +2,7 @@ import {useEffect, useMemo, useState} from 'react';
|
|
2
2
|
import {escapeRegExp} from 'lodash/fp';
|
3
3
|
|
4
4
|
import {TableColumnSetupItem} from '@gravity-ui/uikit/build/esm/components/Table/hoc/withTableSettings/withTableSettings';
|
5
|
-
import {Select, TableColumnSetup} from '@gravity-ui/uikit';
|
5
|
+
import {Select, SelectOption, TableColumnSetup} from '@gravity-ui/uikit';
|
6
6
|
|
7
7
|
import type {ValueOf} from '../../../../../types/common';
|
8
8
|
|
@@ -15,8 +15,8 @@ import {b} from '../Partitions';
|
|
15
15
|
|
16
16
|
interface PartitionsControlsProps {
|
17
17
|
consumers: string[] | undefined;
|
18
|
-
selectedConsumer: string
|
19
|
-
onSelectedConsumerChange: (consumer: string
|
18
|
+
selectedConsumer: string;
|
19
|
+
onSelectedConsumerChange: (consumer: string) => void;
|
20
20
|
selectDisabled: boolean;
|
21
21
|
partitions: PreparedPartitionDataWithHosts[] | undefined;
|
22
22
|
onSearchChange: (filteredPartitions: PreparedPartitionDataWithHosts[]) => void;
|
@@ -39,9 +39,6 @@ export const PartitionsControls = ({
|
|
39
39
|
const [generalSearchValue, setGeneralSearchValue] = useState('');
|
40
40
|
const [partitionIdSearchValue, setPartitionIdSearchValue] = useState('');
|
41
41
|
|
42
|
-
// Manual select control to enforce single-select behaviour for multiple select type
|
43
|
-
const [consumerSelectOpen, setConsumerSelectOpen] = useState(false);
|
44
|
-
|
45
42
|
useEffect(() => {
|
46
43
|
if (!partitions) {
|
47
44
|
return;
|
@@ -83,16 +80,17 @@ export const PartitionsControls = ({
|
|
83
80
|
onSearchChange(filteredPartitions);
|
84
81
|
}, [partitionIdSearchValue, generalSearchValue, partitions, onSearchChange]);
|
85
82
|
|
86
|
-
const consumersToSelect = useMemo(
|
87
|
-
|
88
|
-
consumers
|
83
|
+
const consumersToSelect = useMemo(() => {
|
84
|
+
const options =
|
85
|
+
consumers && consumers.length
|
89
86
|
? consumers.map((consumer) => ({
|
90
87
|
value: consumer,
|
91
88
|
content: consumer,
|
92
89
|
}))
|
93
|
-
:
|
94
|
-
|
95
|
-
|
90
|
+
: [];
|
91
|
+
|
92
|
+
return [{value: '', content: i18n('controls.consumerSelector.emptyOption')}, ...options];
|
93
|
+
}, [consumers]);
|
96
94
|
|
97
95
|
const columnsToSelect = useMemo(() => {
|
98
96
|
return initialColumnsIds.map((id) => {
|
@@ -106,10 +104,7 @@ export const PartitionsControls = ({
|
|
106
104
|
}, [initialColumnsIds, hiddenColumns]);
|
107
105
|
|
108
106
|
const handleConsumerSelectChange = (value: string[]) => {
|
109
|
-
|
110
|
-
// The second value is currently chosen consumer
|
111
|
-
onSelectedConsumerChange(value[1]);
|
112
|
-
setConsumerSelectOpen(false);
|
107
|
+
onSelectedConsumerChange(value[0]);
|
113
108
|
};
|
114
109
|
|
115
110
|
const handlePartitionIdSearchChange = (value: string) => {
|
@@ -137,25 +132,24 @@ export const PartitionsControls = ({
|
|
137
132
|
onHiddenColumnsChange(result);
|
138
133
|
};
|
139
134
|
|
135
|
+
const renderOption = (option: SelectOption) => {
|
136
|
+
return (
|
137
|
+
<div className={b('select-option', {empty: option.value === ''})}>{option.content}</div>
|
138
|
+
);
|
139
|
+
};
|
140
|
+
|
140
141
|
return (
|
141
142
|
<div className={b('controls')}>
|
142
143
|
<Select
|
143
144
|
className={b('consumer-select')}
|
144
|
-
placeholder={i18n('controls.consumerSelector.placeholder')}
|
145
145
|
label={i18n('controls.consumerSelector')}
|
146
146
|
options={consumersToSelect}
|
147
|
-
value={[selectedConsumer
|
147
|
+
value={[selectedConsumer]}
|
148
148
|
onUpdate={handleConsumerSelectChange}
|
149
149
|
filterable={consumers && consumers.length > 5}
|
150
150
|
disabled={selectDisabled || !consumers || !consumers.length}
|
151
|
-
|
152
|
-
|
153
|
-
// Although only one value could be selected
|
154
|
-
// Multiple type Select is used
|
155
|
-
// The reason - we need Select to be able to work with no value
|
156
|
-
// And it is easier to make multiple Select close on value change
|
157
|
-
// Than to enable single select to work with empty values
|
158
|
-
multiple
|
151
|
+
renderOption={renderOption}
|
152
|
+
renderSelectedOption={renderOption}
|
159
153
|
/>
|
160
154
|
<Search
|
161
155
|
onChange={handlePartitionIdSearchChange}
|
@@ -4,7 +4,7 @@
|
|
4
4
|
"headers.unread": "End offset - Last read offset",
|
5
5
|
"headers.uncommited": "End offset - Committed offset",
|
6
6
|
"controls.consumerSelector": "Consumer:",
|
7
|
-
"controls.consumerSelector.
|
7
|
+
"controls.consumerSelector.emptyOption": "No consumer",
|
8
8
|
"controls.partitionSearch": "Partition ID",
|
9
9
|
"controls.generalSearch": "Host, Host ID, Reader, Read Session ID",
|
10
10
|
"table.emptyDataMessage": "No partitions match the current search",
|
@@ -4,7 +4,7 @@
|
|
4
4
|
"headers.unread": "End offset - Last read offset",
|
5
5
|
"headers.uncommited": "End offset - Committed offset",
|
6
6
|
"controls.consumerSelector": "Читатель:",
|
7
|
-
"controls.consumerSelector.
|
7
|
+
"controls.consumerSelector.emptyOption": "Нет читателя",
|
8
8
|
"controls.partitionSearch": "Partition ID",
|
9
9
|
"controls.generalSearch": "Host, Host ID, Reader, Read Session ID",
|
10
10
|
"table.emptyDataMessage": "По заданному поиску нет партиций",
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import type {ReactNode} from 'react';
|
2
|
+
|
3
|
+
import {RadioButton, Switch} from '@gravity-ui/uikit';
|
4
|
+
import {Settings} from '@gravity-ui/navigation';
|
5
|
+
|
6
|
+
import {LabelWithPopover} from '../../components/LabelWithPopover/LabelWithPopover';
|
7
|
+
|
8
|
+
import {useSetting} from '../../utils/hooks';
|
9
|
+
|
10
|
+
import {b} from './UserSettings';
|
11
|
+
|
12
|
+
export type SettingsElementType = 'switch' | 'radio';
|
13
|
+
|
14
|
+
export interface SettingProps {
|
15
|
+
type?: SettingsElementType;
|
16
|
+
title: string;
|
17
|
+
settingKey: string;
|
18
|
+
helpPopoverContent?: ReactNode;
|
19
|
+
values?: {value: string; content: string}[];
|
20
|
+
defaultValue?: unknown;
|
21
|
+
}
|
22
|
+
|
23
|
+
export const Setting = ({
|
24
|
+
type = 'switch',
|
25
|
+
settingKey,
|
26
|
+
title,
|
27
|
+
helpPopoverContent,
|
28
|
+
values,
|
29
|
+
defaultValue,
|
30
|
+
}: SettingProps) => {
|
31
|
+
const [settingValue, setValue] = useSetting(settingKey, defaultValue);
|
32
|
+
|
33
|
+
const renderTitleComponent = (value: ReactNode) => {
|
34
|
+
if (helpPopoverContent) {
|
35
|
+
return (
|
36
|
+
<LabelWithPopover
|
37
|
+
className={b('item-with-popup')}
|
38
|
+
contentClassName={b('popup')}
|
39
|
+
text={value}
|
40
|
+
popoverContent={helpPopoverContent}
|
41
|
+
/>
|
42
|
+
);
|
43
|
+
}
|
44
|
+
|
45
|
+
return value;
|
46
|
+
};
|
47
|
+
|
48
|
+
const getSettingsElement = (elementType: SettingsElementType) => {
|
49
|
+
switch (elementType) {
|
50
|
+
case 'switch': {
|
51
|
+
return <Switch checked={Boolean(settingValue)} onUpdate={setValue} />;
|
52
|
+
}
|
53
|
+
|
54
|
+
case 'radio': {
|
55
|
+
if (!values) {
|
56
|
+
return null;
|
57
|
+
}
|
58
|
+
|
59
|
+
return (
|
60
|
+
<RadioButton value={String(settingValue)} onUpdate={setValue}>
|
61
|
+
{values.map(({value, content}) => {
|
62
|
+
return (
|
63
|
+
<RadioButton.Option value={value} key={value}>
|
64
|
+
{content}
|
65
|
+
</RadioButton.Option>
|
66
|
+
);
|
67
|
+
})}
|
68
|
+
</RadioButton>
|
69
|
+
);
|
70
|
+
}
|
71
|
+
|
72
|
+
default:
|
73
|
+
return null;
|
74
|
+
}
|
75
|
+
};
|
76
|
+
|
77
|
+
return (
|
78
|
+
<Settings.Item title={title} renderTitleComponent={renderTitleComponent}>
|
79
|
+
{getSettingsElement(type)}
|
80
|
+
</Settings.Item>
|
81
|
+
);
|
82
|
+
};
|
@@ -1,15 +1,10 @@
|
|
1
|
-
import {ReactNode} from 'react';
|
2
|
-
import {connect} from 'react-redux';
|
3
1
|
import cn from 'bem-cn-lite';
|
4
2
|
|
5
|
-
import {RadioButton, Switch} from '@gravity-ui/uikit';
|
6
3
|
import {Settings} from '@gravity-ui/navigation';
|
7
4
|
|
8
5
|
import favoriteFilledIcon from '../../assets/icons/star.svg';
|
9
6
|
import flaskIcon from '../../assets/icons/flask.svg';
|
10
7
|
|
11
|
-
import {LabelWithPopover} from '../../components/LabelWithPopover/LabelWithPopover';
|
12
|
-
|
13
8
|
import {
|
14
9
|
ENABLE_QUERY_MODES_FOR_EXPLAIN,
|
15
10
|
INVERTED_DISKS_KEY,
|
@@ -17,11 +12,12 @@ import {
|
|
17
12
|
USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
|
18
13
|
} from '../../utils/constants';
|
19
14
|
|
20
|
-
import {
|
15
|
+
import {Setting, SettingProps} from './Setting';
|
16
|
+
import i18n from './i18n';
|
21
17
|
|
22
18
|
import './UserSettings.scss';
|
23
19
|
|
24
|
-
const b = cn('ydb-user-settings');
|
20
|
+
export const b = cn('ydb-user-settings');
|
25
21
|
|
26
22
|
enum Theme {
|
27
23
|
light = 'light',
|
@@ -29,112 +25,75 @@ enum Theme {
|
|
29
25
|
system = 'system',
|
30
26
|
}
|
31
27
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
<LabelWithPopover
|
52
|
-
className={b('item-with-popup')}
|
53
|
-
contentClassName={b('popup')}
|
54
|
-
text={title}
|
55
|
-
popoverContent={
|
56
|
-
'Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It returns incorrect data on older versions'
|
57
|
-
}
|
58
|
-
/>
|
59
|
-
);
|
60
|
-
};
|
28
|
+
const themeValues = [
|
29
|
+
{
|
30
|
+
value: Theme.system,
|
31
|
+
content: i18n('settings.theme.option-system'),
|
32
|
+
},
|
33
|
+
{
|
34
|
+
value: Theme.light,
|
35
|
+
content: i18n('settings.theme.option-light'),
|
36
|
+
},
|
37
|
+
{
|
38
|
+
value: Theme.dark,
|
39
|
+
content: i18n('settings.theme.option-dark'),
|
40
|
+
},
|
41
|
+
];
|
42
|
+
|
43
|
+
export enum SettingsSection {
|
44
|
+
general = 'general',
|
45
|
+
experiments = 'experiments',
|
46
|
+
}
|
61
47
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
className={b('item-with-popup')}
|
66
|
-
contentClassName={b('popup')}
|
67
|
-
text={title}
|
68
|
-
popoverContent={
|
69
|
-
'Enable script | scan query mode selector for both run and explain. May not work on some versions'
|
70
|
-
}
|
71
|
-
/>
|
72
|
-
);
|
73
|
-
};
|
48
|
+
interface UserSettingsProps {
|
49
|
+
settings?: Partial<Record<SettingsSection, SettingProps[]>>;
|
50
|
+
}
|
74
51
|
|
52
|
+
export const UserSettings = ({settings}: UserSettingsProps) => {
|
75
53
|
return (
|
76
54
|
<Settings>
|
77
55
|
<Settings.Page
|
78
|
-
id=
|
79
|
-
title=
|
56
|
+
id={SettingsSection.general}
|
57
|
+
title={i18n('page.general')}
|
80
58
|
icon={{data: favoriteFilledIcon, height: 14, width: 14}}
|
81
59
|
>
|
82
|
-
<Settings.Section title=
|
83
|
-
<
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
60
|
+
<Settings.Section title={i18n('section.general')}>
|
61
|
+
<Setting
|
62
|
+
settingKey={THEME_KEY}
|
63
|
+
title={i18n('settings.theme.title')}
|
64
|
+
type="radio"
|
65
|
+
values={themeValues}
|
66
|
+
/>
|
67
|
+
{settings?.[SettingsSection.general]?.map((setting) => (
|
68
|
+
<Setting key={setting.settingKey} {...setting} />
|
69
|
+
))}
|
90
70
|
</Settings.Section>
|
91
71
|
</Settings.Page>
|
92
|
-
<Settings.Page
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
<
|
114
|
-
|
115
|
-
onUpdate={_onExplainQueryModesChangeHandler}
|
116
|
-
/>
|
117
|
-
</Settings.Item>
|
72
|
+
<Settings.Page
|
73
|
+
id={SettingsSection.experiments}
|
74
|
+
title={i18n('page.experiments')}
|
75
|
+
icon={{data: flaskIcon}}
|
76
|
+
>
|
77
|
+
<Settings.Section title={i18n('section.experiments')}>
|
78
|
+
<Setting
|
79
|
+
settingKey={INVERTED_DISKS_KEY}
|
80
|
+
title={i18n('settings.invertedDisks.title')}
|
81
|
+
/>
|
82
|
+
<Setting
|
83
|
+
settingKey={USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY}
|
84
|
+
title={i18n('settings.useNodesEndpoint.title')}
|
85
|
+
helpPopoverContent={i18n('settings.useNodesEndpoint.popover')}
|
86
|
+
/>
|
87
|
+
<Setting
|
88
|
+
settingKey={ENABLE_QUERY_MODES_FOR_EXPLAIN}
|
89
|
+
title={i18n('settings.enableQueryModesForExplain.title')}
|
90
|
+
helpPopoverContent={i18n('settings.enableQueryModesForExplain.popover')}
|
91
|
+
/>
|
92
|
+
{settings?.[SettingsSection.experiments]?.map((setting) => (
|
93
|
+
<Setting key={setting.settingKey} {...setting} />
|
94
|
+
))}
|
118
95
|
</Settings.Section>
|
119
96
|
</Settings.Page>
|
120
97
|
</Settings>
|
121
98
|
);
|
122
|
-
}
|
123
|
-
|
124
|
-
const mapStateToProps = (state: any) => {
|
125
|
-
const {theme, invertedDisks, useNodesEndpointInDiagnostics, enableQueryModesForExplain} =
|
126
|
-
state.settings.userSettings;
|
127
|
-
|
128
|
-
return {
|
129
|
-
theme,
|
130
|
-
invertedDisks: JSON.parse(invertedDisks),
|
131
|
-
useNodesEndpointInDiagnostics: JSON.parse(useNodesEndpointInDiagnostics),
|
132
|
-
enableQueryModesForExplain: JSON.parse(enableQueryModesForExplain),
|
133
|
-
};
|
134
99
|
};
|
135
|
-
|
136
|
-
const mapDispatchToProps = {
|
137
|
-
setSettingValue,
|
138
|
-
};
|
139
|
-
|
140
|
-
export default connect(mapStateToProps, mapDispatchToProps)(UserSettings);
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"page.general": "General",
|
3
|
+
"section.general": "General",
|
4
|
+
|
5
|
+
"page.experiments": "Experiments",
|
6
|
+
"section.experiments": "Experiments",
|
7
|
+
|
8
|
+
"settings.theme.title": "Interface theme",
|
9
|
+
"settings.theme.option-dark": "Dark",
|
10
|
+
"settings.theme.option-light": "Light",
|
11
|
+
"settings.theme.option-system": "System",
|
12
|
+
|
13
|
+
"settings.invertedDisks.title": "Inverted disks space indicators",
|
14
|
+
|
15
|
+
"settings.useNodesEndpoint.title": "Break the Nodes tab in Diagnostics",
|
16
|
+
"settings.useNodesEndpoint.popover": "Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It returns incorrect data on versions before 23-1",
|
17
|
+
|
18
|
+
"settings.enableQueryModesForExplain.title": "Enable query modes for explain",
|
19
|
+
"settings.enableQueryModesForExplain.popover": "Enable script | scan query mode selector for both run and explain. May not work on versions before 23-2"
|
20
|
+
}
|
@@ -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-user-settings';
|
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,20 @@
|
|
1
|
+
{
|
2
|
+
"page.general": "Общие",
|
3
|
+
"section.general": "Общие",
|
4
|
+
|
5
|
+
"page.experiments": "Эксперименты",
|
6
|
+
"section.experiments": "Эксперименты",
|
7
|
+
|
8
|
+
"settings.theme.title": "Тема",
|
9
|
+
"settings.theme.option-dark": "Тёмная",
|
10
|
+
"settings.theme.option-light": "Светлая",
|
11
|
+
"settings.theme.option-system": "Системная",
|
12
|
+
|
13
|
+
"settings.invertedDisks.title": "Инвертированные индикаторы места на дисках",
|
14
|
+
|
15
|
+
"settings.useNodesEndpoint.title": "Сломать вкладку Nodes в диагностике",
|
16
|
+
"settings.useNodesEndpoint.popover": "Использовать эндпоинт /viewer/json/nodes для вкладки Nodes в диагностике. Может возвращать некорректные данные на версиях до 23-1",
|
17
|
+
|
18
|
+
"settings.enableQueryModesForExplain.title": "Включить режимы выполнения запроса для explain",
|
19
|
+
"settings.enableQueryModesForExplain.popover": "Включить общий переключатель script | scan для run и explain. Может работать некорректно на версиях до 23-2"
|
20
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
@import '../../../styles/mixins.scss';
|
2
|
+
|
3
|
+
.ydb-versions-grouped-node-tree {
|
4
|
+
$item-width: 100%;
|
5
|
+
$margin-size: 24px;
|
6
|
+
|
7
|
+
&_first-level {
|
8
|
+
margin-top: 10px;
|
9
|
+
margin-bottom: 10px;
|
10
|
+
|
11
|
+
border: 1px solid var(--yc-color-line-generic);
|
12
|
+
border-radius: 10px;
|
13
|
+
}
|
14
|
+
|
15
|
+
&__dt-wrapper {
|
16
|
+
position: relative;
|
17
|
+
z-index: 0;
|
18
|
+
|
19
|
+
overflow-x: auto;
|
20
|
+
|
21
|
+
margin-right: $margin-size;
|
22
|
+
margin-left: $margin-size;
|
23
|
+
|
24
|
+
@include freeze-nth-column(1);
|
25
|
+
@include freeze-nth-column(2, 80px);
|
26
|
+
|
27
|
+
@include table-styles;
|
28
|
+
}
|
29
|
+
|
30
|
+
.ydb-tree-view {
|
31
|
+
font-size: var(--yc-text-body-2-font-size);
|
32
|
+
line-height: var(--yc-text-body-2-line-height);
|
33
|
+
|
34
|
+
// Apply margin ignoring first element of the tree
|
35
|
+
.ydb-tree-view {
|
36
|
+
margin-left: $margin-size;
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
& .tree-view_item {
|
41
|
+
height: 40px;
|
42
|
+
margin: 0;
|
43
|
+
|
44
|
+
// By default tree is rendered with padding calculated based on level
|
45
|
+
// We replace padding with margin for correct hover
|
46
|
+
padding: 0 10px !important;
|
47
|
+
|
48
|
+
border: 0;
|
49
|
+
border-radius: 10px;
|
50
|
+
}
|
51
|
+
|
52
|
+
& .tree-view_children .tree-view_item {
|
53
|
+
width: $item-width;
|
54
|
+
}
|
55
|
+
|
56
|
+
& .yc-progress__stack {
|
57
|
+
cursor: pointer;
|
58
|
+
}
|
59
|
+
}
|
@@ -0,0 +1,98 @@
|
|
1
|
+
import {useState, useEffect} from 'react';
|
2
|
+
import cn from 'bem-cn-lite';
|
3
|
+
|
4
|
+
import {TreeView} from 'ydb-ui-components';
|
5
|
+
|
6
|
+
import type {PreparedClusterNode} from '../../../store/reducers/clusterNodes/types';
|
7
|
+
import type {VersionValue} from '../../../types/versions';
|
8
|
+
|
9
|
+
import type {GroupedNodesItem} from '../types';
|
10
|
+
import {NodesTreeTitle} from '../NodesTreeTitle/NodesTreeTitle';
|
11
|
+
import {NodesTable} from '../NodesTable/NodesTable';
|
12
|
+
|
13
|
+
import './GroupedNodesTree.scss';
|
14
|
+
|
15
|
+
export const b = cn('ydb-versions-grouped-node-tree');
|
16
|
+
|
17
|
+
interface GroupedNodesTreeProps {
|
18
|
+
title?: string;
|
19
|
+
nodes?: PreparedClusterNode[];
|
20
|
+
items?: GroupedNodesItem[];
|
21
|
+
expanded?: boolean;
|
22
|
+
versionColor?: string;
|
23
|
+
versionsValues?: VersionValue[];
|
24
|
+
level?: number;
|
25
|
+
}
|
26
|
+
|
27
|
+
export const GroupedNodesTree = ({
|
28
|
+
title,
|
29
|
+
nodes,
|
30
|
+
items,
|
31
|
+
expanded = false,
|
32
|
+
versionColor,
|
33
|
+
versionsValues,
|
34
|
+
level = 0,
|
35
|
+
}: GroupedNodesTreeProps) => {
|
36
|
+
const [isOpened, toggleBlock] = useState(false);
|
37
|
+
|
38
|
+
useEffect(() => {
|
39
|
+
toggleBlock(expanded);
|
40
|
+
}, [expanded]);
|
41
|
+
|
42
|
+
const groupTitle = (
|
43
|
+
<NodesTreeTitle
|
44
|
+
title={title}
|
45
|
+
nodes={nodes}
|
46
|
+
items={items}
|
47
|
+
versionColor={versionColor}
|
48
|
+
versionsValues={versionsValues}
|
49
|
+
/>
|
50
|
+
);
|
51
|
+
|
52
|
+
const toggleCollapsed = () => {
|
53
|
+
toggleBlock((value) => !value);
|
54
|
+
};
|
55
|
+
|
56
|
+
if (items) {
|
57
|
+
return (
|
58
|
+
<div className={b({'first-level': level === 0})}>
|
59
|
+
<TreeView
|
60
|
+
key={title}
|
61
|
+
name={groupTitle}
|
62
|
+
collapsed={!isOpened}
|
63
|
+
hasArrow={true}
|
64
|
+
onClick={toggleCollapsed}
|
65
|
+
onArrowClick={toggleCollapsed}
|
66
|
+
>
|
67
|
+
{items.map((item, index) => (
|
68
|
+
<GroupedNodesTree
|
69
|
+
key={index}
|
70
|
+
title={item.title}
|
71
|
+
nodes={item.nodes}
|
72
|
+
expanded={expanded}
|
73
|
+
versionColor={item.versionColor}
|
74
|
+
level={level + 1}
|
75
|
+
/>
|
76
|
+
))}
|
77
|
+
</TreeView>
|
78
|
+
</div>
|
79
|
+
);
|
80
|
+
}
|
81
|
+
|
82
|
+
return (
|
83
|
+
<div className={b({'first-level': level === 0})}>
|
84
|
+
<TreeView
|
85
|
+
key={title}
|
86
|
+
name={groupTitle}
|
87
|
+
collapsed={!isOpened}
|
88
|
+
hasArrow
|
89
|
+
onClick={toggleCollapsed}
|
90
|
+
onArrowClick={toggleCollapsed}
|
91
|
+
>
|
92
|
+
<div className={b('dt-wrapper')}>
|
93
|
+
<NodesTable nodes={nodes || []} />
|
94
|
+
</div>
|
95
|
+
</TreeView>
|
96
|
+
</div>
|
97
|
+
);
|
98
|
+
};
|