ydb-embedded-ui 2.0.0 → 2.2.0

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 (40) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/assets/icons/update-arrow.svg +6 -0
  3. package/dist/components/EntityStatus/EntityStatus.js +37 -10
  4. package/dist/components/EntityStatus/EntityStatus.scss +36 -6
  5. package/dist/components/NodesViewer/NodesViewer.js +1 -0
  6. package/dist/components/ShortyString/ShortyString.tsx +21 -8
  7. package/dist/components/ShortyString/i18n/en.json +10 -0
  8. package/dist/components/ShortyString/i18n/index.ts +11 -0
  9. package/dist/components/ShortyString/i18n/ru.json +10 -0
  10. package/dist/containers/Cluster/Cluster.tsx +3 -3
  11. package/dist/containers/Nodes/Nodes.js +6 -6
  12. package/dist/containers/Storage/StorageFilter/StorageFilter.tsx +1 -0
  13. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +2 -2
  14. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +16 -9
  15. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +18 -5
  16. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +83 -0
  17. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuePreview/IssuePreview.tsx +35 -0
  18. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuePreview/index.ts +1 -0
  19. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/IssuesList.tsx +62 -0
  20. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/index.ts +1 -0
  21. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueViewer.scss +21 -15
  22. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssuesViewer.js +52 -86
  23. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +74 -0
  24. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/index.ts +1 -0
  25. package/dist/containers/Tenant/Diagnostics/Healthcheck/i18n/en.json +7 -0
  26. package/dist/containers/Tenant/Diagnostics/Healthcheck/i18n/index.ts +11 -0
  27. package/dist/containers/Tenant/Diagnostics/Healthcheck/i18n/ru.json +7 -0
  28. package/dist/containers/Tenant/Diagnostics/Healthcheck/index.ts +1 -0
  29. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +4 -21
  30. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +18 -19
  31. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +1 -0
  32. package/dist/containers/Tenant/Tenant.tsx +2 -2
  33. package/dist/containers/Tenants/Tenants.js +7 -7
  34. package/dist/services/api.d.ts +9 -0
  35. package/dist/types/api/healthcheck.ts +91 -0
  36. package/dist/types/store/healthcheck.ts +3 -0
  37. package/dist/utils/hooks/index.ts +1 -0
  38. package/dist/utils/hooks/useAutofetcher.ts +29 -0
  39. package/package.json +4 -1
  40. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.js +0 -195
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.2.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.1.0...v2.2.0) (2022-10-14)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Healthcheck:** rework issues list in modal ([e7cb0df](https://github.com/ydb-platform/ydb-embedded-ui/commit/e7cb0df58e22c8c9cd25aae83b78be4808e9ba81))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **EntityStatus:** enable component to left trim links ([fbc6c51](https://github.com/ydb-platform/ydb-embedded-ui/commit/fbc6c51f9fbea3c1a7f5f70cb542971a41f4d8b3))
14
+ * fix pre-commit prettier linting and add json linting ([#189](https://github.com/ydb-platform/ydb-embedded-ui/issues/189)) ([047415d](https://github.com/ydb-platform/ydb-embedded-ui/commit/047415d2d69ecf4a2d99f0092b9e6735bd8efbc0))
15
+ * **Healthcheck:** delete unneeded i18n translations ([0c6de90](https://github.com/ydb-platform/ydb-embedded-ui/commit/0c6de9031607e4cde1387387393a9cfc9e1e2b8f))
16
+ * **Healthcheck:** enable update button in modal to fetch data ([de0b06e](https://github.com/ydb-platform/ydb-embedded-ui/commit/de0b06e7f2d3536df1b3896cbf86a947b2e7a291))
17
+ * **Healthcheck:** fix layout shift on scrollbar appearance ([ccdde6e](https://github.com/ydb-platform/ydb-embedded-ui/commit/ccdde6e065abbdb1c22a2c3bdd17e63f706d0f77))
18
+ * **Healthcheck:** fix styles for long issues trees ([32f1a8d](https://github.com/ydb-platform/ydb-embedded-ui/commit/32f1a8db58d9f84073327b92dcd80a5b4626a526))
19
+ * **Healthcheck:** fix variable typo ([0f0e056](https://github.com/ydb-platform/ydb-embedded-ui/commit/0f0e056576b9ec18fc3ce574d3742d55e5da6e35))
20
+ * **Healthcheck:** full check status in a preview ([bc0b51e](https://github.com/ydb-platform/ydb-embedded-ui/commit/bc0b51eedd4ff3b4ae1650946832f463a6703c12))
21
+ * **Healthcheck:** make modal show only one first level issue ([cdc95a7](https://github.com/ydb-platform/ydb-embedded-ui/commit/cdc95a7412c1266d990df7e2807630a8f4c88780))
22
+ * **Healthcheck:** redesign healthcheck header ([867f57a](https://github.com/ydb-platform/ydb-embedded-ui/commit/867f57aed84b7b72c22a816c6ac02387490ff495))
23
+ * **Healthcheck:** replace update button with icon ([709a994](https://github.com/ydb-platform/ydb-embedded-ui/commit/709a994544f068db1b0fe09009ecb4d8db46fc38))
24
+ * **Healthcheck:** update styles to be closer to the design ([aa1083d](https://github.com/ydb-platform/ydb-embedded-ui/commit/aa1083d299e24590336eeb3d913a9c53fd77bad6))
25
+ * **Nodes:** case insensitive search ([11d2c98](https://github.com/ydb-platform/ydb-embedded-ui/commit/11d2c985e0c30bb74ed07e22273d8b3459b54c89))
26
+ * **QueryEditor:** smarter error message trim ([8632948](https://github.com/ydb-platform/ydb-embedded-ui/commit/863294828090dc8eb2595884283d0996156c3785))
27
+ * **Tenants:** case insensitive search ([0ad93f5](https://github.com/ydb-platform/ydb-embedded-ui/commit/0ad93f57dcbba7d9746be54a4ba7b76ab4d45108))
28
+ * **Tenants:** fix filtering by ControlPlane name ([4941c82](https://github.com/ydb-platform/ydb-embedded-ui/commit/4941c821cdbb7c5d0da26a3b0d5c00d8979401c0))
29
+ * **Tenants:** left trim db names in db list ([81bf0fa](https://github.com/ydb-platform/ydb-embedded-ui/commit/81bf0fafe901d3601dc04fdf71939e914493ff1c))
30
+
31
+ ## [2.1.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.0.0...v2.1.0) (2022-10-04)
32
+
33
+
34
+ ### Features
35
+
36
+ * autofocus all text search fields ([a38ee84](https://github.com/ydb-platform/ydb-embedded-ui/commit/a38ee84abad4202f5e9b8af897eb68d2c006233a))
37
+ * **Healthcheck:** display first level issues in overview ([10b4bf5](https://github.com/ydb-platform/ydb-embedded-ui/commit/10b4bf5d15d32f028702ff8cfecca0e06bc5616f))
38
+
39
+
40
+ ### Bug Fixes
41
+
42
+ * fix production assets paths ([8eaad0f](https://github.com/ydb-platform/ydb-embedded-ui/commit/8eaad0f1db109c4cf3cbf7d11ad32ea335a6b0c1))
43
+ * **Healthcheck:** add translations ([75f9851](https://github.com/ydb-platform/ydb-embedded-ui/commit/75f9851a35766ef692805a6f154d40340b003487))
44
+ * move eslint hooks rule extension to src config ([179b81d](https://github.com/ydb-platform/ydb-embedded-ui/commit/179b81d60adf422addc8d72f947800c72bd3e4c5))
45
+ * **QueryEditor:** disable fullscreen button for empty result ([4825b5b](https://github.com/ydb-platform/ydb-embedded-ui/commit/4825b5b8dcb89fcafd828dabbace521ddc429922))
46
+ * **QueryEditor:** fix query stats spacings ([b836d72](https://github.com/ydb-platform/ydb-embedded-ui/commit/b836d72824a791b3fde2b9e4585c6c9b42385265))
47
+ * **useAutofetcher:** private autofetcher instance for each usage ([3f34b7a](https://github.com/ydb-platform/ydb-embedded-ui/commit/3f34b7aee2042562a42e6d1a7daf03ffddd888c0))
48
+
3
49
  ## [2.0.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.14.2...v2.0.0) (2022-09-26)
4
50
 
5
51
 
@@ -0,0 +1,6 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M21 12C21 16.9706 16.9706 21 12 21C9.5 21 6.5 19 5 17" stroke='currentColor' stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M5 21L5 17L9 17" stroke='currentColor' stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M3 12C3 7.02944 7.02944 3 12 3C14.5 3 17.5 5 19 7" stroke='currentColor' stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M19 3L19 7L15 7" stroke='currentColor' stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
6
+ </svg>
@@ -2,10 +2,22 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
  import {Link} from 'react-router-dom';
5
- import {ClipboardButton, Link as ExternalLink, Button} from '@gravity-ui/uikit';
5
+ import {ClipboardButton, Link as UIKitLink, Button, Icon} from '@gravity-ui/uikit';
6
+
7
+ import circleInfoIcon from '../../assets/icons/circle-info.svg';
8
+ import circleExclamationIcon from '../../assets/icons/circle-exclamation.svg';
9
+ import triangleExclamationIcon from '../../assets/icons/triangle-exclamation.svg';
10
+ import circleTimesIcon from '../../assets/icons/circle-xmark.svg';
6
11
 
7
12
  import './EntityStatus.scss';
8
13
 
14
+ const icons = {
15
+ BLUE: circleInfoIcon,
16
+ YELLOW: circleExclamationIcon,
17
+ ORANGE: triangleExclamationIcon,
18
+ RED: circleTimesIcon,
19
+ };
20
+
9
21
  const b = cn('entity-status');
10
22
 
11
23
  class EntityStatus extends React.Component {
@@ -22,6 +34,8 @@ class EntityStatus extends React.Component {
22
34
  showStatus: PropTypes.bool,
23
35
  externalLink: PropTypes.bool,
24
36
  className: PropTypes.string,
37
+ mode: PropTypes.oneOf(['color', 'icons']),
38
+ withLeftTrim: PropTypes.bool,
25
39
  };
26
40
 
27
41
  static defaultProps = {
@@ -31,35 +45,47 @@ class EntityStatus extends React.Component {
31
45
  label: '',
32
46
  showStatus: true,
33
47
  externalLink: false,
48
+ mode: 'color',
49
+ withLeftTrim: false,
34
50
  };
35
51
  renderIcon() {
36
- const {status, size, showStatus} = this.props;
52
+ const {status, size, showStatus, mode} = this.props;
37
53
 
38
54
  if (!showStatus) {
39
55
  return null;
40
56
  }
41
57
 
42
- return <div className={b('status-icon', {state: status.toLowerCase(), size})} />;
58
+ const modifiers = {state: status.toLowerCase(), size};
59
+
60
+ if (mode === 'icons' && icons[status]) {
61
+ return <Icon className={b('status-icon', modifiers)} data={icons[status]} />;
62
+ }
63
+
64
+ return <div className={b('status-color', modifiers)} />;
43
65
  }
44
66
  renderStatusLink() {
45
67
  const {iconPath} = this.props;
46
68
 
47
69
  return (
48
- <ExternalLink target="_blank" href={iconPath}>
70
+ <UIKitLink target="_blank" href={iconPath}>
49
71
  {this.renderIcon()}
50
- </ExternalLink>
72
+ </UIKitLink>
51
73
  );
52
74
  }
53
75
  renderLink() {
54
76
  const {externalLink, name, path, onNameMouseEnter, onNameMouseLeave} = this.props;
55
77
 
56
78
  if (externalLink) {
57
- return <ExternalLink href={path}>{name}</ExternalLink>;
79
+ return (
80
+ <UIKitLink className={b('name')} href={path}>
81
+ {name}
82
+ </UIKitLink>
83
+ );
58
84
  }
59
85
 
60
86
  return path ? (
61
87
  <Link
62
- title={name}
88
+ className={b('name')}
63
89
  to={path}
64
90
  onMouseEnter={onNameMouseEnter}
65
91
  onMouseLeave={onNameMouseLeave}
@@ -70,7 +96,6 @@ class EntityStatus extends React.Component {
70
96
  name && (
71
97
  <span
72
98
  className={b('name')}
73
- title={name}
74
99
  onMouseEnter={onNameMouseEnter}
75
100
  onMouseLeave={onNameMouseLeave}
76
101
  >
@@ -83,14 +108,16 @@ class EntityStatus extends React.Component {
83
108
  const {name, label, iconPath, hasClipboardButton, className} = this.props;
84
109
 
85
110
  return (
86
- <div className={b(null, className)}>
111
+ <div className={b(null, className)} title={name}>
87
112
  {iconPath ? this.renderStatusLink() : this.renderIcon()}
88
113
  {label && (
89
114
  <span title={label} className={b('label')}>
90
115
  {label}
91
116
  </span>
92
117
  )}
93
- {this.renderLink()}
118
+ <span className={b('link', {'with-left-trim': this.props.withLeftTrim})}>
119
+ {this.renderLink()}
120
+ </span>
94
121
  {hasClipboardButton && (
95
122
  <Button
96
123
  component="span"
@@ -31,11 +31,7 @@
31
31
  }
32
32
 
33
33
  a {
34
- overflow: hidden;
35
-
36
- white-space: nowrap;
37
34
  text-decoration: none;
38
- text-overflow: ellipsis;
39
35
 
40
36
  color: var(--yc-color-text-link);
41
37
  }
@@ -53,30 +49,49 @@
53
49
  color: var(--yc-color-text-complementary);
54
50
  }
55
51
 
56
- &__name {
52
+ &__link {
53
+ overflow: hidden;
54
+
57
55
  white-space: nowrap;
56
+ text-overflow: ellipsis;
57
+ }
58
+
59
+ &__link_with-left-trim {
60
+ direction: rtl;
61
+
62
+ .entity-status__name {
63
+ unicode-bidi: plaintext;
64
+ }
58
65
  }
59
66
 
67
+ &__status-color,
60
68
  &__status-icon {
69
+ flex-shrink: 0;
70
+
61
71
  margin-right: 8px;
62
72
 
63
73
  border-radius: 3px;
64
74
  &_size_xs {
65
75
  aspect-ratio: 1;
66
76
 
77
+ width: 12px;
67
78
  height: 12px;
68
79
  }
69
80
  &_size_s {
70
81
  aspect-ratio: 1;
71
82
 
83
+ width: 16px;
72
84
  height: 16px;
73
85
  }
74
86
  &_size_m {
75
87
  aspect-ratio: 1;
76
88
 
89
+ width: 18px;
77
90
  height: 18px;
78
91
  }
92
+ }
79
93
 
94
+ &__status-color {
80
95
  &_state_running,
81
96
  &_state_green {
82
97
  background-color: var(--yc-color-infographics-positive-heavy);
@@ -96,7 +111,22 @@
96
111
  background: var(--yc-color-text-complementary);
97
112
  }
98
113
  &_state_orange {
99
- background: var(--yc-color-text-warning-heavy);
114
+ background: var(--yc-color-base-warning-orange);
115
+ }
116
+ }
117
+
118
+ &__status-icon {
119
+ &_state_blue {
120
+ color: var(--yc-color-infographics-info-heavy);
121
+ }
122
+ &_state_yellow {
123
+ color: var(--yc-color-infographics-warning-heavy);
124
+ }
125
+ &_state_orange {
126
+ color: var(--yc-color-base-warning-orange);
127
+ }
128
+ &_state_red {
129
+ color: var(--yc-color-infographics-danger-heavy);
100
130
  }
101
131
  }
102
132
 
@@ -106,6 +106,7 @@ class NodesViewer extends React.PureComponent {
106
106
  text={searchQuery}
107
107
  onUpdate={handleSearchQuery}
108
108
  hasClear
109
+ autoFocus
109
110
  />
110
111
  <ProblemFilter value={filter} onChange={this.onChangeProblemFilter} />
111
112
  <Label theme="info" size="m">{`Nodes: ${nodesToShow.length}`}</Label>
@@ -3,6 +3,7 @@ import cn from 'bem-cn-lite';
3
3
 
4
4
  import {Link} from '@gravity-ui/uikit';
5
5
 
6
+ import i18n from './i18n';
6
7
  import './ShortyString.scss';
7
8
 
8
9
  const block = cn('kv-shorty-string');
@@ -10,6 +11,8 @@ const block = cn('kv-shorty-string');
10
11
  type Props = {
11
12
  value?: string;
12
13
  limit?: number;
14
+ /** in strict mode the text always trims at the limit, otherwise it is allowed to overflow a little */
15
+ strict?: boolean;
13
16
  displayLength?: boolean;
14
17
  render?: (value: string) => React.ReactNode;
15
18
  onToggle?: () => void;
@@ -20,19 +23,29 @@ type Props = {
20
23
  export default function ShortyString({
21
24
  value = '',
22
25
  limit = 200,
26
+ strict = false,
23
27
  displayLength = true,
24
28
  render = (v: string) => v,
25
29
  onToggle,
26
- expandLabel = 'Show more',
27
- collapseLabel = 'Show less',
30
+ expandLabel = i18n('default_expand_label'),
31
+ collapseLabel = i18n('default_collapse_label'),
28
32
  }: Props) {
29
33
  const [expanded, setExpanded] = React.useState(false);
30
- const hasToggle = value.length > limit;
31
- const length =
32
- displayLength && !expanded ? `(${value.length} symbols)` : undefined;
33
34
 
34
- const text = expanded || value.length <= limit ? value : value.slice(0, limit - 4) + '\u00a0...';
35
- const label = expanded ? collapseLabel : expandLabel;
35
+ const toggleLabelAction = expanded ? collapseLabel : expandLabel;
36
+ const toggleLabelSymbolsCount = displayLength && !expanded
37
+ ? i18n('chars_count', {count: value.length})
38
+ : '';
39
+ const toggleLabel = toggleLabelAction + toggleLabelSymbolsCount;
40
+
41
+ // showing toogle button with a label that is longer than the hidden part is pointless,
42
+ // hence compare to limit + length in the not-strict mode
43
+ const hasToggle = value.length > limit + (strict ? 0 : toggleLabel.length);
44
+
45
+ const text = expanded || !hasToggle
46
+ ? value
47
+ : value.slice(0, limit - 4) + '\u00a0...';
48
+
36
49
  return (
37
50
  <div className={block()}>
38
51
  {render(text)}
@@ -45,7 +58,7 @@ export default function ShortyString({
45
58
  onToggle?.();
46
59
  }}
47
60
  >
48
- {label} {length}
61
+ {toggleLabel}
49
62
  </Link>
50
63
  ) : null}
51
64
  </div>
@@ -0,0 +1,10 @@
1
+ {
2
+ "default_collapse_label": "Show less",
3
+ "default_expand_label": "Show more",
4
+ "chars_count": [
5
+ " ({{count}} symbol)",
6
+ " ({{count}} symbols)",
7
+ " ({{count}} symbols)",
8
+ " ({{count}} symbols)"
9
+ ]
10
+ }
@@ -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-shorty-string';
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,10 @@
1
+ {
2
+ "default_collapse_label": "Показать меньше",
3
+ "default_expand_label": "Показать ещё",
4
+ "chars_count": [
5
+ " ({{count}} символ)",
6
+ " ({{count}} символа)",
7
+ " ({{count}} символов)",
8
+ " ({{count}} символов)"
9
+ ]
10
+ }
@@ -14,9 +14,9 @@ import ClusterInfo from '../../components/ClusterInfo/ClusterInfo';
14
14
  const b = cn('cluster');
15
15
 
16
16
  interface ClusterProps {
17
- additionalClusterInfo: any;
18
- additionalTenantsInfo: any;
19
- additionalNodesInfo: any;
17
+ additionalClusterInfo?: any;
18
+ additionalTenantsInfo?: any;
19
+ additionalNodesInfo?: any;
20
20
  }
21
21
 
22
22
  function Cluster(props: ClusterProps) {
@@ -87,6 +87,7 @@ class Nodes extends React.Component {
87
87
  text={searchQuery}
88
88
  onUpdate={this.handleSearchQueryChange}
89
89
  hasClear
90
+ autoFocus
90
91
  />
91
92
  <ProblemFilter value={filter} onChange={this.handleFilterChange} />
92
93
  <Label theme="info" size="m">{`Nodes: ${nodes?.length}`}</Label>
@@ -113,11 +114,10 @@ class Nodes extends React.Component {
113
114
  });
114
115
 
115
116
  let preparedNodes = searchQuery
116
- ? nodes.filter((node) =>
117
- node.Host
118
- ? node.Host.includes(searchQuery) || String(node.NodeId).includes(searchQuery)
119
- : true,
120
- )
117
+ ? nodes.filter((node) => {
118
+ const re = new RegExp(searchQuery, 'i');
119
+ return node.Host ? re.test(node.Host) || re.test(String(node.NodeId)) : true;
120
+ })
121
121
  : nodes;
122
122
  preparedNodes = preparedNodes.map((node) => ({
123
123
  ...node,
@@ -142,7 +142,7 @@ class Nodes extends React.Component {
142
142
  columnId: 'NodeId',
143
143
  order: DataTable.ASCENDING,
144
144
  }}
145
- emptyDataMessage='No such nodes'
145
+ emptyDataMessage="No such nodes"
146
146
  />
147
147
  </div>
148
148
  </div>
@@ -47,6 +47,7 @@ export const StorageFilter = (props: StorageFilterProps) => {
47
47
  value={filterValue}
48
48
  onUpdate={changeFilter}
49
49
  hasClear
50
+ autoFocus
50
51
  />
51
52
  );
52
53
  }
@@ -19,8 +19,8 @@ $section-title-line-height: 24px;
19
19
 
20
20
  &__close-modal-button {
21
21
  position: absolute;
22
- top: 10px;
23
- right: 10px;
22
+ top: 23px;
23
+ right: 13px;
24
24
 
25
25
  & .yc-button__text {
26
26
  display: flex;
@@ -8,8 +8,7 @@ import type {EPathType} from '../../../../types/api/schema';
8
8
  //@ts-ignore
9
9
  import Icon from '../../../../components/Icon/Icon';
10
10
  import Overview from '../Overview/Overview';
11
- //@ts-ignore
12
- import Healthcheck from '../Healthcheck/Healthcheck';
11
+ import {Healthcheck} from '../Healthcheck';
13
12
  //@ts-ignore
14
13
  import TenantOverview from '../TenantOverview/TenantOverview';
15
14
 
@@ -27,11 +26,12 @@ const b = cn('kv-detailed-overview');
27
26
  function DetailedOverview(props: DetailedOverviewProps) {
28
27
  const [isModalVisible, setIsModalVisible] = useState(false);
29
28
 
30
- const {
31
- currentSchemaPath,
32
- } = useSelector((state: any) => state.schema);
29
+ const [expandedIssueId, setExpandedIssueId] = useState<string>();
30
+
31
+ const {currentSchemaPath} = useSelector((state: any) => state.schema);
33
32
 
34
- const openModalHandler = () => {
33
+ const openModalHandler = (id: string) => {
34
+ setExpandedIssueId(id);
35
35
  setIsModalVisible(true);
36
36
  };
37
37
 
@@ -42,12 +42,16 @@ function DetailedOverview(props: DetailedOverviewProps) {
42
42
  const renderModal = () => {
43
43
  return (
44
44
  <Modal open={isModalVisible} onClose={closeModalHandler} className={b('modal')}>
45
- <Healthcheck tenant={props.tenantName} />
45
+ <Healthcheck
46
+ tenant={props.tenantName}
47
+ fetchData={false}
48
+ expandedIssueId={expandedIssueId}
49
+ />
46
50
  <Button
47
51
  className={b('close-modal-button')}
48
52
  onClick={closeModalHandler}
49
53
  view="flat-secondary"
50
- title='Close'
54
+ title="Close"
51
55
  >
52
56
  <Icon name="close" viewBox={'0 0 16 16 '} height={20} width={20} />
53
57
  </Button>
@@ -63,7 +67,10 @@ function DetailedOverview(props: DetailedOverviewProps) {
63
67
  {isTenant ? (
64
68
  <>
65
69
  <div className={b('section')}>
66
- <TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo} />
70
+ <TenantOverview
71
+ tenantName={tenantName}
72
+ additionalTenantInfo={additionalTenantInfo}
73
+ />
67
74
  </div>
68
75
  <div className={b('section')}>
69
76
  <Healthcheck
@@ -1,8 +1,19 @@
1
1
  @use '../DetailedOverview/DetailedOverview.scss' as detailedOverview;
2
2
  @import '../../../../styles/mixins.scss';
3
+ @import '@gravity-ui/uikit/styles/mixins.scss';
3
4
 
4
5
  .healthcheck {
5
- padding: 25px 20px 20px;
6
+ // Since most of the inner containers have fixed width, we can set fixed width here as well
7
+ // Thus we will get rid of unneeded layout shift when scrollbar appear
8
+ min-width: 885px;
9
+
10
+ &__issues-list {
11
+ padding: 25px 20px 20px;
12
+ }
13
+
14
+ &__issue-preview {
15
+ margin-bottom: 15px;
16
+ }
6
17
 
7
18
  &__issues {
8
19
  overflow-x: hidden;
@@ -21,19 +32,21 @@
21
32
  padding: 15px 0;
22
33
  }
23
34
 
24
- &__self-check-status {
35
+ &__issues-list-header {
25
36
  display: flex;
26
37
  align-items: center;
27
38
 
28
39
  margin-bottom: 20px;
29
40
  }
30
41
 
31
- &__self-check-status-label {
42
+ &__issues-list-header-title {
32
43
  margin: 0 10px 0 0;
44
+
45
+ @include text-header-1();
33
46
  }
34
47
 
35
- &__self-check-update {
36
- margin-left: 20px;
48
+ &__issues-list-header-update {
49
+ margin-left: 10px;
37
50
  }
38
51
 
39
52
  &__status-wrapper {
@@ -0,0 +1,83 @@
1
+ import {useCallback} from 'react';
2
+ import {useDispatch, useSelector} from 'react-redux';
3
+ import cn from 'bem-cn-lite';
4
+
5
+ import {Loader} from '@gravity-ui/uikit';
6
+
7
+ import {getHealthcheckInfo} from '../../../../store/reducers/healthcheckInfo';
8
+ import {useAutofetcher} from '../../../../utils/hooks';
9
+
10
+ import {IssuesList} from './IssuesList';
11
+ import {Preview} from './Preview';
12
+
13
+ import i18n from './i18n';
14
+ import './Healthcheck.scss';
15
+
16
+ interface HealthcheckProps {
17
+ tenant: string;
18
+ preview?: boolean;
19
+ fetchData?: boolean;
20
+ expandedIssueId?: string;
21
+ showMoreHandler?: (id: string) => void;
22
+ }
23
+
24
+ const b = cn('healthcheck');
25
+
26
+ export const Healthcheck = (props: HealthcheckProps) => {
27
+ const {tenant, preview, fetchData = true, showMoreHandler, expandedIssueId} = props;
28
+
29
+ const dispatch = useDispatch();
30
+
31
+ const {data, loading, wasLoaded, error} = useSelector((state: any) => state.healthcheckInfo);
32
+ const {autorefresh} = useSelector((state: any) => state.schema);
33
+
34
+ const fetchHealthcheck = useCallback(() => {
35
+ dispatch(getHealthcheckInfo(tenant));
36
+ }, [dispatch, tenant]);
37
+
38
+ useAutofetcher(
39
+ () => {
40
+ if (fetchData) {
41
+ fetchHealthcheck();
42
+ }
43
+ },
44
+ [fetchData, fetchHealthcheck],
45
+ autorefresh,
46
+ );
47
+
48
+ const renderContent = () => {
49
+ if (error) {
50
+ return error.statusText;
51
+ }
52
+
53
+ if (loading && !wasLoaded) {
54
+ return (
55
+ <div className={b('loader')}>
56
+ <Loader size="m" />
57
+ </div>
58
+ );
59
+ }
60
+
61
+ if (data && data['self_check_result']) {
62
+ return preview ? (
63
+ <Preview
64
+ data={data}
65
+ loading={loading}
66
+ onShowMore={showMoreHandler}
67
+ onUpdate={fetchHealthcheck}
68
+ />
69
+ ) : (
70
+ <IssuesList
71
+ data={data}
72
+ expandedIssueId={expandedIssueId}
73
+ loading={loading}
74
+ onUpdate={fetchHealthcheck}
75
+ />
76
+ );
77
+ }
78
+
79
+ return <div className="error">{i18n('no-data')}</div>;
80
+ };
81
+
82
+ return <div className={b()}>{renderContent()}</div>;
83
+ };
@@ -0,0 +1,35 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import {Link, Text} from '@gravity-ui/uikit';
4
+
5
+ import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
6
+ import {IssueLog} from '../../../../../types/api/healthcheck';
7
+
8
+ import i18n from '../i18n';
9
+
10
+ const b = cn('healthcheck');
11
+
12
+ interface IssuePreviewProps {
13
+ data?: IssueLog;
14
+ onShowMore?: (id: string) => void;
15
+ }
16
+
17
+ export const IssuePreview = (props: IssuePreviewProps) => {
18
+ const {data, onShowMore} = props;
19
+
20
+ if (!data) {
21
+ return null;
22
+ }
23
+
24
+ return (
25
+ <div className={b('issue-preview')}>
26
+ <EntityStatus mode="icons" status={data.status} name={data.type} />
27
+ <Text as="div" color="secondary" variant="body-2">
28
+ {data.message}
29
+ </Text>
30
+ <Link onClick={() => onShowMore && onShowMore(data.id)}>
31
+ {i18n('label.show-details')}
32
+ </Link>
33
+ </div>
34
+ );
35
+ };
@@ -0,0 +1 @@
1
+ export * from './IssuePreview';