ydb-embedded-ui 3.2.0 → 3.2.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 +11 -0
- package/dist/components/EntitiesCount/EntitiesCount.tsx +34 -0
- package/dist/components/EntitiesCount/i18n/en.json +3 -0
- package/dist/components/{AsideNavigation/Settings → EntitiesCount}/i18n/index.ts +2 -2
- package/dist/components/EntitiesCount/i18n/ru.json +3 -0
- package/dist/components/EntitiesCount/index.ts +1 -0
- package/dist/components/Fullscreen/Fullscreen.scss +7 -5
- package/dist/components/TabletsOverall/TabletsOverall.tsx +4 -4
- package/dist/components/TabletsStatistic/TabletsStatistic.tsx +56 -0
- package/dist/components/TabletsStatistic/index.ts +1 -0
- package/dist/containers/App/App.scss +4 -12
- package/dist/containers/AsideNavigation/AsideNavigation.scss +0 -18
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +95 -33
- package/dist/containers/Heatmap/Heatmap.scss +0 -7
- package/dist/containers/Heatmap/Heatmap.tsx +203 -0
- package/dist/containers/Heatmap/HeatmapCanvas/HeatmapCanvas.js +2 -1
- package/dist/containers/Heatmap/index.ts +1 -0
- package/dist/containers/Node/Node.tsx +1 -1
- package/dist/containers/Storage/Storage.js +12 -19
- package/dist/containers/Tablets/Tablets.scss +0 -5
- package/dist/containers/Tablets/Tablets.tsx +172 -0
- package/dist/containers/Tablets/i18n/en.json +6 -0
- package/dist/{components/AsideNavigation → containers/Tablets}/i18n/index.ts +1 -1
- package/dist/containers/Tablets/i18n/ru.json +6 -0
- package/dist/containers/Tablets/index.ts +1 -0
- package/dist/containers/TabletsFilters/TabletsFilters.js +4 -8
- package/dist/containers/TabletsFilters/TabletsFilters.scss +6 -2
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -8
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -7
- package/dist/containers/Tenants/Tenants.js +1 -1
- package/dist/containers/UserSettings/UserSettings.tsx +4 -3
- package/dist/routes.ts +1 -1
- package/dist/store/reducers/{heatmap.js → heatmap.ts} +33 -18
- package/dist/store/reducers/settings.js +12 -2
- package/dist/types/api/compute.ts +1 -1
- package/dist/types/api/schema.ts +16 -3
- package/dist/types/store/heatmap.ts +51 -0
- package/dist/utils/constants.ts +1 -37
- package/dist/utils/getNodesColumns.js +7 -2
- package/dist/utils/tablet.ts +53 -0
- package/package.json +2 -1
- package/dist/components/AsideNavigation/AsideHeader.scss +0 -147
- package/dist/components/AsideNavigation/AsideHeader.tsx +0 -389
- package/dist/components/AsideNavigation/AsideHeaderFooterItem/AsideHeaderFooterItem.scss +0 -82
- package/dist/components/AsideNavigation/AsideHeaderFooterItem/AsideHeaderFooterItem.tsx +0 -138
- package/dist/components/AsideNavigation/AsideHeaderFooterSlot/AsideHeaderFooterSlot.tsx +0 -33
- package/dist/components/AsideNavigation/AsideHeaderFooterSlot/SlotsContext.tsx +0 -49
- package/dist/components/AsideNavigation/AsideHeaderTooltip/AsideHeaderTooltip.scss +0 -16
- package/dist/components/AsideNavigation/AsideHeaderTooltip/AsideHeaderTooltip.tsx +0 -37
- package/dist/components/AsideNavigation/CompositeBar/CompositeBar.scss +0 -108
- package/dist/components/AsideNavigation/CompositeBar/CompositeBar.tsx +0 -282
- package/dist/components/AsideNavigation/Content/Content.tsx +0 -35
- package/dist/components/AsideNavigation/Drawer/Drawer.scss +0 -76
- package/dist/components/AsideNavigation/Drawer/Drawer.tsx +0 -134
- package/dist/components/AsideNavigation/Drawer/index.ts +0 -1
- package/dist/components/AsideNavigation/Logo/Logo.scss +0 -43
- package/dist/components/AsideNavigation/Logo/Logo.tsx +0 -82
- package/dist/components/AsideNavigation/Settings/README.md +0 -92
- package/dist/components/AsideNavigation/Settings/Settings.scss +0 -128
- package/dist/components/AsideNavigation/Settings/Settings.tsx +0 -270
- package/dist/components/AsideNavigation/Settings/SettingsMenu/SettingsMenu.scss +0 -78
- package/dist/components/AsideNavigation/Settings/SettingsMenu/SettingsMenu.tsx +0 -141
- package/dist/components/AsideNavigation/Settings/SettingsSearch/SettingsSearch.tsx +0 -57
- package/dist/components/AsideNavigation/Settings/collect-settings.ts +0 -156
- package/dist/components/AsideNavigation/Settings/filter-settings.ts +0 -38
- package/dist/components/AsideNavigation/Settings/helpers.ts +0 -39
- package/dist/components/AsideNavigation/Settings/i18n/en.json +0 -5
- package/dist/components/AsideNavigation/Settings/i18n/ru.json +0 -5
- package/dist/components/AsideNavigation/Settings/index.ts +0 -1
- package/dist/components/AsideNavigation/constants.ts +0 -28
- package/dist/components/AsideNavigation/helpers.ts +0 -34
- package/dist/components/AsideNavigation/i18n/en.json +0 -4
- package/dist/components/AsideNavigation/i18n/ru.json +0 -4
- package/dist/components/AsideNavigation/icons.ts +0 -32
- package/dist/components/AsideNavigation/types.ts +0 -23
- package/dist/components/TabletsStatistic/TabletsStatistic.js +0 -58
- package/dist/containers/Heatmap/Heatmap.js +0 -244
- package/dist/containers/Tablets/Tablets.js +0 -228
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.2.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.2.0...v3.2.1) (2023-01-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* align standard errors to the left ([cce100c](https://github.com/ydb-platform/ydb-embedded-ui/commit/cce100c5df83243df1fb0bc59d84d0d9b33719e6))
|
|
9
|
+
* **TabletsFilters:** properly display long data in select options ([ea37d9f](https://github.com/ydb-platform/ydb-embedded-ui/commit/ea37d9fc08245ccdd38a6120dd620f59a528879c))
|
|
10
|
+
* **TabletsFilters:** replace constants ([ea948ca](https://github.com/ydb-platform/ydb-embedded-ui/commit/ea948ca86276b5521979105b2ab99546da389e80))
|
|
11
|
+
* **TabletsStatistic:** process different tablets state types ([78798de](https://github.com/ydb-platform/ydb-embedded-ui/commit/78798de984bf4f6133515bb1c440e4fe0d15b07e))
|
|
12
|
+
* **Tenant:** always display Pools heading ([94baeff](https://github.com/ydb-platform/ydb-embedded-ui/commit/94baeff82f9c2c1aecda7c11c3b090125ba9e4b6))
|
|
13
|
+
|
|
3
14
|
## [3.2.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.1.0...v3.2.0) (2023-01-09)
|
|
4
15
|
|
|
5
16
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {Label} from '@gravity-ui/uikit';
|
|
2
|
+
import i18n from './i18n';
|
|
3
|
+
|
|
4
|
+
interface EntitiesCountProps {
|
|
5
|
+
current: number | string;
|
|
6
|
+
total?: number | string;
|
|
7
|
+
label?: string;
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const EntitiesCount = ({total, current, label, loading, className}: EntitiesCountProps) => {
|
|
13
|
+
let content = '';
|
|
14
|
+
|
|
15
|
+
if (label) {
|
|
16
|
+
content += `${label}: `;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (loading) {
|
|
20
|
+
content += '...';
|
|
21
|
+
} else {
|
|
22
|
+
content += `${current}`;
|
|
23
|
+
|
|
24
|
+
if (total && Number(total) !== Number(current)) {
|
|
25
|
+
content += ` ${i18n('of')} ${total}`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Label theme="info" size="m" className={className}>
|
|
31
|
+
{content}
|
|
32
|
+
</Label>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {i18n, Lang} from '
|
|
1
|
+
import {i18n, Lang} from '../../../utils/i18n';
|
|
2
2
|
|
|
3
3
|
import en from './en.json';
|
|
4
4
|
import ru from './ru.json';
|
|
5
5
|
|
|
6
|
-
const COMPONENT = '
|
|
6
|
+
const COMPONENT = 'ydb-entities-count';
|
|
7
7
|
|
|
8
8
|
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
|
9
9
|
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './EntitiesCount';
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
.kv-fullscreen {
|
|
2
|
-
|
|
2
|
+
// should expand to fill the content area, keeping aside navigation visible
|
|
3
|
+
// counts on .ycn-aside-header__content to have position: relative, it is set in App.scss
|
|
4
|
+
position: absolute;
|
|
3
5
|
z-index: 10;
|
|
4
6
|
top: 0;
|
|
5
|
-
|
|
7
|
+
right: 0;
|
|
8
|
+
bottom: 0;
|
|
9
|
+
left: 0;
|
|
6
10
|
|
|
7
11
|
display: flex;
|
|
8
12
|
overflow: hidden;
|
|
9
13
|
flex-grow: 1;
|
|
10
14
|
|
|
11
|
-
width: calc(100% - var(--nv-aside-header-size));
|
|
12
|
-
height: 100%;
|
|
13
|
-
|
|
14
15
|
background-color: var(--yc-color-base-background);
|
|
15
16
|
|
|
16
17
|
&__close-button {
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
z-index: 11;
|
|
19
20
|
top: 8px;
|
|
20
21
|
right: 20px;
|
|
22
|
+
|
|
21
23
|
.yc-button__text {
|
|
22
24
|
display: flex;
|
|
23
25
|
align-items: center;
|
|
@@ -25,7 +25,7 @@ const b = cn('kv-tablets-overall');
|
|
|
25
25
|
type Color = keyof typeof colors;
|
|
26
26
|
|
|
27
27
|
interface TabletsOverallProps {
|
|
28
|
-
tablets: {Overall
|
|
28
|
+
tablets: {Overall?: string}[];
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
function TabletsOverall({tablets}: TabletsOverallProps) {
|
|
@@ -48,10 +48,10 @@ function TabletsOverall({tablets}: TabletsOverallProps) {
|
|
|
48
48
|
|
|
49
49
|
// determine how many tablets of what color are in "tablets"
|
|
50
50
|
const statesForOverallProgress: Record<string, number> = tablets.reduce((acc, tablet) => {
|
|
51
|
-
const color = tablet.Overall
|
|
52
|
-
if (!acc[color]) {
|
|
51
|
+
const color = tablet.Overall?.toLowerCase();
|
|
52
|
+
if (color && !acc[color]) {
|
|
53
53
|
acc[color] = 1;
|
|
54
|
-
} else {
|
|
54
|
+
} else if (color) {
|
|
55
55
|
acc[color]++;
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import cn from 'bem-cn-lite';
|
|
2
|
+
import {Link} from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import {getTabletLabel} from '../../utils/constants';
|
|
5
|
+
import {mapTabletStateToColorState} from '../../utils/tablet';
|
|
6
|
+
import routes, {createHref} from '../../routes';
|
|
7
|
+
|
|
8
|
+
import type {TTabletStateInfo as TFullTabletStateInfo} from '../../types/api/tablet';
|
|
9
|
+
import type {TTabletStateInfo as TComputeTabletStateInfo} from '../../types/api/compute';
|
|
10
|
+
|
|
11
|
+
import './TabletsStatistic.scss';
|
|
12
|
+
|
|
13
|
+
const b = cn('tablets-statistic');
|
|
14
|
+
|
|
15
|
+
type ITablets = TFullTabletStateInfo[] | TComputeTabletStateInfo[];
|
|
16
|
+
|
|
17
|
+
const prepareTablets = (tablets: ITablets) => {
|
|
18
|
+
const res = tablets.map((tablet) => {
|
|
19
|
+
return {
|
|
20
|
+
label: getTabletLabel(tablet.Type),
|
|
21
|
+
type: tablet.Type,
|
|
22
|
+
count: tablet.Count,
|
|
23
|
+
state: mapTabletStateToColorState(tablet.State),
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return res.sort((x, y) => String(x.label).localeCompare(String(y.label)));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
interface TabletsStatisticProps {
|
|
31
|
+
tablets: ITablets;
|
|
32
|
+
path: string;
|
|
33
|
+
nodeIds: string[] | number[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const TabletsStatistic = ({tablets = [], path, nodeIds}: TabletsStatisticProps) => {
|
|
37
|
+
const renderTabletInfo = (item: ReturnType<typeof prepareTablets>[number], index: number) => {
|
|
38
|
+
return (
|
|
39
|
+
<Link
|
|
40
|
+
to={createHref(routes.tabletsFilters, undefined, {
|
|
41
|
+
nodeIds,
|
|
42
|
+
state: item.state,
|
|
43
|
+
type: item.type,
|
|
44
|
+
path,
|
|
45
|
+
})}
|
|
46
|
+
key={index}
|
|
47
|
+
className={b('tablet', {state: item.state?.toLowerCase()})}
|
|
48
|
+
>
|
|
49
|
+
{item.label}: {item.count}
|
|
50
|
+
</Link>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
const preparedTablets = prepareTablets(tablets);
|
|
54
|
+
|
|
55
|
+
return <div className={b()}>{preparedTablets.map(renderTabletInfo)}</div>;
|
|
56
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TabletsStatistic';
|
|
@@ -47,11 +47,13 @@ body,
|
|
|
47
47
|
color: var(--yc-color-text-secondary);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
.
|
|
50
|
+
.ycn-aside-header__pane-container {
|
|
51
51
|
height: 100%;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
.
|
|
54
|
+
.ycn-aside-header__content {
|
|
55
|
+
position: relative;
|
|
56
|
+
|
|
55
57
|
display: flex;
|
|
56
58
|
overflow: auto;
|
|
57
59
|
flex-direction: column;
|
|
@@ -116,17 +118,7 @@ body,
|
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
.error {
|
|
119
|
-
display: flex;
|
|
120
|
-
justify-content: center;
|
|
121
|
-
align-items: center;
|
|
122
|
-
|
|
123
|
-
margin: 2px;
|
|
124
|
-
padding: 2px;
|
|
125
|
-
|
|
126
|
-
text-align: center;
|
|
127
|
-
|
|
128
121
|
color: var(--yc-color-text-danger);
|
|
129
|
-
border-color: var(--yc-color-text-danger);
|
|
130
122
|
}
|
|
131
123
|
|
|
132
124
|
.data-table__row:hover .entity-status__clipboard-button {
|
|
@@ -23,21 +23,3 @@
|
|
|
23
23
|
padding: 10px;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
.nv-aside-header-footer-item__popup {
|
|
28
|
-
.nv-user-menu-content__organizations {
|
|
29
|
-
margin-top: 19px;
|
|
30
|
-
margin-left: 0;
|
|
31
|
-
.yc-menu__item {
|
|
32
|
-
cursor: auto;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.yc-menu__item {
|
|
37
|
-
//stylelint-disable-next-line declaration-no-important
|
|
38
|
-
padding-right: 0 !important;
|
|
39
|
-
&:hover {
|
|
40
|
-
background-color: unset;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import React, {useState} from 'react';
|
|
1
|
+
import React, {useEffect, useState} from 'react';
|
|
2
|
+
import {connect} from 'react-redux';
|
|
3
|
+
import {useLocation} from 'react-router';
|
|
2
4
|
import {useHistory} from 'react-router-dom';
|
|
3
5
|
import cn from 'bem-cn-lite';
|
|
4
|
-
import {useLocation} from 'react-router';
|
|
5
|
-
import {connect} from 'react-redux';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
AsideHeader,
|
|
9
|
-
AsideHeaderFooterItem,
|
|
10
|
-
AsideHeaderMenuItem,
|
|
11
|
-
SlotName,
|
|
12
|
-
} from '../../components/AsideNavigation/AsideHeader';
|
|
13
7
|
import {Icon, Button} from '@gravity-ui/uikit';
|
|
8
|
+
import {AsideHeader, MenuItem as AsideHeaderMenuItem, FooterItem} from '@gravity-ui/navigation';
|
|
9
|
+
|
|
14
10
|
import signOutIcon from '../../assets/icons/signOut.svg';
|
|
15
11
|
import signInIcon from '../../assets/icons/signIn.svg';
|
|
16
12
|
import databaseIcon from '../../assets/icons/server.svg';
|
|
@@ -20,12 +16,17 @@ import ydbLogoIcon from '../../assets/icons/ydb.svg';
|
|
|
20
16
|
import databasesIcon from '../../assets/icons/databases.svg';
|
|
21
17
|
import userSecret from '../../assets/icons/user-secret.svg';
|
|
22
18
|
import userChecked from '../../assets/icons/user-check.svg';
|
|
23
|
-
|
|
19
|
+
import settingsIcon from '../../assets/icons/settings.svg';
|
|
20
|
+
import supportIcon from '../../assets/icons/support.svg';
|
|
21
|
+
|
|
24
22
|
import UserSettings from '../UserSettings/UserSettings';
|
|
23
|
+
|
|
25
24
|
import routes, {createHref, CLUSTER_PAGES} from '../../routes';
|
|
26
25
|
|
|
27
|
-
//@ts-ignore
|
|
28
26
|
import {logout, setIsNotAuthenticated} from '../../store/reducers/authentication';
|
|
27
|
+
import {getSettingValue, setSettingValue} from '../../store/reducers/settings';
|
|
28
|
+
|
|
29
|
+
import {ASIDE_HEADER_COMPACT_KEY} from '../../utils/constants';
|
|
29
30
|
|
|
30
31
|
import './AsideNavigation.scss';
|
|
31
32
|
|
|
@@ -77,17 +78,21 @@ interface YdbUserDropdownProps {
|
|
|
77
78
|
|
|
78
79
|
function YdbUserDropdown({isCompact, popupAnchor, ydbUser}: YdbUserDropdownProps) {
|
|
79
80
|
const [isUserDropdownVisible, setIsUserDropdownVisible] = useState(false);
|
|
80
|
-
const
|
|
81
|
+
const iconData = ydbUser?.login ? userChecked : userSecret;
|
|
81
82
|
return (
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
<FooterItem
|
|
84
|
+
compact={isCompact}
|
|
85
|
+
item={{
|
|
86
|
+
id: 'user-popup',
|
|
87
|
+
title: ydbUser?.login ?? 'Account',
|
|
88
|
+
current: isUserDropdownVisible,
|
|
89
|
+
icon: iconData,
|
|
90
|
+
iconSize: 22,
|
|
91
|
+
onItemClick: () => setIsUserDropdownVisible(true),
|
|
92
|
+
}}
|
|
93
|
+
enableTooltip={!isUserDropdownVisible}
|
|
88
94
|
popupAnchor={popupAnchor}
|
|
89
95
|
popupVisible={isUserDropdownVisible}
|
|
90
|
-
onClick={() => setIsUserDropdownVisible(true)}
|
|
91
96
|
onClosePopup={() => setIsUserDropdownVisible(false)}
|
|
92
97
|
renderPopupContent={() => (
|
|
93
98
|
<div className={b('ydb-user-wrapper')}>
|
|
@@ -105,8 +110,10 @@ function YdbUserDropdown({isCompact, popupAnchor, ydbUser}: YdbUserDropdownProps
|
|
|
105
110
|
interface AsideNavigationProps {
|
|
106
111
|
children: React.ReactNode;
|
|
107
112
|
ydbUser: string;
|
|
113
|
+
compact: boolean;
|
|
108
114
|
logout: VoidFunction;
|
|
109
115
|
setIsNotAuthenticated: VoidFunction;
|
|
116
|
+
setSettingValue: (name: string, value: string) => void;
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
const items: MenuItem[] = [
|
|
@@ -150,10 +157,30 @@ const items: MenuItem[] = [
|
|
|
150
157
|
},
|
|
151
158
|
];
|
|
152
159
|
|
|
160
|
+
enum Panel {
|
|
161
|
+
UserSettings = 'UserSettings',
|
|
162
|
+
}
|
|
163
|
+
|
|
153
164
|
function AsideNavigation(props: AsideNavigationProps) {
|
|
154
165
|
const location = useLocation();
|
|
155
166
|
const history = useHistory();
|
|
156
167
|
|
|
168
|
+
const [visiblePanel, setVisiblePanel] = useState<Panel>();
|
|
169
|
+
|
|
170
|
+
const setIsCompact = (compact: boolean) => {
|
|
171
|
+
props.setSettingValue(ASIDE_HEADER_COMPACT_KEY, JSON.stringify(compact));
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// navigation managed its compact state internally before, and its approach is not compatible with settings
|
|
175
|
+
// to migrate, save the incoming value again; save only `false` because `true` is the default value
|
|
176
|
+
// assume it is safe to remove this code block if it is at least a few months old
|
|
177
|
+
// there a two of these, search for a similar comment
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (props.compact === false) {
|
|
180
|
+
setIsCompact(props.compact);
|
|
181
|
+
}
|
|
182
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
183
|
+
|
|
157
184
|
const menuItems: AsideHeaderMenuItem[] = React.useMemo(() => {
|
|
158
185
|
const {pathname} = location;
|
|
159
186
|
const isClusterPage = pathname === '/cluster';
|
|
@@ -184,28 +211,51 @@ function AsideNavigation(props: AsideNavigationProps) {
|
|
|
184
211
|
return (
|
|
185
212
|
<React.Fragment>
|
|
186
213
|
<AsideHeader
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
214
|
+
logo={{
|
|
215
|
+
text: 'YDB',
|
|
216
|
+
icon: ydbLogoIcon,
|
|
217
|
+
onClick: () => history.push('/'),
|
|
218
|
+
}}
|
|
190
219
|
menuItems={menuItems}
|
|
191
|
-
|
|
192
|
-
|
|
220
|
+
compact={props.compact}
|
|
221
|
+
onChangeCompact={setIsCompact}
|
|
193
222
|
className={b()}
|
|
194
223
|
renderContent={() => props.children}
|
|
195
|
-
renderFooter={({
|
|
224
|
+
renderFooter={({compact, asideRef}) => (
|
|
196
225
|
<React.Fragment>
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
226
|
+
<FooterItem
|
|
227
|
+
compact={compact}
|
|
228
|
+
item={{
|
|
229
|
+
id: 'documentation',
|
|
230
|
+
title: 'Documentation',
|
|
231
|
+
icon: supportIcon,
|
|
232
|
+
iconSize: 24,
|
|
233
|
+
onItemClick: () => {
|
|
234
|
+
window.open('https://ydb.tech/docs', '_blank', 'noreferrer');
|
|
235
|
+
},
|
|
236
|
+
}}
|
|
237
|
+
/>
|
|
238
|
+
|
|
239
|
+
<FooterItem
|
|
240
|
+
item={{
|
|
241
|
+
id: 'user-settings',
|
|
242
|
+
title: 'Settings',
|
|
243
|
+
icon: settingsIcon,
|
|
244
|
+
iconSize: 24,
|
|
245
|
+
current: visiblePanel === Panel.UserSettings,
|
|
246
|
+
onItemClick: () => {
|
|
247
|
+
setVisiblePanel(
|
|
248
|
+
visiblePanel === Panel.UserSettings
|
|
249
|
+
? undefined
|
|
250
|
+
: Panel.UserSettings,
|
|
251
|
+
);
|
|
252
|
+
},
|
|
204
253
|
}}
|
|
254
|
+
compact={compact}
|
|
205
255
|
/>
|
|
206
256
|
|
|
207
257
|
<YdbUserDropdown
|
|
208
|
-
isCompact={
|
|
258
|
+
isCompact={compact}
|
|
209
259
|
popupAnchor={asideRef}
|
|
210
260
|
ydbUser={{
|
|
211
261
|
login: props.ydbUser,
|
|
@@ -215,6 +265,16 @@ function AsideNavigation(props: AsideNavigationProps) {
|
|
|
215
265
|
/>
|
|
216
266
|
</React.Fragment>
|
|
217
267
|
)}
|
|
268
|
+
panelItems={[
|
|
269
|
+
{
|
|
270
|
+
id: 'user-settings',
|
|
271
|
+
visible: visiblePanel === Panel.UserSettings,
|
|
272
|
+
content: <UserSettings />,
|
|
273
|
+
},
|
|
274
|
+
]}
|
|
275
|
+
onClosePanel={() => {
|
|
276
|
+
setVisiblePanel(undefined);
|
|
277
|
+
}}
|
|
218
278
|
/>
|
|
219
279
|
</React.Fragment>
|
|
220
280
|
);
|
|
@@ -225,12 +285,14 @@ const mapStateToProps = (state: any) => {
|
|
|
225
285
|
|
|
226
286
|
return {
|
|
227
287
|
ydbUser,
|
|
288
|
+
compact: JSON.parse(getSettingValue(state, ASIDE_HEADER_COMPACT_KEY)),
|
|
228
289
|
};
|
|
229
290
|
};
|
|
230
291
|
|
|
231
292
|
const mapDispatchToProps = {
|
|
232
293
|
logout,
|
|
233
294
|
setIsNotAuthenticated,
|
|
295
|
+
setSettingValue,
|
|
234
296
|
};
|
|
235
297
|
|
|
236
298
|
export default connect(mapStateToProps, mapDispatchToProps)(AsideNavigation);
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import React, {useCallback, useEffect, useState} from 'react';
|
|
2
|
+
import {useDispatch} from 'react-redux';
|
|
3
|
+
import cn from 'bem-cn-lite';
|
|
4
|
+
|
|
5
|
+
import {Checkbox, Select} from '@gravity-ui/uikit';
|
|
6
|
+
|
|
7
|
+
import {getTabletsInfo, setHeatmapOptions} from '../../store/reducers/heatmap';
|
|
8
|
+
import {showTooltip, hideTooltip} from '../../store/reducers/tooltip';
|
|
9
|
+
import {formatNumber} from '../../utils';
|
|
10
|
+
import {prepareQueryError} from '../../utils/query';
|
|
11
|
+
import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
|
|
12
|
+
import {Loader} from '../../components/Loader';
|
|
13
|
+
import type {IHeatmapMetricValue} from '../../types/store/heatmap';
|
|
14
|
+
|
|
15
|
+
import {COLORS_RANGE_SIZE, getColorRange, getColorIndex, getCurrentMetricLimits} from './util';
|
|
16
|
+
import {HeatmapCanvas} from './HeatmapCanvas/HeatmapCanvas';
|
|
17
|
+
import {Histogram} from './Histogram/Histogram';
|
|
18
|
+
|
|
19
|
+
import './Heatmap.scss';
|
|
20
|
+
|
|
21
|
+
const b = cn('heatmap');
|
|
22
|
+
const COLORS_RANGE = getColorRange(COLORS_RANGE_SIZE);
|
|
23
|
+
|
|
24
|
+
interface HeatmapProps {
|
|
25
|
+
path: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Heatmap = ({path}: HeatmapProps) => {
|
|
29
|
+
const dispatch = useDispatch();
|
|
30
|
+
|
|
31
|
+
const itemsContainer = React.createRef<HTMLDivElement>();
|
|
32
|
+
|
|
33
|
+
const {autorefresh} = useTypedSelector((state) => state.schema);
|
|
34
|
+
const {
|
|
35
|
+
loading,
|
|
36
|
+
wasLoaded,
|
|
37
|
+
error,
|
|
38
|
+
sort,
|
|
39
|
+
heatmap,
|
|
40
|
+
metrics,
|
|
41
|
+
currentMetric,
|
|
42
|
+
data: tablets = [],
|
|
43
|
+
} = useTypedSelector((state) => state.heatmap);
|
|
44
|
+
|
|
45
|
+
const [selectedMetric, setSelectedMetric] = useState(['']);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!currentMetric && metrics && metrics.length) {
|
|
49
|
+
dispatch(
|
|
50
|
+
setHeatmapOptions({
|
|
51
|
+
currentMetric: metrics[0].value,
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (currentMetric) {
|
|
56
|
+
setSelectedMetric([currentMetric]);
|
|
57
|
+
}
|
|
58
|
+
}, [currentMetric, metrics, dispatch]);
|
|
59
|
+
|
|
60
|
+
const fetchData = useCallback(
|
|
61
|
+
(isBackground: boolean) => {
|
|
62
|
+
if (!isBackground) {
|
|
63
|
+
dispatch(setHeatmapOptions({wasLoaded: false}));
|
|
64
|
+
}
|
|
65
|
+
dispatch(getTabletsInfo({path}));
|
|
66
|
+
},
|
|
67
|
+
[path, dispatch],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
useAutofetcher(fetchData, [fetchData], autorefresh);
|
|
71
|
+
|
|
72
|
+
const onShowTooltip = (...args: Parameters<typeof showTooltip>) => {
|
|
73
|
+
dispatch(showTooltip(...args));
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const onHideTooltip = () => {
|
|
77
|
+
dispatch(hideTooltip);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const handleMetricChange = (value: string[]) => {
|
|
81
|
+
dispatch(
|
|
82
|
+
setHeatmapOptions({
|
|
83
|
+
currentMetric: value[0] as IHeatmapMetricValue,
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
const handleCheckboxChange = () => {
|
|
88
|
+
dispatch(
|
|
89
|
+
setHeatmapOptions({
|
|
90
|
+
sort: !sort,
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const handleHeatmapChange = () => {
|
|
96
|
+
dispatch(
|
|
97
|
+
setHeatmapOptions({
|
|
98
|
+
heatmap: !heatmap,
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const renderHistogram = () => {
|
|
104
|
+
return (
|
|
105
|
+
<Histogram
|
|
106
|
+
tablets={tablets}
|
|
107
|
+
currentMetric={currentMetric}
|
|
108
|
+
showTooltip={onShowTooltip}
|
|
109
|
+
hideTooltip={onHideTooltip}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const renderHeatmapCanvas = () => {
|
|
115
|
+
const {min, max} = getCurrentMetricLimits(currentMetric, tablets);
|
|
116
|
+
|
|
117
|
+
const preparedTablets = tablets.map((tablet) => {
|
|
118
|
+
const value = currentMetric && Number(tablet.metrics?.[currentMetric]);
|
|
119
|
+
const colorIndex = getColorIndex(value, min, max);
|
|
120
|
+
const color = COLORS_RANGE[colorIndex];
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
...tablet,
|
|
124
|
+
color,
|
|
125
|
+
value,
|
|
126
|
+
formattedValue: formatNumber(value),
|
|
127
|
+
currentMetric,
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
const sortedTablets = sort
|
|
131
|
+
? preparedTablets.sort((x, y) => Number(y.value) - Number(x.value))
|
|
132
|
+
: preparedTablets;
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div ref={itemsContainer} className={b('items')}>
|
|
136
|
+
<HeatmapCanvas
|
|
137
|
+
tablets={sortedTablets}
|
|
138
|
+
parentRef={itemsContainer}
|
|
139
|
+
showTooltip={onShowTooltip}
|
|
140
|
+
hideTooltip={onHideTooltip}
|
|
141
|
+
currentMetric={currentMetric}
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const renderContent = () => {
|
|
148
|
+
const {min, max} = getCurrentMetricLimits(currentMetric, tablets);
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<div className={b()}>
|
|
152
|
+
<div className={b('filters')}>
|
|
153
|
+
<Select
|
|
154
|
+
className={b('heatmap-select')}
|
|
155
|
+
value={selectedMetric}
|
|
156
|
+
options={metrics}
|
|
157
|
+
onUpdate={handleMetricChange}
|
|
158
|
+
width={200}
|
|
159
|
+
/>
|
|
160
|
+
<div className={b('sort-checkbox')}>
|
|
161
|
+
<Checkbox onUpdate={handleCheckboxChange} checked={sort}>
|
|
162
|
+
Sort
|
|
163
|
+
</Checkbox>
|
|
164
|
+
</div>
|
|
165
|
+
<div className={b('histogram-checkbox')}>
|
|
166
|
+
<Checkbox onUpdate={handleHeatmapChange} checked={heatmap}>
|
|
167
|
+
Heatmap
|
|
168
|
+
</Checkbox>
|
|
169
|
+
</div>
|
|
170
|
+
<div className={b('limits')}>
|
|
171
|
+
<div className={b('limits-block')}>
|
|
172
|
+
<div className={b('limits-title')}>min:</div>
|
|
173
|
+
<div className={b('limits-value')}>
|
|
174
|
+
{Number.isInteger(min) ? formatNumber(min) : '—'}
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div className={b('limits-block')}>
|
|
178
|
+
<div className={b('limits-title')}>max:</div>
|
|
179
|
+
<div className={b('limits-value')}>
|
|
180
|
+
{Number.isInteger(max) ? formatNumber(max) : '—'}
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
<div className={b('limits-block')}>
|
|
184
|
+
<div className={b('limits-title')}>count:</div>
|
|
185
|
+
<div className={b('limits-value')}>{formatNumber(tablets.length)}</div>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
{heatmap ? renderHeatmapCanvas() : renderHistogram()}
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
if (loading && !wasLoaded) {
|
|
195
|
+
return <Loader />;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (error) {
|
|
199
|
+
return <div>{prepareQueryError(error)}</div>;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return renderContent();
|
|
203
|
+
};
|