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.
Files changed (78) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/assets/icons/shield.svg +3 -0
  3. package/dist/components/Errors/403/AccessDenied.tsx +19 -0
  4. package/dist/components/Errors/403/index.ts +1 -0
  5. package/dist/components/Errors/i18n/en.json +4 -0
  6. package/dist/components/Errors/i18n/index.ts +11 -0
  7. package/dist/components/Errors/i18n/ru.json +4 -0
  8. package/dist/components/NodesViewer/NodesViewer.js +1 -1
  9. package/dist/components/QueryResultTable/QueryResultTable.tsx +16 -21
  10. package/dist/{containers/Storage/StorageFilter/StorageFilter.tsx → components/Search/Search.tsx} +22 -22
  11. package/dist/components/Search/index.ts +1 -0
  12. package/dist/containers/App/App.scss +5 -1
  13. package/dist/containers/Nodes/Nodes.js +6 -1
  14. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +7 -5
  15. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +1 -11
  16. package/dist/containers/Storage/Pdisk/Pdisk.scss +15 -8
  17. package/dist/containers/Storage/Pdisk/Pdisk.tsx +22 -14
  18. package/dist/containers/Storage/Storage.js +39 -50
  19. package/dist/containers/Storage/StorageGroups/StorageGroups.scss +1 -4
  20. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +27 -2
  21. package/dist/containers/Storage/StorageGroups/i18n/en.json +2 -1
  22. package/dist/containers/Storage/StorageGroups/i18n/ru.json +2 -1
  23. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +14 -12
  24. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +7 -5
  25. package/dist/containers/Storage/Vdisk/Vdisk.js +36 -23
  26. package/dist/containers/Storage/Vdisk/Vdisk.scss +6 -0
  27. package/dist/containers/TabletsFilters/TabletsFilters.js +5 -0
  28. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +6 -0
  29. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +82 -0
  30. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +6 -0
  31. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/index.ts +11 -0
  32. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +6 -0
  33. package/dist/containers/Tenant/Diagnostics/Consumers/index.ts +1 -0
  34. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +7 -0
  35. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +29 -11
  36. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -8
  37. package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +55 -0
  38. package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/index.ts +1 -0
  39. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +5 -5
  40. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +16 -6
  41. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/{IssueViewer.scss → IssueTree.scss} +3 -54
  42. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTree.tsx +87 -0
  43. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/IssueTreeItem.scss +50 -0
  44. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/IssueTreeItem.tsx +25 -0
  45. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/index.ts +1 -0
  46. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +13 -16
  47. package/dist/containers/Tenant/Diagnostics/Healthcheck/{IssuePreview/IssuePreview.tsx → Preview/PreviewItem/PreviewItem.tsx} +6 -8
  48. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +1 -0
  49. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +34 -19
  50. package/dist/containers/Tenant/Preview/Preview.scss +6 -0
  51. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +1 -9
  52. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +2 -2
  53. package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +1 -1
  54. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +27 -20
  55. package/dist/containers/Tenant/Tenant.tsx +14 -16
  56. package/dist/containers/Tenants/Tenants.js +1 -1
  57. package/dist/store/reducers/describe.ts +71 -0
  58. package/dist/store/reducers/healthcheckInfo.ts +123 -0
  59. package/dist/store/reducers/olapStats.js +13 -0
  60. package/dist/store/reducers/schema.js +43 -1
  61. package/dist/store/reducers/storage.js +27 -17
  62. package/dist/store/reducers/tenant.js +3 -1
  63. package/dist/store/utils.ts +21 -13
  64. package/dist/styles/mixins.scss +1 -1
  65. package/dist/types/api/consumers.ts +3 -0
  66. package/dist/types/api/healthcheck.ts +1 -1
  67. package/dist/types/api/storage.ts +35 -10
  68. package/dist/types/store/healthcheck.ts +5 -1
  69. package/dist/types/store/storage.ts +1 -0
  70. package/dist/utils/hooks/useAutofetcher.ts +9 -3
  71. package/package.json +1 -1
  72. package/dist/containers/Storage/StorageFilter/index.ts +0 -1
  73. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuePreview/index.ts +0 -1
  74. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/IssuesList.tsx +0 -62
  75. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/index.ts +0 -1
  76. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssuesViewer.js +0 -151
  77. package/dist/store/reducers/describe.js +0 -45
  78. 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,4 @@
