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
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,42 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [2.4.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.3.0...v2.4.0) (2022-10-27)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* **Diagnostics:** add consumers tab for topics ([4bb801c](https://github.com/ydb-platform/ydb-embedded-ui/commit/4bb801c0ef19dcda227c59e464b08f5e8f284c38))
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* add checks for fetch failure with no errors ([2c55107](https://github.com/ydb-platform/ydb-embedded-ui/commit/2c55107a7b47b3540ed0af66630ff85591f269a1))
|
14
|
+
* **Nodes:** display access denied on 403 ([7832afe](https://github.com/ydb-platform/ydb-embedded-ui/commit/7832afee601a40fc8b75f83bf0ed18b01c798d71))
|
15
|
+
* **QueryResult:** fix table display in fullscreen ([98674db](https://github.com/ydb-platform/ydb-embedded-ui/commit/98674db26b5fb09ac0d039a7779ae0c58951adde))
|
16
|
+
* **QueryResultTable:** make preview display all rows ([0ac83d0](https://github.com/ydb-platform/ydb-embedded-ui/commit/0ac83d0258b0d0d3d2e14c06be096fe5ddce02da))
|
17
|
+
* **Storage:** display access denied on 403 ([6d20333](https://github.com/ydb-platform/ydb-embedded-ui/commit/6d2033378956a54f05190905b0d537c6bd6c9851))
|
18
|
+
* **TabletsFilters:** display access denied on 403 ([018be19](https://github.com/ydb-platform/ydb-embedded-ui/commit/018be199602123f1d90e58c0b95545f6accc41fb))
|
19
|
+
|
20
|
+
## [2.3.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.2.1...v2.3.0) (2022-10-24)
|
21
|
+
|
22
|
+
|
23
|
+
### Features
|
24
|
+
|
25
|
+
* **PDisk:** display type on disk progressbar ([00bcbf5](https://github.com/ydb-platform/ydb-embedded-ui/commit/00bcbf5d439ca3bb4834fd5f191c65f0ac62585f))
|
26
|
+
* **Storage:** display media type for groups ([cdff5e9](https://github.com/ydb-platform/ydb-embedded-ui/commit/cdff5e9882f3f1f8769a3aeaf3e53c05f3ce1c07))
|
27
|
+
* **Storage:** display shield icon for encrypted groups ([d0a4442](https://github.com/ydb-platform/ydb-embedded-ui/commit/d0a4442dc100c312dcc54afcf685057cc587211d))
|
28
|
+
|
29
|
+
|
30
|
+
### Bug Fixes
|
31
|
+
|
32
|
+
* **Diagnostics:** fix tabs reset on page reload ([68d2971](https://github.com/ydb-platform/ydb-embedded-ui/commit/68d297165aea1360d1081349d8133804004f8fe0))
|
33
|
+
* **Storage:** prevent loading reset on cancelled fetch ([625159a](https://github.com/ydb-platform/ydb-embedded-ui/commit/625159a396e1ab84fe9da94d047da67fdd03b30f))
|
34
|
+
* **Storage:** shrink tooltip active area on FQDN ([7c33d5a](https://github.com/ydb-platform/ydb-embedded-ui/commit/7c33d5afb561efa64f90ce5b93edd30f7d27c247))
|
35
|
+
* **Tenant:** prevent selected tab reset on tree navigation ([a4e633a](https://github.com/ydb-platform/ydb-embedded-ui/commit/a4e633aa45c803503fe69e52f0f2cfac4c6aae0d))
|
36
|
+
* **Tenant:** show loader when fetching overview data ([ae77495](https://github.com/ydb-platform/ydb-embedded-ui/commit/ae77495faa687652040a1f2965700184220778b4))
|
37
|
+
* use correct prop for textinputs value ([de97ba1](https://github.com/ydb-platform/ydb-embedded-ui/commit/de97ba17ba8da54a626509cf08f147f9fcc67004))
|
38
|
+
* **useAutofetcher:** pass argument to indicate background fetch ([4063cb1](https://github.com/ydb-platform/ydb-embedded-ui/commit/4063cb1411338d351b612fc46c06bcc708fe32f1))
|
39
|
+
|
3
40
|
## [2.2.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.2.0...v2.2.1) (2022-10-19)
|
4
41
|
|
5
42
|
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.69222 1.56606C8.88795 1.47798 9.11205 1.47798 9.30778 1.56606L15.3078 4.26613C15.5769 4.38725 15.75 4.65494 15.75 4.95007V7.24592C15.75 11.1569 13.413 14.6895 9.81353 16.2192L9.29335 16.4403C9.1059 16.5199 8.8941 16.5199 8.70665 16.4403L8.18647 16.2192C4.58703 14.6895 2.25 11.1569 2.25 7.24592V4.95007C2.25 4.65494 2.42309 4.38725 2.69222 4.26613L8.69222 1.56606ZM3.75 5.43501V7.24592C3.75 10.5552 5.72748 13.5443 8.77317 14.8387L9 14.9351L9.22683 14.8387C12.2725 13.5443 14.25 10.5552 14.25 7.24592V5.43501L9 3.07244L3.75 5.43501ZM9.75 8.79933C10.1984 8.53997 10.5 8.05521 10.5 7.5C10.5 6.67157 9.82843 6 9 6C8.17157 6 7.5 6.67157 7.5 7.5C7.5 8.05521 7.80165 8.53997 8.25 8.79933V11.25C8.25 11.6642 8.58579 12 9 12C9.41421 12 9.75 11.6642 9.75 11.25V8.79933Z"/>
|
3
|
+
</svg>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import EmptyState from '../../EmptyState/EmptyState';
|
2
|
+
import {Illustration} from '../../Illustration';
|
3
|
+
|
4
|
+
import i18n from '../i18n';
|
5
|
+
|
6
|
+
interface AccessDeniedProps {
|
7
|
+
title?: string;
|
8
|
+
description?: string;
|
9
|
+
}
|
10
|
+
|
11
|
+
export const AccessDenied = ({title, description}: AccessDeniedProps) => {
|
12
|
+
return (
|
13
|
+
<EmptyState
|
14
|
+
image={<Illustration name="403" />}
|
15
|
+
title={title || i18n('403.title')}
|
16
|
+
description={description || i18n('403.description')}
|
17
|
+
/>
|
18
|
+
);
|
19
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './AccessDenied';
|
@@ -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-errors-access-denied';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -17,6 +17,8 @@ import './QueryResultTable.scss';
|
|
17
17
|
const TABLE_SETTINGS: Settings = {
|
18
18
|
...DEFAULT_TABLE_SETTINGS,
|
19
19
|
stripedRows: true,
|
20
|
+
dynamicRenderType: 'variable',
|
21
|
+
dynamicItemSizeGetter: () => 40,
|
20
22
|
};
|
21
23
|
|
22
24
|
export const b = cn('ydb-query-result-table');
|
@@ -53,7 +55,7 @@ const prepareGenericColumns = (data: KeyValueRow[]) => {
|
|
53
55
|
const column: Column<KeyValueRow> = {
|
54
56
|
name,
|
55
57
|
align: isNumeric(data[0][name]) ? DataTable.RIGHT : DataTable.LEFT,
|
56
|
-
sortAccessor: (row) => isNumeric(row[name]) ? Number(row[name]) : row[name],
|
58
|
+
sortAccessor: (row) => (isNumeric(row[name]) ? Number(row[name]) : row[name]),
|
57
59
|
render: ({value}) => <Cell value={value as string} />,
|
58
60
|
};
|
59
61
|
|
@@ -61,31 +63,28 @@ const prepareGenericColumns = (data: KeyValueRow[]) => {
|
|
61
63
|
});
|
62
64
|
};
|
63
65
|
|
64
|
-
const getRowIndex = (_: unknown, index: number) => index
|
66
|
+
const getRowIndex = (_: unknown, index: number) => index;
|
65
67
|
|
66
|
-
interface QueryResultTableProps
|
68
|
+
interface QueryResultTableProps
|
69
|
+
extends Omit<DataTableProps<KeyValueRow>, 'data' | 'columns' | 'theme'> {
|
67
70
|
data?: KeyValueRow[];
|
68
71
|
columns?: ColumnType[];
|
69
72
|
}
|
70
73
|
|
71
74
|
export const QueryResultTable = (props: QueryResultTableProps) => {
|
72
|
-
const {
|
73
|
-
columns: rawColumns,
|
74
|
-
data: rawData,
|
75
|
-
settings: settingsMix,
|
76
|
-
...restProps
|
77
|
-
} = props;
|
75
|
+
const {columns: rawColumns, data: rawData, settings: settingsMix, ...restProps} = props;
|
78
76
|
|
79
77
|
const data = useMemo(() => prepareQueryResponse(rawData), [rawData]);
|
80
78
|
const columns = useMemo(() => {
|
81
|
-
return rawColumns ?
|
82
|
-
prepareTypedColumns(rawColumns) :
|
83
|
-
prepareGenericColumns(data);
|
79
|
+
return rawColumns ? prepareTypedColumns(rawColumns) : prepareGenericColumns(data);
|
84
80
|
}, [data, rawColumns]);
|
85
|
-
const settings = useMemo(
|
86
|
-
|
87
|
-
|
88
|
-
|
81
|
+
const settings = useMemo(
|
82
|
+
() => ({
|
83
|
+
...TABLE_SETTINGS,
|
84
|
+
...settingsMix,
|
85
|
+
}),
|
86
|
+
[settingsMix],
|
87
|
+
);
|
89
88
|
|
90
89
|
// empty data is expected to be be an empty array
|
91
90
|
// undefined data is not rendered at all
|
@@ -94,11 +93,7 @@ export const QueryResultTable = (props: QueryResultTableProps) => {
|
|
94
93
|
}
|
95
94
|
|
96
95
|
if (!columns.length) {
|
97
|
-
return (
|
98
|
-
<div className={b('message')}>
|
99
|
-
{i18n('empty')}
|
100
|
-
</div>
|
101
|
-
);
|
96
|
+
return <div className={b('message')}>{i18n('empty')}</div>;
|
102
97
|
}
|
103
98
|
|
104
99
|
return (
|
package/dist/{containers/Storage/StorageFilter/StorageFilter.tsx → components/Search/Search.tsx}
RENAMED
@@ -1,28 +1,28 @@
|
|
1
|
-
import {
|
1
|
+
import {useRef, useEffect, useState} from 'react';
|
2
2
|
|
3
3
|
import {TextInput} from '@gravity-ui/uikit';
|
4
4
|
|
5
|
-
interface
|
6
|
-
|
5
|
+
interface SearchProps {
|
6
|
+
onChange: (value: string) => void;
|
7
7
|
value?: string;
|
8
|
-
|
9
|
-
onChange?: (value: string) => void;
|
8
|
+
className?: string;
|
10
9
|
debounce?: number;
|
10
|
+
placeholder?: string;
|
11
11
|
}
|
12
12
|
|
13
|
-
export const
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
export const Search = ({
|
14
|
+
onChange,
|
15
|
+
value = '',
|
16
|
+
className,
|
17
|
+
debounce = 200,
|
18
|
+
placeholder,
|
19
|
+
}: SearchProps) => {
|
20
|
+
const [searchValue, setSearchValue] = useState<string>(value);
|
21
|
+
|
22
22
|
const timer = useRef<number>();
|
23
23
|
|
24
24
|
useEffect(() => {
|
25
|
-
|
25
|
+
setSearchValue((prevValue) => {
|
26
26
|
if (prevValue !== value) {
|
27
27
|
return value;
|
28
28
|
}
|
@@ -31,8 +31,8 @@ export const StorageFilter = (props: StorageFilterProps) => {
|
|
31
31
|
});
|
32
32
|
}, [value]);
|
33
33
|
|
34
|
-
const
|
35
|
-
|
34
|
+
const onSearchValueChange = (newValue: string) => {
|
35
|
+
setSearchValue(newValue);
|
36
36
|
|
37
37
|
window.clearTimeout(timer.current);
|
38
38
|
timer.current = window.setTimeout(() => {
|
@@ -42,12 +42,12 @@ export const StorageFilter = (props: StorageFilterProps) => {
|
|
42
42
|
|
43
43
|
return (
|
44
44
|
<TextInput
|
45
|
-
className={className}
|
46
|
-
placeholder={placeholder}
|
47
|
-
value={filterValue}
|
48
|
-
onUpdate={changeFilter}
|
49
45
|
hasClear
|
50
46
|
autoFocus
|
47
|
+
className={className}
|
48
|
+
placeholder={placeholder}
|
49
|
+
value={searchValue}
|
50
|
+
onUpdate={onSearchValueChange}
|
51
51
|
/>
|
52
52
|
);
|
53
|
-
}
|
53
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './Search';
|
@@ -31,6 +31,10 @@ body,
|
|
31
31
|
--data-table-row-height: 40px;
|
32
32
|
}
|
33
33
|
|
34
|
+
.yc-root {
|
35
|
+
--ydb-data-table-color-hover: var(--yc-color-base-float-hover);
|
36
|
+
}
|
37
|
+
|
34
38
|
.yc-select__label {
|
35
39
|
font-weight: 600;
|
36
40
|
}
|
@@ -130,7 +134,7 @@ body,
|
|
130
134
|
}
|
131
135
|
|
132
136
|
.yc-root .data-table_highlight-rows .data-table__row:hover {
|
133
|
-
background: var(--
|
137
|
+
background: var(--ydb-data-table-color-hover);
|
134
138
|
}
|
135
139
|
|
136
140
|
.yc-table-column-setup__item {
|
@@ -8,6 +8,7 @@ import {Loader, TextInput, Label} from '@gravity-ui/uikit';
|
|
8
8
|
|
9
9
|
import ProblemFilter, {problemFilterType} from '../../components/ProblemFilter/ProblemFilter';
|
10
10
|
import {Illustration} from '../../components/Illustration';
|
11
|
+
import {AccessDenied} from '../../components/Errors/403';
|
11
12
|
|
12
13
|
import {hideTooltip, showTooltip} from '../../store/reducers/tooltip';
|
13
14
|
import {withSearch} from '../../HOCS';
|
@@ -84,7 +85,7 @@ class Nodes extends React.Component {
|
|
84
85
|
<TextInput
|
85
86
|
className={b('search')}
|
86
87
|
placeholder="Host name"
|
87
|
-
|
88
|
+
value={searchQuery}
|
88
89
|
onUpdate={this.handleSearchQueryChange}
|
89
90
|
hasClear
|
90
91
|
autoFocus
|
@@ -164,6 +165,10 @@ class Nodes extends React.Component {
|
|
164
165
|
if (loading && !wasLoaded) {
|
165
166
|
return Nodes.renderLoader();
|
166
167
|
} else if (error) {
|
168
|
+
if (error.status === 403) {
|
169
|
+
return <AccessDenied />;
|
170
|
+
}
|
171
|
+
|
167
172
|
return <div>{error.statusText}</div>;
|
168
173
|
} else {
|
169
174
|
return this.renderContent();
|
@@ -1,18 +1,20 @@
|
|
1
1
|
.storage-disk-progress-bar {
|
2
|
+
$block: &;
|
3
|
+
|
2
4
|
$border-width: 2px;
|
3
5
|
$outer-border-radius: 4px;
|
4
6
|
$inner-border-radius: $outer-border-radius - $border-width;
|
5
7
|
|
6
|
-
$block: &;
|
7
8
|
position: relative;
|
8
9
|
|
9
|
-
display:
|
10
|
+
display: block;
|
10
11
|
|
11
|
-
width:
|
12
|
+
min-width: 50px;
|
12
13
|
height: var(--yc-text-body-2-line-height);
|
13
14
|
|
14
|
-
|
15
|
+
text-align: center;
|
15
16
|
|
17
|
+
color: var(--yc-color-text-primary);
|
16
18
|
border: $border-width solid var(--yc-color-infographics-misc-heavy);
|
17
19
|
border-radius: $outer-border-radius;
|
18
20
|
background-color: var(--yc-color-infographics-misc-light);
|
@@ -82,7 +84,7 @@
|
|
82
84
|
}
|
83
85
|
}
|
84
86
|
&__filled-title {
|
85
|
-
position:
|
87
|
+
position: relative;
|
86
88
|
z-index: 2;
|
87
89
|
|
88
90
|
font-size: var(--yc-text-body-1-font-size);
|
@@ -5,8 +5,6 @@ import cn from 'bem-cn-lite';
|
|
5
5
|
import {INVERTED_DISKS_KEY} from '../../../utils/constants';
|
6
6
|
import {getSettingValue} from '../../../store/reducers/settings';
|
7
7
|
|
8
|
-
import InternalLink from '../../../components/InternalLink/InternalLink';
|
9
|
-
|
10
8
|
import './DiskStateProgressBar.scss';
|
11
9
|
|
12
10
|
const b = cn('storage-disk-progress-bar');
|
@@ -23,13 +21,11 @@ export const diskProgressColors = {
|
|
23
21
|
interface DiskStateProgressBarProps {
|
24
22
|
diskAllocatedPercent?: number;
|
25
23
|
severity?: keyof typeof diskProgressColors;
|
26
|
-
href?: string;
|
27
24
|
}
|
28
25
|
|
29
26
|
function DiskStateProgressBar({
|
30
27
|
diskAllocatedPercent = -1,
|
31
28
|
severity,
|
32
|
-
href,
|
33
29
|
}: DiskStateProgressBarProps) {
|
34
30
|
const inverted = useSelector((state) => getSettingValue(state, INVERTED_DISKS_KEY));
|
35
31
|
|
@@ -63,13 +59,7 @@ function DiskStateProgressBar({
|
|
63
59
|
aria-valuemax={100}
|
64
60
|
aria-valuenow={diskAllocatedPercent}
|
65
61
|
>
|
66
|
-
{
|
67
|
-
<InternalLink to={href} className={b('link')}>
|
68
|
-
{renderAllocatedPercent()}
|
69
|
-
</InternalLink>
|
70
|
-
) : (
|
71
|
-
renderAllocatedPercent()
|
72
|
-
)}
|
62
|
+
{renderAllocatedPercent()}
|
73
63
|
</div>
|
74
64
|
);
|
75
65
|
}
|
@@ -1,18 +1,25 @@
|
|
1
1
|
.pdisk-storage {
|
2
|
-
|
3
|
-
flex-grow: 1;
|
4
|
-
align-items: center;
|
2
|
+
position: relative;
|
5
3
|
|
6
|
-
|
7
|
-
margin-right: 10px;
|
4
|
+
min-width: 120px;
|
8
5
|
|
9
|
-
|
6
|
+
border-radius: 4px; // to match interactive area with disk shape
|
10
7
|
|
11
|
-
|
12
|
-
|
8
|
+
&__content {
|
9
|
+
border-radius: 4px; // to match interactive area with disk shape
|
13
10
|
}
|
14
11
|
|
15
12
|
&__popup-wrapper {
|
16
13
|
padding: 12px;
|
17
14
|
}
|
15
|
+
|
16
|
+
&__media-type {
|
17
|
+
position: absolute;
|
18
|
+
top: 0;
|
19
|
+
right: 4px;
|
20
|
+
|
21
|
+
font-size: var(--yc-text-body-1-font-size);
|
22
|
+
|
23
|
+
color: var(--yc-color-text-secondary);
|
24
|
+
}
|
18
25
|
}
|
@@ -1,21 +1,23 @@
|
|
1
1
|
import React, {useEffect, useState, useRef, useMemo} from 'react';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
|
+
|
3
4
|
import {Popup} from '@gravity-ui/uikit';
|
4
5
|
|
5
|
-
import
|
6
|
-
|
7
|
-
|
8
|
-
//@ts-ignore
|
6
|
+
import {InfoViewer} from '../../../components/InfoViewer';
|
7
|
+
import InternalLink from '../../../components/InternalLink/InternalLink';
|
8
|
+
|
9
9
|
import routes, {createHref} from '../../../routes';
|
10
|
-
|
10
|
+
import type {RequiredField} from '../../../types';
|
11
|
+
import {TPDiskStateInfo, TPDiskState} from '../../../types/api/storage';
|
11
12
|
import {getPDiskId} from '../../../utils';
|
12
13
|
import {getPDiskType} from '../../../utils/pdisk';
|
13
|
-
import {
|
14
|
-
|
14
|
+
import {bytesToGB} from '../../../utils/utils';
|
15
|
+
|
16
|
+
import {STRUCTURE} from '../../Node/NodePages';
|
17
|
+
|
15
18
|
import DiskStateProgressBar, {
|
16
19
|
diskProgressColors,
|
17
20
|
} from '../DiskStateProgressBar/DiskStateProgressBar';
|
18
|
-
import {STRUCTURE} from '../../Node/NodePages';
|
19
21
|
|
20
22
|
import {colorSeverity, NOT_AVAILABLE_SEVERITY} from '../utils';
|
21
23
|
|
@@ -123,8 +125,9 @@ function Pdisk(props: PDiskProps) {
|
|
123
125
|
const {AvailableSize, TotalSize} = props;
|
124
126
|
|
125
127
|
if (!AvailableSize || !TotalSize) {
|
126
|
-
return;
|
128
|
+
return undefined;
|
127
129
|
}
|
130
|
+
|
128
131
|
return !isNaN(Number(AvailableSize)) && !isNaN(Number(TotalSize))
|
129
132
|
? Math.round(((Number(TotalSize) - Number(AvailableSize)) * 100) / Number(TotalSize))
|
130
133
|
: undefined;
|
@@ -134,15 +137,20 @@ function Pdisk(props: PDiskProps) {
|
|
134
137
|
<React.Fragment>
|
135
138
|
{renderPopup()}
|
136
139
|
<div className={b()} ref={anchor} onMouseEnter={showPopup} onMouseLeave={hidePopup}>
|
137
|
-
<
|
138
|
-
|
139
|
-
severity={severity as keyof typeof diskProgressColors}
|
140
|
-
href={createHref(
|
140
|
+
<InternalLink
|
141
|
+
to={createHref(
|
141
142
|
routes.node,
|
142
143
|
{id: props.NodeId, activeTab: STRUCTURE},
|
143
144
|
{pdiskId: props.PDiskId || ''},
|
144
145
|
)}
|
145
|
-
|
146
|
+
className={b('content')}
|
147
|
+
>
|
148
|
+
<DiskStateProgressBar
|
149
|
+
diskAllocatedPercent={pdiskAllocatedPercent}
|
150
|
+
severity={severity as keyof typeof diskProgressColors}
|
151
|
+
/>
|
152
|
+
<div className={b('media-type')}>{getPDiskType(props)}</div>
|
153
|
+
</InternalLink>
|
146
154
|
</div>
|
147
155
|
</React.Fragment>
|
148
156
|
);
|
@@ -5,10 +5,11 @@ import cn from 'bem-cn-lite';
|
|
5
5
|
import DataTable from '@yandex-cloud/react-data-table';
|
6
6
|
import {RadioButton, Label} from '@gravity-ui/uikit';
|
7
7
|
|
8
|
-
import {
|
8
|
+
import {Search} from '../../components/Search';
|
9
9
|
import {UsageFilter} from './UsageFilter';
|
10
10
|
import {AutoFetcher} from '../../utils/autofetcher';
|
11
11
|
import {TableSkeleton} from '../../components/TableSkeleton/TableSkeleton';
|
12
|
+
import {AccessDenied} from '../../components/Errors/403';
|
12
13
|
|
13
14
|
import {
|
14
15
|
getStorageInfo,
|
@@ -68,14 +69,8 @@ class Storage extends React.Component {
|
|
68
69
|
};
|
69
70
|
|
70
71
|
componentDidMount() {
|
71
|
-
const {
|
72
|
-
|
73
|
-
nodeId,
|
74
|
-
setVisibleEntities,
|
75
|
-
storageType,
|
76
|
-
setHeader,
|
77
|
-
getNodesList,
|
78
|
-
} = this.props;
|
72
|
+
const {tenant, nodeId, setVisibleEntities, storageType, setHeader, getNodesList} =
|
73
|
+
this.props;
|
79
74
|
|
80
75
|
this.autofetcher = new AutoFetcher();
|
81
76
|
getNodesList();
|
@@ -106,12 +101,7 @@ class Storage extends React.Component {
|
|
106
101
|
}
|
107
102
|
|
108
103
|
componentDidUpdate(prevProps) {
|
109
|
-
const {
|
110
|
-
visibleEntities,
|
111
|
-
storageType,
|
112
|
-
autorefresh,
|
113
|
-
database,
|
114
|
-
} = this.props;
|
104
|
+
const {visibleEntities, storageType, autorefresh, database} = this.props;
|
115
105
|
|
116
106
|
const startFetch = () => {
|
117
107
|
this.getStorageInfo({
|
@@ -139,7 +129,10 @@ class Storage extends React.Component {
|
|
139
129
|
restartAutorefresh();
|
140
130
|
}
|
141
131
|
|
142
|
-
if (
|
132
|
+
if (
|
133
|
+
storageType !== prevProps.storageType ||
|
134
|
+
visibleEntities !== prevProps.visibleEntities
|
135
|
+
) {
|
143
136
|
startFetch();
|
144
137
|
|
145
138
|
if (!database || (database && autorefresh)) {
|
@@ -154,25 +147,22 @@ class Storage extends React.Component {
|
|
154
147
|
}
|
155
148
|
|
156
149
|
getStorageInfo(data) {
|
157
|
-
const {
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
});
|
150
|
+
const {tenant, nodeId, getStorageInfo} = this.props;
|
151
|
+
|
152
|
+
getStorageInfo(
|
153
|
+
{
|
154
|
+
tenant,
|
155
|
+
nodeId,
|
156
|
+
...data,
|
157
|
+
},
|
158
|
+
{
|
159
|
+
concurrentId: 'getStorageInfo',
|
160
|
+
},
|
161
|
+
);
|
170
162
|
}
|
171
163
|
|
172
164
|
renderLoader() {
|
173
|
-
return (
|
174
|
-
<TableSkeleton className={b('loader')}/>
|
175
|
-
);
|
165
|
+
return <TableSkeleton className={b('loader')} />;
|
176
166
|
}
|
177
167
|
|
178
168
|
renderDataTable() {
|
@@ -212,14 +202,8 @@ class Storage extends React.Component {
|
|
212
202
|
};
|
213
203
|
|
214
204
|
renderEntitiesCount() {
|
215
|
-
const {
|
216
|
-
|
217
|
-
groupsCount,
|
218
|
-
nodesCount,
|
219
|
-
flatListStorageEntities,
|
220
|
-
loading,
|
221
|
-
wasLoaded,
|
222
|
-
} = this.props;
|
205
|
+
const {storageType, groupsCount, nodesCount, flatListStorageEntities, loading, wasLoaded} =
|
206
|
+
this.props;
|
223
207
|
|
224
208
|
let label = `${storageType === StorageTypes.groups ? 'Groups' : 'Nodes'}: `;
|
225
209
|
const count = storageType === StorageTypes.groups ? groupsCount : nodesCount;
|
@@ -253,8 +237,12 @@ class Storage extends React.Component {
|
|
253
237
|
return (
|
254
238
|
<div className={b('controls')}>
|
255
239
|
<div className={b('search')}>
|
256
|
-
<
|
257
|
-
placeholder={
|
240
|
+
<Search
|
241
|
+
placeholder={
|
242
|
+
storageType === StorageTypes.groups
|
243
|
+
? 'Group ID, Pool name'
|
244
|
+
: 'Node ID, FQDN'
|
245
|
+
}
|
258
246
|
onChange={setStorageFilter}
|
259
247
|
value={filter}
|
260
248
|
/>
|
@@ -300,17 +288,18 @@ class Storage extends React.Component {
|
|
300
288
|
const {loading, wasLoaded, error} = this.props;
|
301
289
|
const showLoader = loading && !wasLoaded;
|
302
290
|
|
291
|
+
if (error) {
|
292
|
+
if (error.status === 403) {
|
293
|
+
return <AccessDenied />;
|
294
|
+
}
|
295
|
+
|
296
|
+
return <div className={b()}>{error.statusText}</div>;
|
297
|
+
}
|
298
|
+
|
303
299
|
return (
|
304
300
|
<div className={b()}>
|
305
301
|
{this.renderControls()}
|
306
|
-
{
|
307
|
-
<div>{error.statusText}</div>
|
308
|
-
)}
|
309
|
-
{showLoader ? (
|
310
|
-
this.renderLoader()
|
311
|
-
) : (
|
312
|
-
this.renderDataTable()
|
313
|
-
)}
|
302
|
+
{showLoader ? this.renderLoader() : this.renderDataTable()}
|
314
303
|
</div>
|
315
304
|
);
|
316
305
|
}
|
@@ -23,7 +23,7 @@
|
|
23
23
|
background: var(--yc-color-base-background);
|
24
24
|
|
25
25
|
.data-table__row:hover & {
|
26
|
-
background: var(--
|
26
|
+
background: var(--ydb-data-table-color-hover);
|
27
27
|
}
|
28
28
|
}
|
29
29
|
}
|
@@ -51,7 +51,4 @@
|
|
51
51
|
&__group-id {
|
52
52
|
font-weight: 500;
|
53
53
|
}
|
54
|
-
&__tooltip {
|
55
|
-
word-break: break-all;
|
56
|
-
}
|
57
54
|
}
|