ydb-embedded-ui 3.3.1 → 3.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/components/Errors/ResponseError/ResponseError.tsx +17 -0
  3. package/dist/components/Errors/ResponseError/index.ts +1 -0
  4. package/dist/components/Errors/i18n/en.json +2 -1
  5. package/dist/components/Errors/i18n/ru.json +2 -1
  6. package/dist/components/FullGroupViewer/FullGroupViewer.js +1 -1
  7. package/dist/components/InfoViewer/InfoViewer.scss +1 -1
  8. package/dist/components/InfoViewer/InfoViewer.tsx +29 -21
  9. package/dist/components/InfoViewer/formatters/index.ts +1 -0
  10. package/dist/components/InfoViewer/formatters/table.ts +40 -0
  11. package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +26 -8
  12. package/dist/components/QueryExecutionStatus/index.ts +1 -0
  13. package/dist/components/QueryResultTable/QueryResultTable.tsx +2 -2
  14. package/dist/containers/App/Content.js +12 -5
  15. package/dist/containers/AsideNavigation/AsideNavigation.tsx +10 -13
  16. package/dist/containers/Authentication/Authentication.scss +6 -0
  17. package/dist/containers/Authentication/Authentication.tsx +34 -15
  18. package/dist/containers/Node/NodeStructure/Pdisk.tsx +7 -10
  19. package/dist/containers/Nodes/Nodes.tsx +1 -1
  20. package/dist/containers/Nodes/getNodesColumns.tsx +4 -4
  21. package/dist/containers/Storage/PDisk/PDisk.tsx +25 -17
  22. package/dist/containers/Storage/PDisk/__tests__/colors.tsx +64 -1
  23. package/dist/containers/Storage/Storage.js +1 -1
  24. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +4 -3
  25. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -1
  26. package/dist/containers/Storage/VDisk/VDisk.tsx +1 -1
  27. package/dist/containers/Storage/utils/index.ts +26 -10
  28. package/dist/containers/Tablet/Tablet.js +1 -1
  29. package/dist/containers/Tenant/Acl/Acl.js +1 -1
  30. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +2 -2
  31. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  32. package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.tsx +6 -1
  33. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +10 -21
  34. package/dist/containers/Tenant/{Schema/SchemaInfoViewer/SchemaInfoViewer.scss → Diagnostics/Overview/TableInfo/TableInfo.scss} +8 -10
  35. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/TableInfo.tsx +71 -0
  36. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +5 -0
  37. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.ts +11 -0
  38. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/ru.json +5 -0
  39. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/index.ts +1 -0
  40. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +96 -0
  41. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +7 -1
  42. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -1
  43. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +8 -3
  44. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +16 -11
  45. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +37 -23
  46. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +4 -0
  47. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
  48. package/dist/containers/Tenants/Tenants.js +4 -3
  49. package/dist/routes.ts +1 -0
  50. package/dist/services/api.js +4 -1
  51. package/dist/store/reducers/shardsWorkload.ts +2 -1
  52. package/dist/store/reducers/storage.js +1 -1
  53. package/dist/utils/constants.ts +1 -1
  54. package/dist/utils/index.js +3 -1
  55. package/dist/utils/prepareQueryExplain.ts +1 -1
  56. package/package.json +5 -3
  57. package/dist/containers/Tenant/Diagnostics/Overview/Overview.scss +0 -13
  58. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +0 -201
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.2...v3.3.3) (2023-02-08)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **Auth:** add a step in history for auth form ([c72d06e](https://github.com/ydb-platform/ydb-embedded-ui/commit/c72d06ecacdba47cac59bd705c1185e1ddf0b20d))
9
+ * format dates with date-utils ([948598b](https://github.com/ydb-platform/ydb-embedded-ui/commit/948598b83c9bdd09268d128e15a42d5a6e0c15cc))
10
+ * **InfoViewer:** add prop renderEmptyState ([44fe28f](https://github.com/ydb-platform/ydb-embedded-ui/commit/44fe28f72ea299b3b5d9b5a33a0a0130d471f7dd))
11
+ * minor fixes in Nodes and Tenants tables ([8dca43a](https://github.com/ydb-platform/ydb-embedded-ui/commit/8dca43a482b0da31dbc618875b416dcfcedac036))
12
+ * **OverloadedShards:** display IntervalEnd ([c7cbd72](https://github.com/ydb-platform/ydb-embedded-ui/commit/c7cbd7215eaf60601941410acb13ffb25d151eb9))
13
+ * **Overview:** display error statusText on schema error ([99b030f](https://github.com/ydb-platform/ydb-embedded-ui/commit/99b030f90e6044e98a151e5128603835c84e1b4e))
14
+ * **PDisk:** calculate severity based on usage ([64c6890](https://github.com/ydb-platform/ydb-embedded-ui/commit/64c6890ac6d5a77aef73da7dfc7f1eaff8a72441))
15
+ * **QueryEditor:** make client request timeout 9 min ([44528a8](https://github.com/ydb-platform/ydb-embedded-ui/commit/44528a865b039003cda4c7b1b1367840da015d09))
16
+ * **QueryEditor:** result status for aborted connection ([4b0d84b](https://github.com/ydb-platform/ydb-embedded-ui/commit/4b0d84b550deb41a140d4a3d215e52084507a558))
17
+ * **QueryResult:** output client error messages ([deef610](https://github.com/ydb-platform/ydb-embedded-ui/commit/deef6103f4d08825837520cab9e8ae5b8c7fd496))
18
+ * **Storage:** replace hasOwn to hasOwnProperty ([2452310](https://github.com/ydb-platform/ydb-embedded-ui/commit/2452310ce8e953d7a9ee4bbaa2bd466396aa0131))
19
+ * **TopQueries:** display IntervalEnd ([e5b2b07](https://github.com/ydb-platform/ydb-embedded-ui/commit/e5b2b07cf1e686c20817dcdc1ae32e0c8912a21a))
20
+
21
+ ## [3.3.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.1...v3.3.2) (2023-01-31)
22
+
23
+
24
+ ### Bug Fixes
25
+
26
+ * **QueryEditor:** collapse bottom panel if empty ([566db3b](https://github.com/ydb-platform/ydb-embedded-ui/commit/566db3b15c4393555071f058c88ad36b4073cc2d))
27
+ * **VDisk:** use pdiskid field for link ([5ee0705](https://github.com/ydb-platform/ydb-embedded-ui/commit/5ee0705416aa31be9bee4be0776ecb8a61d3e82c))
28
+
3
29
  ## [3.3.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.0...v3.3.1) (2023-01-31)
4
30
 
5
31
 
@@ -0,0 +1,17 @@
1
+ import type {IResponseError} from '../../../types/api/error';
2
+
3
+ import i18n from '../i18n';
4
+
5
+ interface ResponseErrorProps {
6
+ error: IResponseError;
7
+ className?: string;
8
+ defaultMessage?: string;
9
+ }
10
+
11
+ export const ResponseError = ({
12
+ error,
13
+ className,
14
+ defaultMessage = i18n('responseError.defaultMessage'),
15
+ }: ResponseErrorProps) => {
16
+ return <div className={`error ${className}`}>{error.statusText || defaultMessage}</div>;
17
+ };
@@ -0,0 +1 @@
1
+ export * from './ResponseError';
@@ -1,4 +1,5 @@
1
1
  {
2
2
  "403.title": "Access denied",
3
- "403.description": "You don’t have the necessary roles to view this page."
3
+ "403.description": "You don’t have the necessary roles to view this page.",
4
+ "responseError.defaultMessage": "Response error"
4
5
  }
@@ -1,4 +1,5 @@
1
1
  {
2
2
  "403.title": "Доступ запрещен",
3
- "403.description": "У вас недостаточно прав для просмотра данной страницы."
3
+ "403.description": "У вас недостаточно прав для просмотра данной страницы.",
4
+ "responseError.defaultMessage": "Ошибка запроса"
4
5
  }
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import PropTypes from 'prop-types';
4
4
 
5
- import DataTable from '@yandex-cloud/react-data-table';
5
+ import DataTable from '@gravity-ui/react-data-table';
6
6
 
7
7
  import InfoViewer from '../InfoViewer/InfoViewer';
8
8
  import EntityStatus from '../EntityStatus/EntityStatus';
@@ -64,7 +64,7 @@
64
64
  &__value {
65
65
  display: flex;
66
66
 
67
- min-width: 120px;
67
+ min-width: 130px;
68
68
 
69
69
  word-break: break-all;
70
70
  }
@@ -15,6 +15,7 @@ interface InfoViewerProps {
15
15
  size?: 's';
16
16
  className?: string;
17
17
  multilineLabels?: boolean;
18
+ renderEmptyState?: (props?: Pick<InfoViewerProps, 'title' | 'size'>) => ReactNode;
18
19
  }
19
20
 
20
21
  const b = cn('info-viewer');
@@ -26,28 +27,35 @@ const InfoViewer = ({
26
27
  size,
27
28
  className,
28
29
  multilineLabels,
29
- }: InfoViewerProps) => (
30
- <div className={b({size}, className)}>
31
- {title && <div className={b('title')}>{title}</div>}
32
- {info && info.length > 0 ? (
33
- <div className={b('items')}>
34
- {info.map((data, infoIndex) => (
35
- <div className={b('row')} key={data.label + infoIndex}>
36
- <div className={b('label')}>
37
- <div className={b('label-text', {multiline: multilineLabels})}>
38
- {data.label}
30
+ renderEmptyState,
31
+ }: InfoViewerProps) => {
32
+ if ((!info || !info.length) && renderEmptyState) {
33
+ return <>{renderEmptyState({title, size})}</>;
34
+ }
35
+
36
+ return (
37
+ <div className={b({size}, className)}>
38
+ {title && <div className={b('title')}>{title}</div>}
39
+ {info && info.length > 0 ? (
40
+ <div className={b('items')}>
41
+ {info.map((data, infoIndex) => (
42
+ <div className={b('row')} key={data.label + infoIndex}>
43
+ <div className={b('label')}>
44
+ <div className={b('label-text', {multiline: multilineLabels})}>
45
+ {data.label}
46
+ </div>
47
+ {dots && <div className={b('dots')} />}
39
48
  </div>
40
- {dots && <div className={b('dots')} />}
41
- </div>
42
49
 
43
- <div className={b('value')}>{data.value}</div>
44
- </div>
45
- ))}
46
- </div>
47
- ) : (
48
- <>No {title} data</>
49
- )}
50
- </div>
51
- );
50
+ <div className={b('value')}>{data.value}</div>
51
+ </div>
52
+ ))}
53
+ </div>
54
+ ) : (
55
+ <>No {title} data</>
56
+ )}
57
+ </div>
58
+ );
59
+ };
52
60
 
53
61
  export default InfoViewer;
@@ -3,3 +3,4 @@ export * from './schema';
3
3
  export * from './topicStats';
4
4
  export * from './pqGroup';
5
5
  export * from './cdcStream';
6
+ export * from './table';
@@ -0,0 +1,40 @@
1
+ import type {TFollowerGroup, TPartitionConfig, TTableStats} from '../../../types/api/schema';
2
+ import type {TMetrics} from '../../../types/api/tenant';
3
+ import {formatCPU, formatBytes, formatNumber, formatBps, formatDateTime} from '../../../utils';
4
+
5
+ import {createInfoFormatter} from '../utils';
6
+
7
+ export const formatTabletMetricsItem = createInfoFormatter<TMetrics>({
8
+ values: {
9
+ CPU: formatCPU,
10
+ Memory: formatBytes,
11
+ Storage: formatBytes,
12
+ Network: formatBps,
13
+ ReadThroughput: formatBps,
14
+ WriteThroughput: formatBps,
15
+ },
16
+ defaultValueFormatter: formatNumber,
17
+ });
18
+
19
+ export const formatFollowerGroupItem = createInfoFormatter<TFollowerGroup>({
20
+ values: {
21
+ FollowerCount: formatNumber,
22
+ },
23
+ });
24
+
25
+ export const formatPartitionConfigItem = createInfoFormatter<TPartitionConfig>({
26
+ values: {
27
+ FollowerCount: formatNumber,
28
+ CrossDataCenterFollowerCount: formatNumber,
29
+ },
30
+ });
31
+
32
+ export const formatTableStatsItem = createInfoFormatter<TTableStats>({
33
+ values: {
34
+ DataSize: formatBytes,
35
+ IndexSize: formatBytes,
36
+ LastAccessTime: formatDateTime,
37
+ LastUpdateTime: formatDateTime,
38
+ },
39
+ defaultValueFormatter: formatNumber,
40
+ });
@@ -1,19 +1,32 @@
1
+ import type {ReactNode} from 'react';
2
+ import type {AxiosError} from 'axios';
1
3
  import cn from 'bem-cn-lite';
2
4
 
5
+ import {Icon as UiKitIcon} from '@gravity-ui/uikit';
6
+
3
7
  import Icon from '../Icon/Icon';
4
8
 
9
+ import questionIcon from '../../assets/icons/question.svg';
10
+
5
11
  import './QueryExecutionStatus.scss';
6
12
 
7
13
  const b = cn('kv-query-execution-status');
8
14
 
9
15
  interface QueryExecutionStatusProps {
10
16
  className?: string;
11
- hasError?: boolean;
17
+ error?: AxiosError | Record<string, any>;
12
18
  }
13
19
 
14
- function QueryExecutionStatus({className, hasError}: QueryExecutionStatusProps) {
15
- return hasError === undefined ? null : (
16
- <div className={b(null, className)}>
20
+ export const QueryExecutionStatus = ({className, error}: QueryExecutionStatusProps) => {
21
+ let icon: ReactNode;
22
+ let label: string;
23
+
24
+ if (error?.code === 'ECONNABORTED') {
25
+ icon = <UiKitIcon data={questionIcon} size={16} />;
26
+ label = 'Connection aborted';
27
+ } else {
28
+ const hasError = Boolean(error);
29
+ icon = (
17
30
  <Icon
18
31
  name={hasError ? 'failure' : 'success'}
19
32
  viewBox={hasError ? '0 0 512 512' : '0 0 16 16'}
@@ -21,9 +34,14 @@ function QueryExecutionStatus({className, hasError}: QueryExecutionStatusProps)
21
34
  height={16}
22
35
  className={b('result-status-icon', {error: hasError})}
23
36
  />
24
- {hasError ? 'Failed' : 'Completed'}
37
+ );
38
+ label = hasError ? 'Failed' : 'Completed';
39
+ }
40
+
41
+ return (
42
+ <div className={b(null, className)}>
43
+ {icon}
44
+ {label}
25
45
  </div>
26
46
  );
27
- }
28
-
29
- export default QueryExecutionStatus;
47
+ };
@@ -0,0 +1 @@
1
+ export * from './QueryExecutionStatus';
@@ -1,8 +1,8 @@
1
1
  import {useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
- import DataTable from '@yandex-cloud/react-data-table';
5
- import type {Column, DataTableProps, Settings} from '@yandex-cloud/react-data-table';
4
+ import DataTable from '@gravity-ui/react-data-table';
5
+ import type {Column, DataTableProps, Settings} from '@gravity-ui/react-data-table';
6
6
 
7
7
  import type {ColumnType, KeyValueRow} from '../../types/api/query';
8
8
  import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
@@ -79,11 +79,18 @@ function ContentWrapper(props) {
79
79
  <HistoryContext.Consumer>
80
80
  {(history) => (
81
81
  <Router history={history}>
82
- <ThemeProvider theme={theme}>
83
- <div className={!singleClusterMode ? b() : b({embedded: true})}>
84
- {isAuthenticated ? props.children : <Authentication />}
85
- </div>
86
- </ThemeProvider>
82
+ <Switch>
83
+ <Route path={routes.auth}>
84
+ <Authentication closable />
85
+ </Route>
86
+ <Route>
87
+ <ThemeProvider theme={theme}>
88
+ <div className={b({embedded: singleClusterMode})}>
89
+ {isAuthenticated ? props.children : <Authentication />}
90
+ </div>
91
+ </ThemeProvider>
92
+ </Route>
93
+ </Switch>
87
94
  </Router>
88
95
  )}
89
96
  </HistoryContext.Consumer>
@@ -23,7 +23,7 @@ import UserSettings from '../UserSettings/UserSettings';
23
23
 
24
24
  import routes, {createHref, CLUSTER_PAGES} from '../../routes';
25
25
 
26
- import {logout, setIsNotAuthenticated} from '../../store/reducers/authentication';
26
+ import {logout} from '../../store/reducers/authentication';
27
27
  import {getSettingValue, setSettingValue} from '../../store/reducers/settings';
28
28
 
29
29
  import {ASIDE_HEADER_COMPACT_KEY} from '../../utils/constants';
@@ -43,10 +43,15 @@ interface MenuItem {
43
43
  interface YbdInternalUserProps {
44
44
  ydbUser?: string;
45
45
  logout: VoidFunction;
46
- setIsNotAuthenticated: VoidFunction;
47
46
  }
48
47
 
49
- function YbdInternalUser({ydbUser, logout, setIsNotAuthenticated}: YbdInternalUserProps) {
48
+ function YbdInternalUser({ydbUser, logout}: YbdInternalUserProps) {
49
+ const history = useHistory();
50
+
51
+ const handleLoginClick = () => {
52
+ history.push(createHref(routes.auth, undefined, {returnUrl: encodeURI(location.href)}));
53
+ };
54
+
50
55
  return (
51
56
  <div className={b('internal-user')}>
52
57
  <div className={b('user-info-wrapper')}>
@@ -58,7 +63,7 @@ function YbdInternalUser({ydbUser, logout, setIsNotAuthenticated}: YbdInternalUs
58
63
  <Icon data={signOutIcon} size={16} />
59
64
  </Button>
60
65
  ) : (
61
- <Button view="flat-secondary" onClick={setIsNotAuthenticated} title="login">
66
+ <Button view="flat-secondary" title="login" onClick={handleLoginClick}>
62
67
  <Icon data={signInIcon} size={16} />
63
68
  </Button>
64
69
  )}
@@ -71,7 +76,6 @@ interface YdbUserDropdownProps {
71
76
  ydbUser: {
72
77
  login?: string;
73
78
  logout: VoidFunction;
74
- setIsNotAuthenticated: VoidFunction;
75
79
  };
76
80
  popupAnchor: React.RefObject<HTMLDivElement>;
77
81
  }
@@ -96,11 +100,7 @@ function YdbUserDropdown({isCompact, popupAnchor, ydbUser}: YdbUserDropdownProps
96
100
  onClosePopup={() => setIsUserDropdownVisible(false)}
97
101
  renderPopupContent={() => (
98
102
  <div className={b('ydb-user-wrapper')}>
99
- <YbdInternalUser
100
- ydbUser={ydbUser.login}
101
- logout={ydbUser.logout}
102
- setIsNotAuthenticated={ydbUser.setIsNotAuthenticated}
103
- />
103
+ <YbdInternalUser ydbUser={ydbUser.login} logout={ydbUser.logout} />
104
104
  </div>
105
105
  )}
106
106
  />
@@ -112,7 +112,6 @@ interface AsideNavigationProps {
112
112
  ydbUser: string;
113
113
  compact: boolean;
114
114
  logout: VoidFunction;
115
- setIsNotAuthenticated: VoidFunction;
116
115
  setSettingValue: (name: string, value: string) => void;
117
116
  }
118
117
 
@@ -260,7 +259,6 @@ function AsideNavigation(props: AsideNavigationProps) {
260
259
  ydbUser={{
261
260
  login: props.ydbUser,
262
261
  logout: props.logout,
263
- setIsNotAuthenticated: props.setIsNotAuthenticated,
264
262
  }}
265
263
  />
266
264
  </React.Fragment>
@@ -291,7 +289,6 @@ const mapStateToProps = (state: any) => {
291
289
 
292
290
  const mapDispatchToProps = {
293
291
  logout,
294
- setIsNotAuthenticated,
295
292
  setSettingValue,
296
293
  };
297
294
 
@@ -83,4 +83,10 @@
83
83
  &__show-password-button {
84
84
  margin-left: 4px;
85
85
  }
86
+
87
+ &__close {
88
+ position: absolute;
89
+ top: 40px;
90
+ right: 40px;
91
+ }
86
92
  }
@@ -1,19 +1,33 @@
1
1
  import {KeyboardEvent, useEffect, useState} from 'react';
2
+ import {useDispatch} from 'react-redux';
3
+ import {useHistory} from 'react-router';
2
4
  import cn from 'bem-cn-lite';
3
- import {connect} from 'react-redux';
5
+
4
6
  import {Button, TextInput, Icon, Link as ExternalLink} from '@gravity-ui/uikit';
5
- //@ts-ignore
7
+
6
8
  import {authenticate} from '../../store/reducers/authentication';
9
+ import {useTypedSelector} from '../../utils/hooks';
7
10
 
8
11
  import ydbLogoIcon from '../../assets/icons/ydb.svg';
9
12
  import showIcon from '../../assets/icons/show.svg';
10
13
  import hideIcon from '../../assets/icons/hide.svg';
14
+ import closeIcon from '../../assets/icons/close.svg';
11
15
 
12
16
  import './Authentication.scss';
13
17
 
14
18
  const b = cn('authentication');
15
19
 
16
- function Authentication({authenticate, error}: any) {
20
+ interface AuthenticationProps {
21
+ returnUrl?: string;
22
+ closable?: boolean;
23
+ }
24
+
25
+ function Authentication({returnUrl, closable = false}: AuthenticationProps) {
26
+ const dispatch = useDispatch();
27
+ const history = useHistory();
28
+
29
+ const {error} = useTypedSelector((state) => state.authentication);
30
+
17
31
  const [login, setLogin] = useState('');
18
32
  const [pass, setPass] = useState('');
19
33
  const [loginError, setLoginError] = useState('');
@@ -40,7 +54,13 @@ function Authentication({authenticate, error}: any) {
40
54
  };
41
55
 
42
56
  const onLoginClick = () => {
43
- authenticate(login, pass);
57
+ // @ts-expect-error
58
+ // typed dispatch required, remove error expectation after adding it
59
+ dispatch(authenticate(login, pass)).then(() => {
60
+ if (returnUrl) {
61
+ history.replace(decodeURI(returnUrl));
62
+ }
63
+ });
44
64
  };
45
65
 
46
66
  const onEnterClick = (e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
@@ -49,6 +69,10 @@ function Authentication({authenticate, error}: any) {
49
69
  }
50
70
  };
51
71
 
72
+ const onClose = () => {
73
+ history.go(-1);
74
+ };
75
+
52
76
  const onTogglePasswordVisibility = () => {
53
77
  setShowPassword((prev) => !prev);
54
78
  };
@@ -106,18 +130,13 @@ function Authentication({authenticate, error}: any) {
106
130
  Sign in
107
131
  </Button>
108
132
  </form>
133
+ {closable && history.length > 1 && (
134
+ <Button onClick={onClose} className={b('close')}>
135
+ <Icon data={closeIcon} size={24} />
136
+ </Button>
137
+ )}
109
138
  </section>
110
139
  );
111
140
  }
112
141
 
113
- function mapStateToProps(state: any) {
114
- return {
115
- error: state.authentication.error,
116
- };
117
- }
118
-
119
- const mapDispatchToProps = {
120
- authenticate,
121
- };
122
-
123
- export default connect(mapStateToProps, mapDispatchToProps)(Authentication);
142
+ export default Authentication;
@@ -4,7 +4,7 @@ import _ from 'lodash';
4
4
 
5
5
  import {ArrowToggle, Button, Popover} from '@gravity-ui/uikit';
6
6
 
7
- import DataTable, {Column, Settings} from '@yandex-cloud/react-data-table';
7
+ import DataTable, {Column, Settings} from '@gravity-ui/react-data-table';
8
8
 
9
9
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
10
10
  import InfoViewer from '../../../components/InfoViewer/InfoViewer';
@@ -295,10 +295,7 @@ export function PDisk(props: PDiskProps) {
295
295
  value={data.PDiskId}
296
296
  className={b('pdisk-title-id')}
297
297
  />
298
- <PDiskTitleBadge
299
- value={getPDiskType(data)}
300
- className={b('pdisk-title-type')}
301
- />
298
+ <PDiskTitleBadge value={getPDiskType(data)} className={b('pdisk-title-type')} />
302
299
  <ProgressViewer
303
300
  value={data.TotalSize - data.AvailableSize}
304
301
  capacity={data.TotalSize}
@@ -306,12 +303,12 @@ export function PDisk(props: PDiskProps) {
306
303
  colorizeProgress={true}
307
304
  className={b('pdisk-title-size')}
308
305
  />
309
- <PDiskTitleBadge
310
- label="VDisks"
311
- value={data.vDisks.length}
312
- />
306
+ <PDiskTitleBadge label="VDisks" value={data.vDisks.length} />
313
307
  </div>
314
- <Button onClick={unfolded ? onClosePDiskDetails : onOpenPDiskDetails} view="flat-secondary">
308
+ <Button
309
+ onClick={unfolded ? onClosePDiskDetails : onOpenPDiskDetails}
310
+ view="flat-secondary"
311
+ >
315
312
  <ArrowToggle direction={unfolded ? 'top' : 'bottom'} />
316
313
  </Button>
317
314
  </div>
@@ -2,7 +2,7 @@ import {useCallback, useEffect} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {useDispatch} from 'react-redux';
4
4
 
5
- import DataTable from '@yandex-cloud/react-data-table';
5
+ import DataTable from '@gravity-ui/react-data-table';
6
6
 
7
7
  import {AccessDenied} from '../../components/Errors/403';
8
8
  import {Illustration} from '../../components/Illustration';
@@ -1,5 +1,5 @@
1
1
  import cn from 'bem-cn-lite';
2
- import DataTable, {Column} from '@yandex-cloud/react-data-table';
2
+ import DataTable, {Column} from '@gravity-ui/react-data-table';
3
3
  import {Button, Popover} from '@gravity-ui/uikit';
4
4
 
5
5
  import Icon from '../../components/Icon/Icon';
@@ -8,7 +8,7 @@ import PoolsGraph from '../../components/PoolsGraph/PoolsGraph';
8
8
  import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
9
9
  import {TabletsStatistic} from '../../components/TabletsStatistic';
10
10
 
11
- import {formatBytes} from '../../utils/index';
11
+ import {formatBytesToGigabyte} from '../../utils/index';
12
12
  import {INodesPreparedEntity} from '../../types/store/nodes';
13
13
  import {showTooltip as externalShowTooltip} from '../../store/reducers/tooltip';
14
14
 
@@ -100,7 +100,7 @@ export function getNodesColumns({
100
100
  name: 'Uptime',
101
101
  header: 'Uptime',
102
102
  sortAccessor: ({StartTime}) => StartTime && -StartTime,
103
- align: DataTable.LEFT,
103
+ align: DataTable.RIGHT,
104
104
  width: '110px',
105
105
  },
106
106
  {
@@ -110,7 +110,7 @@ export function getNodesColumns({
110
110
  defaultOrder: DataTable.DESCENDING,
111
111
  render: ({row}) => {
112
112
  if (row.MemoryUsed) {
113
- return formatBytes(row.MemoryUsed);
113
+ return formatBytesToGigabyte(row.MemoryUsed);
114
114
  } else {
115
115
  return '—';
116
116
  }
@@ -17,7 +17,7 @@ import {DiskStateProgressBar, EDiskStateSeverity} from '../DiskStateProgressBar'
17
17
  import {PDiskPopup} from '../PDiskPopup';
18
18
  import {VDisk} from '../VDisk';
19
19
 
20
- import {NOT_AVAILABLE_SEVERITY} from '../utils';
20
+ import {getUsageSeverityForPDisk, NOT_AVAILABLE_SEVERITY} from '../utils';
21
21
 
22
22
  import './PDisk.scss';
23
23
 
@@ -67,21 +67,6 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => {
67
67
 
68
68
  const anchor = useRef(null);
69
69
 
70
- useEffect(() => {
71
- const newSeverity = getStateSeverity(data.State);
72
- if (severity !== newSeverity) {
73
- setSeverity(newSeverity);
74
- }
75
- }, [data.State]);
76
-
77
- const showPopup = () => {
78
- setIsPopupVisible(true);
79
- };
80
-
81
- const hidePopup = () => {
82
- setIsPopupVisible(false);
83
- };
84
-
85
70
  const pdiskAllocatedPercent = useMemo(() => {
86
71
  const {AvailableSize, TotalSize} = data;
87
72
 
@@ -94,6 +79,29 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => {
94
79
  : undefined;
95
80
  }, [data]);
96
81
 
82
+ useEffect(() => {
83
+ const newStateSeverity = getStateSeverity(data.State);
84
+ const newSpaceSeverityFlag = getUsageSeverityForPDisk(pdiskAllocatedPercent || 0);
85
+
86
+ let newSeverity: number;
87
+
88
+ if (newStateSeverity === NOT_AVAILABLE_SEVERITY || !newSpaceSeverityFlag) {
89
+ newSeverity = newStateSeverity;
90
+ } else {
91
+ newSeverity = Math.max(newStateSeverity, EDiskStateSeverity[newSpaceSeverityFlag]);
92
+ }
93
+
94
+ setSeverity(newSeverity);
95
+ }, [data.State, pdiskAllocatedPercent]);
96
+
97
+ const showPopup = () => {
98
+ setIsPopupVisible(true);
99
+ };
100
+
101
+ const hidePopup = () => {
102
+ setIsPopupVisible(false);
103
+ };
104
+
97
105
  const renderVDisks = () => {
98
106
  if (!vdisks?.length) {
99
107
  return null;
@@ -108,7 +116,7 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => {
108
116
  style={{
109
117
  // 1 is small enough for empty disks to be of the minimum width
110
118
  // but if all of them are empty, `flex-grow: 1` would size them evenly
111
- flexGrow: (Number(vdisk.AllocatedSize) || 1),
119
+ flexGrow: Number(vdisk.AllocatedSize) || 1,
112
120
  }}
113
121
  >
114
122
  <VDisk data={vdisk} compact />