ydb-embedded-ui 3.3.2 → 3.3.4

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +27 -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 +12 -3
  25. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -1
  26. package/dist/containers/Storage/utils/index.ts +26 -10
  27. package/dist/containers/Tablet/Tablet.js +1 -1
  28. package/dist/containers/Tenant/Acl/Acl.js +1 -1
  29. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +2 -2
  30. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
  31. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +7 -7
  32. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -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/Diagnostics/TopShards/Filters/Filters.scss +8 -0
  43. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +56 -0
  44. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/index.ts +1 -0
  45. package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.scss → TopShards/TopShards.scss} +2 -10
  46. package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.tsx → TopShards/TopShards.tsx} +64 -29
  47. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +6 -0
  48. package/dist/containers/Tenant/Diagnostics/{OverloadedShards → TopShards}/i18n/index.ts +1 -1
  49. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +6 -0
  50. package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +1 -0
  51. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -1
  52. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +16 -11
  53. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +37 -23
  54. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +4 -0
  55. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
  56. package/dist/containers/Tenants/Tenants.js +4 -3
  57. package/dist/routes.ts +1 -0
  58. package/dist/services/api.js +4 -1
  59. package/dist/store/reducers/authentication.js +0 -15
  60. package/dist/store/reducers/shardsWorkload.ts +30 -3
  61. package/dist/store/reducers/storage.js +1 -1
  62. package/dist/store/state-url-mapping.js +3 -0
  63. package/dist/types/store/shardsWorkload.ts +6 -0
  64. package/dist/utils/constants.ts +1 -1
  65. package/dist/utils/index.js +3 -1
  66. package/dist/utils/prepareQueryExplain.ts +1 -1
  67. package/dist/utils/typecheckers.ts +5 -0
  68. package/package.json +5 -3
  69. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +0 -4
  70. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +0 -4
  71. package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +0 -1
  72. package/dist/containers/Tenant/Diagnostics/Overview/Overview.scss +0 -13
  73. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +0 -201
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.4](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.3...v3.3.4) (2023-02-16)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **OverloadedShards:** rename to top shards ([ffa4f27](https://github.com/ydb-platform/ydb-embedded-ui/commit/ffa4f27f2cf0a5e12b2800c81bf61b1d3c25912c))
9
+ * **StorageGroups:** display Erasure ([4a7ebc0](https://github.com/ydb-platform/ydb-embedded-ui/commit/4a7ebc08b87fe75af83df70a38ebd486d64d6d4e))
10
+ * **TopShards:** switch between history and immediate data ([eeb9bb0](https://github.com/ydb-platform/ydb-embedded-ui/commit/eeb9bb0911b9e889b633558c9d3c13f986f72bfe))
11
+
12
+ ## [3.3.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.2...v3.3.3) (2023-02-08)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **Auth:** add a step in history for auth form ([c72d06e](https://github.com/ydb-platform/ydb-embedded-ui/commit/c72d06ecacdba47cac59bd705c1185e1ddf0b20d))
18
+ * format dates with date-utils ([948598b](https://github.com/ydb-platform/ydb-embedded-ui/commit/948598b83c9bdd09268d128e15a42d5a6e0c15cc))
19
+ * **InfoViewer:** add prop renderEmptyState ([44fe28f](https://github.com/ydb-platform/ydb-embedded-ui/commit/44fe28f72ea299b3b5d9b5a33a0a0130d471f7dd))
20
+ * minor fixes in Nodes and Tenants tables ([8dca43a](https://github.com/ydb-platform/ydb-embedded-ui/commit/8dca43a482b0da31dbc618875b416dcfcedac036))
21
+ * **OverloadedShards:** display IntervalEnd ([c7cbd72](https://github.com/ydb-platform/ydb-embedded-ui/commit/c7cbd7215eaf60601941410acb13ffb25d151eb9))
22
+ * **Overview:** display error statusText on schema error ([99b030f](https://github.com/ydb-platform/ydb-embedded-ui/commit/99b030f90e6044e98a151e5128603835c84e1b4e))
23
+ * **PDisk:** calculate severity based on usage ([64c6890](https://github.com/ydb-platform/ydb-embedded-ui/commit/64c6890ac6d5a77aef73da7dfc7f1eaff8a72441))
24
+ * **QueryEditor:** make client request timeout 9 min ([44528a8](https://github.com/ydb-platform/ydb-embedded-ui/commit/44528a865b039003cda4c7b1b1367840da015d09))
25
+ * **QueryEditor:** result status for aborted connection ([4b0d84b](https://github.com/ydb-platform/ydb-embedded-ui/commit/4b0d84b550deb41a140d4a3d215e52084507a558))
26
+ * **QueryResult:** output client error messages ([deef610](https://github.com/ydb-platform/ydb-embedded-ui/commit/deef6103f4d08825837520cab9e8ae5b8c7fd496))
27
+ * **Storage:** replace hasOwn to hasOwnProperty ([2452310](https://github.com/ydb-platform/ydb-embedded-ui/commit/2452310ce8e953d7a9ee4bbaa2bd466396aa0131))
28
+ * **TopQueries:** display IntervalEnd ([e5b2b07](https://github.com/ydb-platform/ydb-embedded-ui/commit/e5b2b07cf1e686c20817dcdc1ae32e0c8912a21a))
29
+
3
30
  ## [3.3.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.1...v3.3.2) (2023-01-31)
4
31
 
5
32
 
@@ -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 />