1
+ {
2
+ "403.title": "Access denied",
3
+ "403.description": "You don’t have the necessary roles to view this page."
4
+ }
@@ -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);
@@ -0,0 +1,4 @@
1
+ {
2
+ "403.title": "Доступ запрещен",
3
+ "403.description": "У вас недостаточно прав для просмотра данной страницы."
4
+ }
@@ -103,7 +103,7 @@ class NodesViewer extends React.PureComponent {
103
103
  className={b('search')}
104
104
  size="s"
105
105
  placeholder="Host name…"
106
- text={searchQuery}
106
+ value={searchQuery}
107
107
  onUpdate={handleSearchQuery}
108
108
  hasClear
109
109
  autoFocus
@@ -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 extends Omit<DataTableProps<KeyValueRow>, 'data' | 'columns' | 'theme'> {
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
- ...TABLE_SETTINGS,
87
- ...settingsMix,
88
- }), [settingsMix]);
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 (
@@ -1,28 +1,28 @@
1
- import {useEffect, useRef, useState} from 'react';
1
+ import {useRef, useEffect, useState} from 'react';
2
2
 
3
3
  import {TextInput} from '@gravity-ui/uikit';
4
4
 
5
- interface StorageFilterProps {
6
- className?: string;
5
+ interface SearchProps {
6
+ onChange: (value: string) => void;
7
7
  value?: string;
8
- placeholder?: string;
9
- onChange?: (value: string) => void;
8
+ className?: string;
10
9
  debounce?: number;
10
+ placeholder?: string;
11
11
  }
12
12
 
13
- export const StorageFilter = (props: StorageFilterProps) => {
14
- const {
15
- className,
16
- value = '',
17
- placeholder,
18
- onChange,
19
- debounce = 200,
20
- } = props;
21
- const [filterValue, setFilterValue] = useState(value);
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
- setFilterValue((prevValue) => {
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 changeFilter = (newValue: string) => {
35
- setFilterValue(newValue);
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(--yc-color-base-float-hover);
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
- text={searchQuery}
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: inline-block;
10
+ display: block;
10
11
 
11
- width: 100%;
12
+ min-width: 50px;
12
13
  height: var(--yc-text-body-2-line-height);
13
14
 
14
- vertical-align: top;
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: absolute;
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
- {href ? (
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
- display: flex;
3
- flex-grow: 1;
4
- align-items: center;
2
+ position: relative;
5
3
 
6
- max-width: 200px;
7
- margin-right: 10px;
4
+ min-width: 120px;
8
5
 
9
- cursor: pointer;
6
+ border-radius: 4px; // to match interactive area with disk shape
10
7
 
11
- &:last-child {
12
- margin-right: 0px;
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 type {RequiredField} from '../../../types';
6
- //@ts-ignore
7
- import {bytesToGB} from '../../../utils/utils';
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
- //@ts-ignore
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 {TPDiskStateInfo, TPDiskState} from '../../../types/api/storage';
14
- import {InfoViewer} from '../../../components/InfoViewer';
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
- <DiskStateProgressBar
138
- diskAllocatedPercent={pdiskAllocatedPercent}
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 {StorageFilter} from './StorageFilter';
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
- tenant,
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 (storageType !== prevProps.storageType || visibleEntities !== prevProps.visibleEntities) {
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
- tenant,
159
- nodeId,
160
- getStorageInfo,
161
- } = this.props;
162
-
163
- getStorageInfo({
164
- tenant,
165
- nodeId,
166
- ...data,
167
- }, {
168
- concurrentId: 'getStorageInfo',
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
- storageType,
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
- <StorageFilter
257
- placeholder={storageType === StorageTypes.groups ? 'Group ID, Pool name' : 'Node ID, FQDN'}
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
- {error && (
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(--yc-color-base-float-hover);
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
  }