ydb-embedded-ui 2.2.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +37 -0
- package/dist/assets/icons/shield.svg +3 -0
- package/dist/components/Errors/403/AccessDenied.tsx +19 -0
- package/dist/components/Errors/403/index.ts +1 -0
- package/dist/components/Errors/i18n/en.json +4 -0
- package/dist/components/Errors/i18n/index.ts +11 -0
- package/dist/components/Errors/i18n/ru.json +4 -0
- package/dist/components/NodesViewer/NodesViewer.js +1 -1
- package/dist/components/QueryResultTable/QueryResultTable.tsx +16 -21
- package/dist/{containers/Storage/StorageFilter/StorageFilter.tsx → components/Search/Search.tsx} +22 -22
- package/dist/components/Search/index.ts +1 -0
- package/dist/containers/App/App.scss +5 -1
- package/dist/containers/Nodes/Nodes.js +6 -1
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +7 -5
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +1 -11
- package/dist/containers/Storage/Pdisk/Pdisk.scss +15 -8
- package/dist/containers/Storage/Pdisk/Pdisk.tsx +22 -14
- package/dist/containers/Storage/Storage.js +39 -50
- package/dist/containers/Storage/StorageGroups/StorageGroups.scss +1 -4
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +27 -2
- package/dist/containers/Storage/StorageGroups/i18n/en.json +2 -1
- package/dist/containers/Storage/StorageGroups/i18n/ru.json +2 -1
- package/dist/containers/Storage/StorageNodes/StorageNodes.scss +14 -12
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +7 -5
- package/dist/containers/Storage/Vdisk/Vdisk.js +36 -23
- package/dist/containers/Storage/Vdisk/Vdisk.scss +6 -0
- package/dist/containers/TabletsFilters/TabletsFilters.js +5 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +6 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +82 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +6 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +6 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +7 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +29 -11
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -8
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +55 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +5 -5
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +16 -6
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/{IssueViewer.scss → IssueTree.scss} +3 -54
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTree.tsx +87 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/IssueTreeItem.scss +50 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/IssueTreeItem.tsx +25 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +13 -16
- package/dist/containers/Tenant/Diagnostics/Healthcheck/{IssuePreview/IssuePreview.tsx → Preview/PreviewItem/PreviewItem.tsx} +6 -8
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +34 -19
- package/dist/containers/Tenant/Preview/Preview.scss +6 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +1 -9
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +2 -2
- package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +1 -1
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +27 -20
- package/dist/containers/Tenant/Tenant.tsx +14 -16
- package/dist/containers/Tenants/Tenants.js +1 -1
- package/dist/store/reducers/describe.ts +71 -0
- package/dist/store/reducers/healthcheckInfo.ts +123 -0
- package/dist/store/reducers/olapStats.js +13 -0
- package/dist/store/reducers/schema.js +43 -1
- package/dist/store/reducers/storage.js +27 -17
- package/dist/store/reducers/tenant.js +3 -1
- package/dist/store/utils.ts +21 -13
- package/dist/styles/mixins.scss +1 -1
- package/dist/types/api/consumers.ts +3 -0
- package/dist/types/api/healthcheck.ts +1 -1
- package/dist/types/api/storage.ts +35 -10
- package/dist/types/store/healthcheck.ts +5 -1
- package/dist/types/store/storage.ts +1 -0
- package/dist/utils/hooks/useAutofetcher.ts +9 -3
- package/package.json +1 -1
- package/dist/containers/Storage/StorageFilter/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuePreview/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/IssuesList.tsx +0 -62
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssuesViewer.js +0 -151
- package/dist/store/reducers/describe.js +0 -45
- package/dist/store/reducers/healthcheckInfo.js +0 -45
@@ -1,7 +1,9 @@
|
|
1
1
|
import _ from 'lodash';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
3
|
import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
|
4
|
-
import {Label, Popover, PopoverBehavior} from '@gravity-ui/uikit';
|
4
|
+
import {Icon, Label, Popover, PopoverBehavior} from '@gravity-ui/uikit';
|
5
|
+
|
6
|
+
import shieldIcon from '../../../assets/icons/shield.svg';
|
5
7
|
|
6
8
|
import {Stack} from '../../../components/Stack/Stack';
|
7
9
|
//@ts-ignore
|
@@ -25,6 +27,7 @@ import './StorageGroups.scss';
|
|
25
27
|
|
26
28
|
enum TableColumnsIds {
|
27
29
|
PoolName = 'PoolName',
|
30
|
+
Type = 'Type',
|
28
31
|
GroupID = 'GroupID',
|
29
32
|
Used = 'Used',
|
30
33
|
Limit = 'Limit',
|
@@ -49,6 +52,7 @@ interface StorageGroupsProps {
|
|
49
52
|
|
50
53
|
const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
|
51
54
|
PoolName: 'Pool Name',
|
55
|
+
Type: 'Type',
|
52
56
|
GroupID: 'Group ID',
|
53
57
|
Used: 'Used',
|
54
58
|
Limit: 'Limit',
|
@@ -100,7 +104,7 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
|
|
100
104
|
<div className={b('pool-name-wrapper')}>
|
101
105
|
{splitted && (
|
102
106
|
<Popover
|
103
|
-
content={
|
107
|
+
content={value as string}
|
104
108
|
placement={['right']}
|
105
109
|
behavior={PopoverBehavior.Immediate}
|
106
110
|
>
|
@@ -114,6 +118,27 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
|
|
114
118
|
},
|
115
119
|
align: DataTable.LEFT,
|
116
120
|
},
|
121
|
+
{
|
122
|
+
name: TableColumnsIds.Type,
|
123
|
+
header: tableColumnsNames[TableColumnsIds.Type],
|
124
|
+
render: ({value, row}) => (
|
125
|
+
<>
|
126
|
+
<Label>{value as string || '—'}</Label>
|
127
|
+
{' '}
|
128
|
+
{row.Encryption && (
|
129
|
+
<Popover
|
130
|
+
content={i18n('encrypted')}
|
131
|
+
placement="right"
|
132
|
+
behavior={PopoverBehavior.Immediate}
|
133
|
+
>
|
134
|
+
<Label>
|
135
|
+
<Icon data={shieldIcon} />
|
136
|
+
</Label>
|
137
|
+
</Popover>
|
138
|
+
)}
|
139
|
+
</>
|
140
|
+
),
|
141
|
+
},
|
117
142
|
{
|
118
143
|
name: TableColumnsIds.Missing,
|
119
144
|
header: tableColumnsNames[TableColumnsIds.Missing],
|
@@ -7,27 +7,29 @@
|
|
7
7
|
|
8
8
|
min-width: 500px;
|
9
9
|
}
|
10
|
-
&
|
11
|
-
|
12
|
-
|
10
|
+
&__pdisks-item {
|
11
|
+
flex-grow: 1;
|
12
|
+
|
13
|
+
max-width: 200px;
|
14
|
+
margin-right: 10px;
|
15
|
+
|
16
|
+
&:last-child {
|
17
|
+
margin-right: 0px;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
&__fqdn-wrapper {
|
21
|
+
width: 330px;
|
13
22
|
}
|
14
|
-
&
|
23
|
+
&__fqdn {
|
15
24
|
display: inline-block;
|
16
25
|
overflow: hidden;
|
17
26
|
|
18
|
-
width: 330px;
|
19
27
|
max-width: 330px;
|
20
28
|
|
29
|
+
vertical-align: top;
|
21
30
|
text-overflow: ellipsis;
|
22
31
|
}
|
23
32
|
&__group-id {
|
24
33
|
font-weight: 500;
|
25
34
|
}
|
26
|
-
&__tooltip-wrapper {
|
27
|
-
display: flex;
|
28
|
-
align-items: center;
|
29
|
-
}
|
30
|
-
&__tooltip {
|
31
|
-
word-break: break-all;
|
32
|
-
}
|
33
35
|
}
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import _ from 'lodash';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
|
+
|
3
4
|
import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
|
4
5
|
import {Popover, PopoverBehavior} from '@gravity-ui/uikit';
|
5
6
|
|
6
|
-
//@ts-ignore
|
7
7
|
import {VisibleEntities} from '../../../store/reducers/storage';
|
8
8
|
|
9
9
|
import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
|
@@ -75,13 +75,13 @@ function StorageNodes({data, tableSettings, visibleEntities, onShowAll}: Storage
|
|
75
75
|
width: 350,
|
76
76
|
render: ({value}) => {
|
77
77
|
return (
|
78
|
-
<div className={b('
|
78
|
+
<div className={b('fqdn-wrapper')}>
|
79
79
|
<Popover
|
80
|
-
content={
|
80
|
+
content={value as string}
|
81
81
|
placement={['right']}
|
82
82
|
behavior={PopoverBehavior.Immediate}
|
83
83
|
>
|
84
|
-
<span className={b('
|
84
|
+
<span className={b('fqdn')}>{value as string}</span>
|
85
85
|
</Popover>
|
86
86
|
</div>
|
87
87
|
);
|
@@ -108,7 +108,9 @@ function StorageNodes({data, tableSettings, visibleEntities, onShowAll}: Storage
|
|
108
108
|
render: ({value, row}) => (
|
109
109
|
<div className={b('pdisks-wrapper')}>
|
110
110
|
{_.map(value as any, (el) => (
|
111
|
-
<
|
111
|
+
<div className={b('pdisks-item')}>
|
112
|
+
<Pdisk key={el.PDiskId} {...el} NodeId={row.NodeId} />
|
113
|
+
</div>
|
112
114
|
))}
|
113
115
|
</div>
|
114
116
|
),
|
@@ -1,17 +1,22 @@
|
|
1
1
|
import React, {useEffect, useState, useRef, useMemo} from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import cn from 'bem-cn-lite';
|
4
|
+
|
4
5
|
import {Label, Popup} from '@gravity-ui/uikit';
|
5
6
|
|
6
|
-
import
|
7
|
+
import InternalLink from '../../../components/InternalLink/InternalLink';
|
8
|
+
import {InfoViewer} from '../../../components/InfoViewer';
|
9
|
+
|
7
10
|
import routes, {createHref} from '../../../routes';
|
8
11
|
import {stringifyVdiskId, getPDiskId} from '../../../utils';
|
9
12
|
import {getPDiskType} from '../../../utils/pdisk';
|
10
|
-
import {
|
13
|
+
import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
|
14
|
+
|
15
|
+
import {STRUCTURE} from '../../Node/NodePages';
|
16
|
+
|
11
17
|
import DiskStateProgressBar, {
|
12
18
|
diskProgressColors,
|
13
19
|
} from '../DiskStateProgressBar/DiskStateProgressBar';
|
14
|
-
import {STRUCTURE} from '../../Node/NodePages';
|
15
20
|
|
16
21
|
import {colorSeverity, NOT_AVAILABLE_SEVERITY} from '../utils';
|
17
22
|
|
@@ -225,33 +230,41 @@ function Vdisk(props) {
|
|
225
230
|
const available = AvailableSize ? AvailableSize : PDisk?.AvailableSize;
|
226
231
|
|
227
232
|
if (!available) {
|
228
|
-
return;
|
233
|
+
return undefined;
|
229
234
|
}
|
230
|
-
|
231
|
-
|
232
|
-
|
235
|
+
|
236
|
+
return isNaN(Number(AllocatedSize))
|
237
|
+
? undefined
|
238
|
+
: (Number(AllocatedSize) * 100) / (Number(available) + Number(AllocatedSize));
|
233
239
|
}, [props.AllocatedSize, props.AvailableSize, props.PDisk?.AvailableSize]);
|
234
240
|
|
235
241
|
return (
|
236
242
|
<React.Fragment>
|
237
243
|
{renderPopup()}
|
238
244
|
<div className={b()} ref={anchor} onMouseEnter={showPopup} onMouseLeave={hidePopup}>
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
245
|
+
{props.NodeId ? (
|
246
|
+
<InternalLink
|
247
|
+
to={createHref(
|
248
|
+
routes.node,
|
249
|
+
{id: props.NodeId, activeTab: STRUCTURE},
|
250
|
+
{
|
251
|
+
pdiskId: props.PDisk?.PDiskId,
|
252
|
+
vdiskId: stringifyVdiskId(props.VDiskId),
|
253
|
+
},
|
254
|
+
)}
|
255
|
+
className={b('content')}
|
256
|
+
>
|
257
|
+
<DiskStateProgressBar
|
258
|
+
diskAllocatedPercent={vdiskAllocatedPercent}
|
259
|
+
severity={severity}
|
260
|
+
/>
|
261
|
+
</InternalLink>
|
262
|
+
) : (
|
263
|
+
<DiskStateProgressBar
|
264
|
+
diskAllocatedPercent={vdiskAllocatedPercent}
|
265
|
+
severity={severity}
|
266
|
+
/>
|
267
|
+
)}
|
255
268
|
</div>
|
256
269
|
</React.Fragment>
|
257
270
|
);
|
@@ -9,6 +9,7 @@ import {Loader, Select} from '@gravity-ui/uikit';
|
|
9
9
|
import ReactList from 'react-list';
|
10
10
|
|
11
11
|
import Tablet from '../../components/Tablet/Tablet';
|
12
|
+
import {AccessDenied} from '../../components/Errors/403';
|
12
13
|
|
13
14
|
import {TABLET_COLOR_TO_STATES, TABLETS_STATES} from '../../utils/constants';
|
14
15
|
import {showTooltip, hideTooltip} from '../../store/reducers/tooltip';
|
@@ -212,6 +213,10 @@ class TabletsFilters extends React.Component {
|
|
212
213
|
if (loading && !wasLoaded) {
|
213
214
|
return TabletsFilters.renderLoader();
|
214
215
|
} else if (error && typeof error === 'object') {
|
216
|
+
if (error.status === 403) {
|
217
|
+
return <AccessDenied />;
|
218
|
+
}
|
219
|
+
|
215
220
|
return <div>{error.statusText}</div>;
|
216
221
|
} else {
|
217
222
|
return this.renderContent();
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import {useEffect, useState} from 'react';
|
2
|
+
import {useDispatch, useSelector} from 'react-redux';
|
3
|
+
import block from 'bem-cn-lite';
|
4
|
+
|
5
|
+
import DataTable, {Column} from '@yandex-cloud/react-data-table';
|
6
|
+
|
7
|
+
import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
|
8
|
+
import {useAutofetcher} from '../../../../utils/hooks';
|
9
|
+
import {Search} from '../../../../components/Search';
|
10
|
+
import {getDescribe, selectConsumers} from '../../../../store/reducers/describe';
|
11
|
+
|
12
|
+
import i18n from './i18n';
|
13
|
+
|
14
|
+
import './Consumers.scss';
|
15
|
+
|
16
|
+
const b = block('ydb-consumers');
|
17
|
+
|
18
|
+
interface ConsumersProps {
|
19
|
+
path: string;
|
20
|
+
}
|
21
|
+
|
22
|
+
export const Consumers = ({path}: ConsumersProps) => {
|
23
|
+
const dispath = useDispatch();
|
24
|
+
|
25
|
+
const fetchData = () => {
|
26
|
+
dispath(getDescribe({path}));
|
27
|
+
};
|
28
|
+
|
29
|
+
useAutofetcher(fetchData, [path]);
|
30
|
+
|
31
|
+
const consumers = useSelector((state) => selectConsumers(state, path));
|
32
|
+
|
33
|
+
const [consumersToRender, setConsumersToRender] = useState(consumers);
|
34
|
+
|
35
|
+
useEffect(() => {
|
36
|
+
setConsumersToRender(consumers);
|
37
|
+
}, [consumers]);
|
38
|
+
|
39
|
+
const filterConsumersByName = (search: string) => {
|
40
|
+
const filteredConsumers = search
|
41
|
+
? consumers.filter((consumer) => {
|
42
|
+
const re = new RegExp(search, 'i');
|
43
|
+
return re.test(consumer.name);
|
44
|
+
})
|
45
|
+
: consumers;
|
46
|
+
|
47
|
+
setConsumersToRender(filteredConsumers);
|
48
|
+
};
|
49
|
+
|
50
|
+
const handleSearch = (value: string) => {
|
51
|
+
filterConsumersByName(value);
|
52
|
+
};
|
53
|
+
|
54
|
+
const columns: Column<any>[] = [
|
55
|
+
{
|
56
|
+
name: 'name',
|
57
|
+
header: i18n('table.columns.consumerName'),
|
58
|
+
width: 200,
|
59
|
+
},
|
60
|
+
];
|
61
|
+
|
62
|
+
if (consumers.length === 0) {
|
63
|
+
return <div>{i18n('noConsumersMessage')}</div>;
|
64
|
+
}
|
65
|
+
|
66
|
+
return (
|
67
|
+
<div className={b()}>
|
68
|
+
<Search
|
69
|
+
onChange={handleSearch}
|
70
|
+
placeholder={i18n('search.placeholder')}
|
71
|
+
className={b('search')}
|
72
|
+
/>
|
73
|
+
<DataTable
|
74
|
+
theme="yandex-cloud"
|
75
|
+
settings={DEFAULT_TABLE_SETTINGS}
|
76
|
+
columns={columns}
|
77
|
+
data={consumersToRender}
|
78
|
+
emptyDataMessage={i18n('table.emptyDataMessage')}
|
79
|
+
/>
|
80
|
+
</div>
|
81
|
+
);
|
82
|
+
};
|
@@ -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-consumers';
|
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 @@
|
|
1
|
+
export * from './Consumers';
|
@@ -5,7 +5,7 @@ import {Link} from 'react-router-dom';
|
|
5
5
|
import {useDispatch, useSelector} from 'react-redux';
|
6
6
|
import {useLocation} from 'react-router';
|
7
7
|
|
8
|
-
import {Switch, Tabs} from '@gravity-ui/uikit';
|
8
|
+
import {Loader, Switch, Tabs} from '@gravity-ui/uikit';
|
9
9
|
|
10
10
|
//@ts-ignore
|
11
11
|
import TopQueries from './TopQueries/TopQueries';
|
@@ -27,6 +27,7 @@ import Heatmap from '../../Heatmap/Heatmap';
|
|
27
27
|
import Compute from './Compute/Compute';
|
28
28
|
//@ts-ignore
|
29
29
|
import Tablets from '../../Tablets/Tablets';
|
30
|
+
import {Consumers} from './Consumers';
|
30
31
|
|
31
32
|
import routes, {createHref} from '../../../routes';
|
32
33
|
import type {EPathType} from '../../../types/api/schema';
|
@@ -53,9 +54,9 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
53
54
|
currentSchema: currentItem = {},
|
54
55
|
autorefresh,
|
55
56
|
} = useSelector((state: any) => state.schema);
|
56
|
-
const {
|
57
|
-
|
58
|
-
|
57
|
+
const {diagnosticsTab = GeneralPagesIds.overview, wasLoaded} = useSelector(
|
58
|
+
(state: any) => state.tenant,
|
59
|
+
);
|
59
60
|
|
60
61
|
const location = useLocation();
|
61
62
|
|
@@ -79,14 +80,17 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
79
80
|
dispatch(setDiagnosticsTab(tab));
|
80
81
|
};
|
81
82
|
const activeTab = useMemo(() => {
|
82
|
-
if (
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
83
|
+
if (wasLoaded) {
|
84
|
+
if (pages.find((el) => el.id === diagnosticsTab)) {
|
85
|
+
return diagnosticsTab;
|
86
|
+
} else {
|
87
|
+
const newPage = pages[0].id;
|
88
|
+
forwardToDiagnosticTab(newPage);
|
89
|
+
return newPage;
|
90
|
+
}
|
88
91
|
}
|
89
|
-
|
92
|
+
return undefined;
|
93
|
+
}, [pages, diagnosticsTab, wasLoaded]);
|
90
94
|
|
91
95
|
const onAutorefreshToggle = (value: boolean) => {
|
92
96
|
if (value) {
|
@@ -148,6 +152,9 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
148
152
|
case GeneralPagesIds.graph: {
|
149
153
|
return <Heatmap path={currentItem.Path} />;
|
150
154
|
}
|
155
|
+
case GeneralPagesIds.consumers: {
|
156
|
+
return <Consumers path={currentItem.Path} />;
|
157
|
+
}
|
151
158
|
default: {
|
152
159
|
return <div>No data...</div>;
|
153
160
|
}
|
@@ -185,6 +192,17 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
185
192
|
);
|
186
193
|
};
|
187
194
|
|
195
|
+
// Loader prevents incorrect loading of tabs
|
196
|
+
// After tabs are initially loaded it is no longer needed
|
197
|
+
// Thus there is no also "loading" check as in other parts of the project
|
198
|
+
if (!wasLoaded) {
|
199
|
+
return (
|
200
|
+
<div className={b('loader')}>
|
201
|
+
<Loader size="l" />
|
202
|
+
</div>
|
203
|
+
);
|
204
|
+
}
|
205
|
+
|
188
206
|
return (
|
189
207
|
<div className={b()}>
|
190
208
|
{renderTabs()}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {EPathType} from
|
1
|
+
import {EPathType} from '../../../types/api/schema';
|
2
2
|
|
3
3
|
export enum GeneralPagesIds {
|
4
4
|
'overview' = 'Overview',
|
@@ -11,11 +11,12 @@ export enum GeneralPagesIds {
|
|
11
11
|
'describe' = 'Describe',
|
12
12
|
'hotKeys' = 'hotKeys',
|
13
13
|
'graph' = 'graph',
|
14
|
+
'consumers' = 'consumers',
|
14
15
|
}
|
15
16
|
|
16
17
|
type Page = {
|
17
|
-
id: GeneralPagesIds
|
18
|
-
title: string
|
18
|
+
id: GeneralPagesIds;
|
19
|
+
title: string;
|
19
20
|
};
|
20
21
|
|
21
22
|
const overview = {
|
@@ -66,6 +67,11 @@ const graph = {
|
|
66
67
|
title: 'Graph',
|
67
68
|
};
|
68
69
|
|
70
|
+
const consumers = {
|
71
|
+
id: GeneralPagesIds.consumers,
|
72
|
+
title: 'Consumers',
|
73
|
+
};
|
74
|
+
|
69
75
|
export const DATABASE_PAGES = [
|
70
76
|
overview,
|
71
77
|
topQueries,
|
@@ -81,7 +87,8 @@ export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, descri
|
|
81
87
|
|
82
88
|
export const DIR_PAGES = [overview, topShards, describe];
|
83
89
|
|
84
|
-
export const
|
90
|
+
export const CDC_STREAM_PAGES = [overview, describe];
|
91
|
+
export const TOPIC_PAGES = [overview, consumers, describe];
|
85
92
|
|
86
93
|
// verbose mapping to guarantee correct tabs for new path types
|
87
94
|
// TS will error when a new type is added but not mapped here
|
@@ -97,10 +104,10 @@ const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
|
|
97
104
|
|
98
105
|
[EPathType.EPathTypeDir]: DIR_PAGES,
|
99
106
|
[EPathType.EPathTypeTableIndex]: DIR_PAGES,
|
100
|
-
|
101
|
-
[EPathType.EPathTypeCdcStream]:
|
107
|
+
|
108
|
+
[EPathType.EPathTypeCdcStream]: CDC_STREAM_PAGES,
|
109
|
+
|
102
110
|
[EPathType.EPathTypePersQueueGroup]: TOPIC_PAGES,
|
103
111
|
};
|
104
112
|
|
105
|
-
export const getPagesByType = (type?: EPathType) =>
|
106
|
-
(type && pathTypeToPages[type]) || DIR_PAGES;
|
113
|
+
export const getPagesByType = (type?: EPathType) => (type && pathTypeToPages[type]) || DIR_PAGES;
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {Button, Icon} from '@gravity-ui/uikit';
|
4
|
+
|
5
|
+
import updateArrow from '../../../../../assets/icons/update-arrow.svg';
|
6
|
+
|
7
|
+
import type {IIssuesTree} from '../../../../../types/store/healthcheck';
|
8
|
+
|
9
|
+
import IssueTree from '../IssuesViewer/IssueTree';
|
10
|
+
|
11
|
+
import i18n from '../i18n';
|
12
|
+
|
13
|
+
const b = cn('healthcheck');
|
14
|
+
|
15
|
+
interface DetailsProps {
|
16
|
+
issueTree?: IIssuesTree;
|
17
|
+
loading?: boolean;
|
18
|
+
onUpdate: VoidFunction;
|
19
|
+
}
|
20
|
+
|
21
|
+
export const Details = (props: DetailsProps) => {
|
22
|
+
const {loading, onUpdate, issueTree} = props;
|
23
|
+
|
24
|
+
if (!issueTree) {
|
25
|
+
return null;
|
26
|
+
}
|
27
|
+
|
28
|
+
const renderHealthcheckHeader = () => {
|
29
|
+
return (
|
30
|
+
<div className={b('details-header')}>
|
31
|
+
<h3 className={b('details-header-title')}>{i18n('title.healthcheck')}</h3>
|
32
|
+
<div className={b('details-header-update')}>
|
33
|
+
<Button size="s" onClick={onUpdate} loading={loading} view="flat-secondary">
|
34
|
+
<Icon data={updateArrow} height={20} width={20} />
|
35
|
+
</Button>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
);
|
39
|
+
};
|
40
|
+
|
41
|
+
const renderHealthcheckIssues = () => {
|
42
|
+
return (
|
43
|
+
<div className={b('issues-wrapper')}>
|
44
|
+
<IssueTree issueTree={issueTree} />
|
45
|
+
</div>
|
46
|
+
);
|
47
|
+
};
|
48
|
+
|
49
|
+
return (
|
50
|
+
<div className={b('details')}>
|
51
|
+
{renderHealthcheckHeader()}
|
52
|
+
{renderHealthcheckIssues()}
|
53
|
+
</div>
|
54
|
+
);
|
55
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './Details';
|
@@ -7,7 +7,7 @@
|
|
7
7
|
// Thus we will get rid of unneeded layout shift when scrollbar appear
|
8
8
|
min-width: 885px;
|
9
9
|
|
10
|
-
&
|
10
|
+
&__details {
|
11
11
|
padding: 25px 20px 20px;
|
12
12
|
}
|
13
13
|
|
@@ -15,7 +15,7 @@
|
|
15
15
|
margin-bottom: 15px;
|
16
16
|
}
|
17
17
|
|
18
|
-
&__issues {
|
18
|
+
&__issues-wrapper {
|
19
19
|
overflow-x: hidden;
|
20
20
|
overflow-y: auto;
|
21
21
|
|
@@ -32,20 +32,20 @@
|
|
32
32
|
padding: 15px 0;
|
33
33
|
}
|
34
34
|
|
35
|
-
&
|
35
|
+
&__details-header {
|
36
36
|
display: flex;
|
37
37
|
align-items: center;
|
38
38
|
|
39
39
|
margin-bottom: 20px;
|
40
40
|
}
|
41
41
|
|
42
|
-
&
|
42
|
+
&__details-header-title {
|
43
43
|
margin: 0 10px 0 0;
|
44
44
|
|
45
45
|
@include text-header-1();
|
46
46
|
}
|
47
47
|
|
48
|
-
&
|
48
|
+
&__details-header-update {
|
49
49
|
margin-left: 10px;
|
50
50
|
}
|
51
51
|
|