ydb-embedded-ui 4.15.1 → 4.16.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 +21 -0
- package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +2 -2
- package/dist/components/DiagnosticCard/DiagnosticCard.scss +5 -0
- package/dist/components/DiagnosticCard/DiagnosticCard.tsx +17 -0
- package/dist/components/EntityStatus/EntityStatus.js +2 -2
- package/dist/components/EntityStatus/EntityStatus.scss +15 -0
- package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +2 -1
- package/dist/containers/Cluster/Cluster.tsx +2 -2
- package/dist/containers/Node/Node.tsx +3 -1
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +3 -1
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +2 -2
- package/dist/containers/Nodes/Nodes.tsx +3 -2
- package/dist/containers/Nodes/getNodesColumns.tsx +3 -1
- package/dist/containers/Storage/Storage.tsx +3 -2
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +5 -12
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -2
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +20 -11
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +14 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +26 -37
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +37 -25
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +155 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +7 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +7 -0
- package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +3 -2
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss +4 -1
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +42 -11
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +1 -1
- package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +11 -7
- package/dist/containers/Tenant/Query/i18n/en.json +3 -0
- package/dist/containers/Tenant/Query/i18n/ru.json +3 -0
- package/dist/containers/Tenant/Tenant.tsx +3 -2
- package/dist/containers/UserSettings/Setting.tsx +9 -2
- package/dist/containers/UserSettings/i18n/en.json +5 -1
- package/dist/containers/UserSettings/i18n/ru.json +5 -1
- package/dist/containers/UserSettings/settings.ts +25 -0
- package/dist/services/api.ts +16 -16
- package/dist/store/reducers/executeQuery.ts +33 -7
- package/dist/store/reducers/explainQuery.ts +12 -4
- package/dist/store/reducers/healthcheckInfo.ts +27 -11
- package/dist/store/reducers/settings/settings.ts +4 -10
- package/dist/store/reducers/tenant/tenant.ts +19 -1
- package/dist/store/reducers/tenant/types.ts +3 -1
- package/dist/store/reducers/tenants/selectors.ts +1 -1
- package/dist/store/reducers/tenants/utils.ts +2 -2
- package/dist/types/additionalProps.ts +8 -1
- package/dist/types/api/tenant.ts +19 -20
- package/dist/types/store/executeQuery.ts +11 -1
- package/dist/types/store/query.ts +2 -1
- package/dist/utils/autofetcher.ts +7 -7
- package/dist/utils/constants.ts +2 -1
- package/dist/utils/hooks/i18n/en.json +1 -1
- package/dist/utils/hooks/i18n/ru.json +1 -1
- package/dist/utils/hooks/useQueryModes.ts +4 -2
- package/dist/utils/i18n/i18n.ts +10 -4
- package/dist/utils/nodes.ts +0 -6
- package/dist/utils/query.ts +14 -0
- package/dist/utils/settings.ts +10 -0
- package/package.json +1 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/PreviewItem.tsx +0 -33
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +0 -213
@@ -1,13 +1,14 @@
|
|
1
1
|
import cn from 'bem-cn-lite';
|
2
2
|
|
3
|
-
import {Button, Icon} from '@gravity-ui/uikit';
|
3
|
+
import {Button, Icon, Link} from '@gravity-ui/uikit';
|
4
4
|
|
5
5
|
import updateArrow from '../../../../../assets/icons/update-arrow.svg';
|
6
6
|
|
7
|
-
import {SelfCheckResult} from '../../../../../types/api/healthcheck';
|
8
|
-
import type {
|
9
|
-
|
10
|
-
import
|
7
|
+
import {SelfCheckResult, type StatusFlag} from '../../../../../types/api/healthcheck';
|
8
|
+
import type {IResponseError} from '../../../../../types/api/error';
|
9
|
+
import {DiagnosticCard} from '../../../../../components/DiagnosticCard/DiagnosticCard';
|
10
|
+
import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
|
11
|
+
import {ResponseError} from '../../../../../components/Errors/ResponseError';
|
11
12
|
|
12
13
|
import i18n from '../i18n';
|
13
14
|
|
@@ -15,21 +16,18 @@ const b = cn('healthcheck');
|
|
15
16
|
|
16
17
|
interface PreviewProps {
|
17
18
|
selfCheckResult: SelfCheckResult;
|
18
|
-
|
19
|
+
issuesStatistics?: [StatusFlag, number][];
|
19
20
|
loading?: boolean;
|
20
|
-
onShowMore?:
|
21
|
+
onShowMore?: VoidFunction;
|
21
22
|
onUpdate: VoidFunction;
|
23
|
+
error?: IResponseError;
|
22
24
|
}
|
23
25
|
|
24
26
|
export const Preview = (props: PreviewProps) => {
|
25
|
-
const {selfCheckResult,
|
27
|
+
const {selfCheckResult, issuesStatistics, loading, onShowMore, onUpdate, error} = props;
|
26
28
|
|
27
29
|
const isStatusOK = selfCheckResult === SelfCheckResult.GOOD;
|
28
30
|
|
29
|
-
if (!issuesTrees) {
|
30
|
-
return null;
|
31
|
-
}
|
32
|
-
|
33
31
|
const renderStatus = () => {
|
34
32
|
const modifier = selfCheckResult.toLowerCase();
|
35
33
|
|
@@ -46,26 +44,40 @@ export const Preview = (props: PreviewProps) => {
|
|
46
44
|
);
|
47
45
|
};
|
48
46
|
|
49
|
-
const
|
47
|
+
const renderContent = () => {
|
48
|
+
if (error) {
|
49
|
+
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
|
50
|
+
}
|
51
|
+
|
50
52
|
return (
|
51
53
|
<div className={b('preview-content')}>
|
52
|
-
{isStatusOK
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
{isStatusOK || !issuesStatistics || !issuesStatistics.length ? (
|
55
|
+
i18n('status_message.ok')
|
56
|
+
) : (
|
57
|
+
<>
|
58
|
+
<div>Issues:</div>
|
59
|
+
<div className={b('issues-statistics')}>
|
60
|
+
{issuesStatistics.map(([status, count]) => (
|
61
|
+
<EntityStatus
|
62
|
+
key={status}
|
63
|
+
mode="icons"
|
64
|
+
status={status}
|
65
|
+
label={count.toString()}
|
66
|
+
size="l"
|
67
|
+
/>
|
68
|
+
))}
|
69
|
+
</div>
|
70
|
+
<Link onClick={() => onShowMore?.()}>{i18n('label.show-details')}</Link>
|
71
|
+
</>
|
72
|
+
)}
|
61
73
|
</div>
|
62
74
|
);
|
63
75
|
};
|
64
76
|
|
65
77
|
return (
|
66
|
-
<
|
78
|
+
<DiagnosticCard className={b('preview')}>
|
67
79
|
{renderStatus()}
|
68
|
-
{
|
69
|
-
</
|
80
|
+
{renderContent()}
|
81
|
+
</DiagnosticCard>
|
70
82
|
);
|
71
83
|
};
|
@@ -0,0 +1,155 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
import {useCallback} from 'react';
|
3
|
+
import {useDispatch} from 'react-redux';
|
4
|
+
|
5
|
+
import {Loader} from '@gravity-ui/uikit';
|
6
|
+
|
7
|
+
import {InfoViewer} from '../../../../components/InfoViewer';
|
8
|
+
import {PoolUsage} from '../../../../components/PoolUsage/PoolUsage';
|
9
|
+
import {Tablet} from '../../../../components/Tablet';
|
10
|
+
import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
|
11
|
+
import {formatCPU} from '../../../../utils';
|
12
|
+
import {TABLET_STATES, TENANT_DEFAULT_TITLE} from '../../../../utils/constants';
|
13
|
+
import {bytesToGB} from '../../../../utils/utils';
|
14
|
+
import {mapDatabaseTypeToDBName} from '../../utils/schema';
|
15
|
+
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
16
|
+
import type {ETabletVolatileState} from '../../../../types/api/tenant';
|
17
|
+
import type {AdditionalTenantsProps} from '../../../../types/additionalProps';
|
18
|
+
import {getTenantInfo, setDataWasNotLoaded} from '../../../../store/reducers/tenant/tenant';
|
19
|
+
|
20
|
+
import i18n from './i18n';
|
21
|
+
import './TenantOverview.scss';
|
22
|
+
|
23
|
+
const b = cn('tenant-overview');
|
24
|
+
|
25
|
+
interface TenantOverviewProps {
|
26
|
+
tenantName: string;
|
27
|
+
additionalTenantInfo?: AdditionalTenantsProps;
|
28
|
+
}
|
29
|
+
|
30
|
+
export function TenantOverview({tenantName, additionalTenantInfo}: TenantOverviewProps) {
|
31
|
+
const {tenant, loading, wasLoaded} = useTypedSelector((state) => state.tenant);
|
32
|
+
const {autorefresh} = useTypedSelector((state) => state.schema);
|
33
|
+
const dispatch = useDispatch();
|
34
|
+
const fetchTenant = useCallback(
|
35
|
+
(isBackground = true) => {
|
36
|
+
if (!isBackground) {
|
37
|
+
dispatch(setDataWasNotLoaded());
|
38
|
+
}
|
39
|
+
dispatch(getTenantInfo({path: tenantName}));
|
40
|
+
},
|
41
|
+
[dispatch, tenantName],
|
42
|
+
);
|
43
|
+
|
44
|
+
useAutofetcher(fetchTenant, [fetchTenant], autorefresh);
|
45
|
+
|
46
|
+
const {
|
47
|
+
Metrics = {},
|
48
|
+
PoolStats,
|
49
|
+
StateStats = [],
|
50
|
+
MemoryUsed,
|
51
|
+
Name,
|
52
|
+
State,
|
53
|
+
CoresUsed,
|
54
|
+
StorageGroups,
|
55
|
+
StorageAllocatedSize,
|
56
|
+
Type,
|
57
|
+
SystemTablets,
|
58
|
+
} = tenant || {};
|
59
|
+
|
60
|
+
const tenantType = mapDatabaseTypeToDBName(Type);
|
61
|
+
const memoryRaw = MemoryUsed ?? Metrics.Memory;
|
62
|
+
|
63
|
+
const memory = (memoryRaw && bytesToGB(memoryRaw)) || i18n('no-data');
|
64
|
+
const storage = (Metrics.Storage && bytesToGB(Metrics.Storage)) || i18n('no-data');
|
65
|
+
const storageGroups = StorageGroups ?? i18n('no-data');
|
66
|
+
const blobStorage =
|
67
|
+
(StorageAllocatedSize && bytesToGB(StorageAllocatedSize)) || i18n('no-data');
|
68
|
+
const storageEfficiency =
|
69
|
+
Metrics.Storage && StorageAllocatedSize
|
70
|
+
? `${((parseInt(Metrics.Storage) * 100) / parseInt(StorageAllocatedSize)).toFixed(2)}%`
|
71
|
+
: i18n('no-data');
|
72
|
+
|
73
|
+
const cpuRaw = CoresUsed !== undefined ? Number(CoresUsed) * 1_000_000 : Metrics.CPU;
|
74
|
+
|
75
|
+
const cpu = formatCPU(cpuRaw);
|
76
|
+
|
77
|
+
const metricsInfo = [
|
78
|
+
{label: 'Type', value: Type},
|
79
|
+
{label: 'Memory', value: memory},
|
80
|
+
{label: 'CPU', value: cpu},
|
81
|
+
{label: 'Tablet storage', value: storage},
|
82
|
+
{label: 'Storage groups', value: storageGroups},
|
83
|
+
{label: 'Blob storage', value: blobStorage},
|
84
|
+
{label: 'Storage efficiency', value: storageEfficiency},
|
85
|
+
];
|
86
|
+
|
87
|
+
const tabletsInfo = StateStats.filter(
|
88
|
+
(item): item is {VolatileState: ETabletVolatileState; Count: number} => {
|
89
|
+
return item.VolatileState !== undefined && item.Count !== undefined;
|
90
|
+
},
|
91
|
+
).map((info) => {
|
92
|
+
return {label: TABLET_STATES[info.VolatileState], value: info.Count};
|
93
|
+
});
|
94
|
+
|
95
|
+
const renderName = () => {
|
96
|
+
return (
|
97
|
+
<div className={b('tenant-name-wrapper')}>
|
98
|
+
<EntityStatus
|
99
|
+
status={State}
|
100
|
+
name={Name || TENANT_DEFAULT_TITLE}
|
101
|
+
withLeftTrim
|
102
|
+
hasClipboardButton={Boolean(tenant)}
|
103
|
+
clipboardButtonAlwaysVisible
|
104
|
+
/>
|
105
|
+
</div>
|
106
|
+
);
|
107
|
+
};
|
108
|
+
|
109
|
+
if (loading && !wasLoaded) {
|
110
|
+
return (
|
111
|
+
<div className={b('loader')}>
|
112
|
+
<Loader size="m" />
|
113
|
+
</div>
|
114
|
+
);
|
115
|
+
}
|
116
|
+
|
117
|
+
return (
|
118
|
+
<div className={b()}>
|
119
|
+
<div className={b('top-label')}>{tenantType}</div>
|
120
|
+
<div className={b('top')}>
|
121
|
+
{renderName()}
|
122
|
+
{additionalTenantInfo?.getMonitoringLink?.(Name, Type)}
|
123
|
+
</div>
|
124
|
+
<div className={b('system-tablets')}>
|
125
|
+
{SystemTablets &&
|
126
|
+
SystemTablets.map((tablet, tabletIndex) => (
|
127
|
+
<Tablet key={tabletIndex} tablet={tablet} tenantName={Name} />
|
128
|
+
))}
|
129
|
+
</div>
|
130
|
+
<div className={b('common-info')}>
|
131
|
+
<div>
|
132
|
+
<div className={b('section-title')}>{i18n('title.pools')}</div>
|
133
|
+
{PoolStats ? (
|
134
|
+
<div className={b('section', {pools: true})}>
|
135
|
+
{PoolStats.map((pool, poolIndex) => (
|
136
|
+
<PoolUsage key={poolIndex} data={pool} />
|
137
|
+
))}
|
138
|
+
</div>
|
139
|
+
) : (
|
140
|
+
<div className="error">{i18n('no-pools-data')}</div>
|
141
|
+
)}
|
142
|
+
</div>
|
143
|
+
<InfoViewer
|
144
|
+
title={i18n('title.metrics')}
|
145
|
+
className={b('section', {metrics: true})}
|
146
|
+
info={metricsInfo}
|
147
|
+
/>
|
148
|
+
|
149
|
+
<div className={b('section')}>
|
150
|
+
<InfoViewer info={tabletsInfo} title="Tablets" />
|
151
|
+
</div>
|
152
|
+
</div>
|
153
|
+
</div>
|
154
|
+
);
|
155
|
+
}
|
@@ -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-tenant-overview';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -4,6 +4,7 @@ import cn from 'bem-cn-lite';
|
|
4
4
|
import {useThemeValue} from '@gravity-ui/uikit';
|
5
5
|
|
6
6
|
import type {EPathType} from '../../../types/api/schema';
|
7
|
+
import type {AdditionalTenantsProps, AdditionalNodesProps} from '../../../types/additionalProps';
|
7
8
|
import {TENANT_PAGES_IDS} from '../../../store/reducers/tenant/constants';
|
8
9
|
import {useSetting} from '../../../utils/hooks';
|
9
10
|
import {TENANT_INITIAL_PAGE_KEY} from '../../../utils/constants';
|
@@ -18,8 +19,8 @@ const b = cn('object-general');
|
|
18
19
|
|
19
20
|
interface ObjectGeneralProps {
|
20
21
|
type?: EPathType;
|
21
|
-
additionalTenantInfo?:
|
22
|
-
additionalNodesInfo?:
|
22
|
+
additionalTenantInfo?: AdditionalTenantsProps;
|
23
|
+
additionalNodesInfo?: AdditionalNodesProps;
|
23
24
|
}
|
24
25
|
|
25
26
|
function ObjectGeneral(props: ObjectGeneralProps) {
|
@@ -3,10 +3,13 @@ import block from 'bem-cn-lite';
|
|
3
3
|
|
4
4
|
import DataTable, {Column} from '@gravity-ui/react-data-table';
|
5
5
|
|
6
|
+
import type {QueryInHistory} from '../../../../types/store/executeQuery';
|
6
7
|
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
|
7
8
|
import {setQueryTab} from '../../../../store/reducers/tenant/tenant';
|
9
|
+
import {selectQueriesHistory} from '../../../../store/reducers/executeQuery';
|
8
10
|
import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants';
|
9
|
-
import {useTypedSelector} from '../../../../utils/hooks';
|
11
|
+
import {useQueryModes, useTypedSelector} from '../../../../utils/hooks';
|
12
|
+
import {QUERY_MODES, QUERY_MODES_TITLES, QUERY_SYNTAX} from '../../../../utils/query';
|
10
13
|
import {MAX_QUERY_HEIGHT, QUERY_TABLE_SETTINGS} from '../../utils/constants';
|
11
14
|
|
12
15
|
import i18n from '../i18n';
|
@@ -22,24 +25,51 @@ interface QueriesHistoryProps {
|
|
22
25
|
function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
|
23
26
|
const dispatch = useDispatch();
|
24
27
|
|
25
|
-
const
|
28
|
+
const [queryMode, setQueryMode] = useQueryModes();
|
29
|
+
|
30
|
+
const queriesHistory = useTypedSelector(selectQueriesHistory);
|
26
31
|
const reversedHistory = [...queriesHistory].reverse();
|
27
32
|
|
28
|
-
const onQueryClick = (
|
29
|
-
|
30
|
-
|
33
|
+
const onQueryClick = (query: QueryInHistory) => {
|
34
|
+
let isQueryModeSet = true;
|
35
|
+
|
36
|
+
if (query.syntax === QUERY_SYNTAX.pg && queryMode !== QUERY_MODES.pg) {
|
37
|
+
isQueryModeSet = setQueryMode(
|
38
|
+
QUERY_MODES.pg,
|
39
|
+
i18n('history.cannot-set-mode', {mode: QUERY_MODES_TITLES[QUERY_MODES.pg]}),
|
40
|
+
);
|
41
|
+
} else if (query.syntax !== QUERY_SYNTAX.pg && queryMode === QUERY_MODES.pg) {
|
42
|
+
// Set query mode for queries with yql syntax
|
43
|
+
isQueryModeSet = setQueryMode(QUERY_MODES.script);
|
44
|
+
}
|
45
|
+
|
46
|
+
if (isQueryModeSet) {
|
47
|
+
changeUserInput({input: query.queryText});
|
48
|
+
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
|
49
|
+
}
|
31
50
|
};
|
32
51
|
|
33
|
-
const columns: Column<
|
52
|
+
const columns: Column<QueryInHistory>[] = [
|
34
53
|
{
|
35
54
|
name: 'queryText',
|
36
55
|
header: 'Query Text',
|
37
|
-
render: ({row
|
38
|
-
|
39
|
-
<
|
40
|
-
|
41
|
-
|
56
|
+
render: ({row}) => {
|
57
|
+
return (
|
58
|
+
<div className={b('query')}>
|
59
|
+
<TruncatedQuery value={row.queryText} maxQueryHeight={MAX_QUERY_HEIGHT} />
|
60
|
+
</div>
|
61
|
+
);
|
62
|
+
},
|
63
|
+
sortable: false,
|
64
|
+
},
|
65
|
+
{
|
66
|
+
name: 'syntax',
|
67
|
+
header: 'Syntax',
|
68
|
+
render: ({row}) => {
|
69
|
+
return row.syntax === QUERY_SYNTAX.pg ? 'PostgreSQL' : 'YQL';
|
70
|
+
},
|
42
71
|
sortable: false,
|
72
|
+
width: 200,
|
43
73
|
},
|
44
74
|
];
|
45
75
|
|
@@ -52,6 +82,7 @@ function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
|
|
52
82
|
settings={QUERY_TABLE_SETTINGS}
|
53
83
|
emptyDataMessage={i18n('history.empty')}
|
54
84
|
onRowClick={(row) => onQueryClick(row)}
|
85
|
+
rowClassName={() => b('table-row')}
|
55
86
|
/>
|
56
87
|
</div>
|
57
88
|
);
|
@@ -264,7 +264,7 @@ function QueryEditor(props) {
|
|
264
264
|
|
265
265
|
const {queries, currentIndex} = history;
|
266
266
|
if (input !== queries[currentIndex]) {
|
267
|
-
saveQueryToHistory(input);
|
267
|
+
saveQueryToHistory(input, mode);
|
268
268
|
}
|
269
269
|
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
|
270
270
|
};
|
@@ -4,7 +4,7 @@ import {Button, ButtonView, DropdownMenu} from '@gravity-ui/uikit';
|
|
4
4
|
import {useMemo} from 'react';
|
5
5
|
|
6
6
|
import type {QueryAction, QueryMode} from '../../../../types/store/query';
|
7
|
-
import {QUERY_MODES} from '../../../../utils/query';
|
7
|
+
import {QUERY_MODES, QUERY_MODES_TITLES} from '../../../../utils/query';
|
8
8
|
import {Icon} from '../../../../components/Icon';
|
9
9
|
import {LabelWithPopover} from '../../../../components/LabelWithPopover';
|
10
10
|
|
@@ -21,32 +21,36 @@ const b = block('ydb-query-editor-controls');
|
|
21
21
|
|
22
22
|
const OldQueryModeSelectorOptions = {
|
23
23
|
[QUERY_MODES.script]: {
|
24
|
-
title:
|
24
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.script],
|
25
25
|
description: i18n('method-description.script'),
|
26
26
|
},
|
27
27
|
[QUERY_MODES.scan]: {
|
28
|
-
title:
|
28
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.scan],
|
29
29
|
description: i18n('method-description.scan'),
|
30
30
|
},
|
31
31
|
} as const;
|
32
32
|
|
33
33
|
const QueryModeSelectorOptions = {
|
34
34
|
[QUERY_MODES.script]: {
|
35
|
-
title:
|
35
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.script],
|
36
36
|
description: i18n('method-description.script'),
|
37
37
|
},
|
38
38
|
[QUERY_MODES.scan]: {
|
39
|
-
title:
|
39
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.scan],
|
40
40
|
description: i18n('method-description.scan'),
|
41
41
|
},
|
42
42
|
[QUERY_MODES.data]: {
|
43
|
-
title:
|
43
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.data],
|
44
44
|
description: i18n('method-description.data'),
|
45
45
|
},
|
46
46
|
[QUERY_MODES.query]: {
|
47
|
-
title:
|
47
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.query],
|
48
48
|
description: i18n('method-description.query'),
|
49
49
|
},
|
50
|
+
[QUERY_MODES.pg]: {
|
51
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.pg],
|
52
|
+
description: i18n('method-description.pg'),
|
53
|
+
},
|
50
54
|
} as const;
|
51
55
|
|
52
56
|
interface QueryEditorControlsProps {
|
@@ -8,6 +8,8 @@
|
|
8
8
|
"history.empty": "History is empty",
|
9
9
|
"saved.empty": "There are no saved queries",
|
10
10
|
|
11
|
+
"history.cannot-set-mode": "This query is available only with '{{mode}}' query mode. You need to turn in additional query modes in settings to enable it",
|
12
|
+
|
11
13
|
"delete-dialog.header": "Delete query",
|
12
14
|
"delete-dialog.question": "Are you sure you want to delete query",
|
13
15
|
"delete-dialog.delete": "Delete",
|
@@ -21,6 +23,7 @@
|
|
21
23
|
"method-description.scan": "Read-only queries, potentially reading a lot of data.\nAPI call: table.ExecuteScan",
|
22
24
|
"method-description.data": "DML queries for changing and fetching data in serialization mode.\nAPI call: table.executeDataQuery",
|
23
25
|
"method-description.query": "Any query. An experimental API call supposed to replace all existing methods.\nAPI Call: query.ExecuteScript",
|
26
|
+
"method-description.pg": "Queries in postgresql syntax.\nAPI call: query.ExecuteScript",
|
24
27
|
|
25
28
|
"query-duration.description": "Duration of server-side query execution"
|
26
29
|
}
|
@@ -8,6 +8,8 @@
|
|
8
8
|
"history.empty": "История пуста",
|
9
9
|
"saved.empty": "Нет сохраненных запросов",
|
10
10
|
|
11
|
+
"history.cannot-set-mode": "Этот запрос доступен только в режиме '{{mode}}'. Вам необходимо включить дополнительные режимы выполнения запросов в настройках",
|
12
|
+
|
11
13
|
"delete-dialog.header": "Удалить запрос",
|
12
14
|
"delete-dialog.question": "Вы уверены что хотите удалить запрос",
|
13
15
|
"delete-dialog.delete": "Удалить",
|
@@ -21,6 +23,7 @@
|
|
21
23
|
"method-description.scan": "Только читающие запросы, потенциально читающие много данных.\nAPI call: table.ExecuteScan",
|
22
24
|
"method-description.data": "DML-запросы для изменения и выборки данных в режиме изоляции Serializable.\nAPI call: table.executeDataQuery",
|
23
25
|
"method-description.query": "Любые запросы. Экспериментальный перспективный метод, который в будущем заменит все остальные.\nAPI call: query.ExecuteScript",
|
26
|
+
"method-description.pg": "Запросы в синтаксисе postgresql.\nAPI call: query.ExecuteScript",
|
24
27
|
|
25
28
|
"query-duration.description": "Время выполнения запроса на стороне сервера"
|
26
29
|
}
|
@@ -5,6 +5,7 @@ import {useLocation} from 'react-router';
|
|
5
5
|
import qs from 'qs';
|
6
6
|
|
7
7
|
import type {TEvDescribeSchemeResult} from '../../types/api/schema';
|
8
|
+
import type {AdditionalTenantsProps, AdditionalNodesProps} from '../../types/additionalProps';
|
8
9
|
|
9
10
|
import {DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY} from '../../utils/constants';
|
10
11
|
import {useTypedSelector} from '../../utils/hooks';
|
@@ -37,8 +38,8 @@ const initialTenantSummaryState = {
|
|
37
38
|
};
|
38
39
|
|
39
40
|
interface TenantProps {
|
40
|
-
additionalTenantInfo?:
|
41
|
-
additionalNodesInfo?:
|
41
|
+
additionalTenantInfo?: AdditionalTenantsProps;
|
42
|
+
additionalNodesInfo?: AdditionalNodesProps;
|
42
43
|
}
|
43
44
|
|
44
45
|
function Tenant(props: TenantProps) {
|
@@ -18,6 +18,7 @@ export interface SettingProps {
|
|
18
18
|
helpPopoverContent?: ReactNode;
|
19
19
|
options?: {value: string; content: string}[];
|
20
20
|
defaultValue?: unknown;
|
21
|
+
onValueUpdate?: VoidFunction;
|
21
22
|
}
|
22
23
|
|
23
24
|
export const Setting = ({
|
@@ -27,9 +28,15 @@ export const Setting = ({
|
|
27
28
|
helpPopoverContent,
|
28
29
|
options,
|
29
30
|
defaultValue,
|
31
|
+
onValueUpdate,
|
30
32
|
}: SettingProps) => {
|
31
33
|
const [settingValue, setValue] = useSetting(settingKey, defaultValue);
|
32
34
|
|
35
|
+
const onUpdate = (value: unknown) => {
|
36
|
+
setValue(value);
|
37
|
+
onValueUpdate?.();
|
38
|
+
};
|
39
|
+
|
33
40
|
const renderTitleComponent = (value: ReactNode) => {
|
34
41
|
if (helpPopoverContent) {
|
35
42
|
return (
|
@@ -48,7 +55,7 @@ export const Setting = ({
|
|
48
55
|
const getSettingsElement = (elementType: SettingsElementType) => {
|
49
56
|
switch (elementType) {
|
50
57
|
case 'switch': {
|
51
|
-
return <Switch checked={Boolean(settingValue)} onUpdate={
|
58
|
+
return <Switch checked={Boolean(settingValue)} onUpdate={onUpdate} />;
|
52
59
|
}
|
53
60
|
|
54
61
|
case 'radio': {
|
@@ -57,7 +64,7 @@ export const Setting = ({
|
|
57
64
|
}
|
58
65
|
|
59
66
|
return (
|
60
|
-
<RadioButton value={String(settingValue)} onUpdate={
|
67
|
+
<RadioButton value={String(settingValue)} onUpdate={onUpdate}>
|
61
68
|
{options.map(({value, content}) => {
|
62
69
|
return (
|
63
70
|
<RadioButton.Option value={value} key={value}>
|
@@ -10,6 +10,10 @@
|
|
10
10
|
"settings.theme.option-light": "Light",
|
11
11
|
"settings.theme.option-system": "System",
|
12
12
|
|
13
|
+
"settings.language.title": "Interface language",
|
14
|
+
"settings.language.option-russian": "Russian",
|
15
|
+
"settings.language.option-english": "English",
|
16
|
+
|
13
17
|
"settings.invertedDisks.title": "Inverted disks space indicators",
|
14
18
|
|
15
19
|
"settings.useNodesEndpoint.title": "Break the Nodes tab in Diagnostics",
|
@@ -19,5 +23,5 @@
|
|
19
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",
|
20
24
|
|
21
25
|
"settings.enableAdditionalQueryModes.title": "Enable additional query modes",
|
22
|
-
"settings.enableAdditionalQueryModes.popover": "Adds 'Data'
|
26
|
+
"settings.enableAdditionalQueryModes.popover": "Adds 'Data', 'YQL - QueryService' and 'PostgreSQL' modes. May not work on some versions"
|
23
27
|
}
|
@@ -10,6 +10,10 @@
|
|
10
10
|
"settings.theme.option-light": "Светлая",
|
11
11
|
"settings.theme.option-system": "Системная",
|
12
12
|
|
13
|
+
"settings.language.title": "Язык интерфейса",
|
14
|
+
"settings.language.option-russian": "Русский",
|
15
|
+
"settings.language.option-english": "English",
|
16
|
+
|
13
17
|
"settings.invertedDisks.title": "Инвертированные индикаторы места на дисках",
|
14
18
|
|
15
19
|
"settings.useNodesEndpoint.title": "Сломать вкладку Nodes в диагностике",
|
@@ -19,5 +23,5 @@
|
|
19
23
|
"settings.useBackendParamsForTables.popover": "Добавляет фильтрацию и сортировку таблиц Nodes и Storage с использованием параметров запроса. Может улушить производительность, но на старых версиях может привести к дополнительным запросам и большему времени ожидания загрузки",
|
20
24
|
|
21
25
|
"settings.enableAdditionalQueryModes.title": "Включить дополнительные режимы выполнения запросов",
|
22
|
-
"settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data'
|
26
|
+
"settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data', 'YQL - QueryService' и 'PostgreSQL'. Может работать некорректно на некоторых версиях"
|
23
27
|
}
|
@@ -6,10 +6,12 @@ import flaskIcon from '../../assets/icons/flask.svg';
|
|
6
6
|
import {
|
7
7
|
ENABLE_ADDITIONAL_QUERY_MODES,
|
8
8
|
INVERTED_DISKS_KEY,
|
9
|
+
LANGUAGE_KEY,
|
9
10
|
THEME_KEY,
|
10
11
|
USE_BACKEND_PARAMS_FOR_TABLES_KEY,
|
11
12
|
USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
|
12
13
|
} from '../../utils/constants';
|
14
|
+
import {Lang, defaultLang} from '../../utils/i18n';
|
13
15
|
|
14
16
|
import type {SettingProps} from './Setting';
|
15
17
|
import i18n from './i18n';
|
@@ -50,6 +52,29 @@ export const themeSetting: SettingProps = {
|
|
50
52
|
type: 'radio',
|
51
53
|
options: themeOptions,
|
52
54
|
};
|
55
|
+
|
56
|
+
const languageOptions = [
|
57
|
+
{
|
58
|
+
value: Lang.Ru,
|
59
|
+
content: i18n('settings.language.option-russian'),
|
60
|
+
},
|
61
|
+
{
|
62
|
+
value: Lang.En,
|
63
|
+
content: i18n('settings.language.option-english'),
|
64
|
+
},
|
65
|
+
];
|
66
|
+
|
67
|
+
export const languageSetting: SettingProps = {
|
68
|
+
settingKey: LANGUAGE_KEY,
|
69
|
+
title: i18n('settings.language.title'),
|
70
|
+
type: 'radio',
|
71
|
+
options: languageOptions,
|
72
|
+
defaultValue: defaultLang,
|
73
|
+
onValueUpdate: () => {
|
74
|
+
window.location.reload();
|
75
|
+
},
|
76
|
+
};
|
77
|
+
|
53
78
|
export const invertedDisksSetting: SettingProps = {
|
54
79
|
settingKey: INVERTED_DISKS_KEY,
|
55
80
|
title: i18n('settings.invertedDisks.title'),
|