ydb-embedded-ui 4.17.0 → 4.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +2 -2
  3. package/dist/components/InfoViewer/formatters/common.ts +1 -1
  4. package/dist/components/InfoViewer/formatters/pqGroup.ts +1 -1
  5. package/dist/components/InfoViewer/formatters/table.ts +6 -1
  6. package/dist/components/ProgressViewer/ProgressViewer.tsx +99 -0
  7. package/dist/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx +1 -1
  8. package/dist/containers/AsideNavigation/AsideNavigation.tsx +3 -1
  9. package/dist/containers/Authentication/Authentication.tsx +14 -4
  10. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +2 -2
  11. package/dist/containers/Heatmap/Heatmap.tsx +1 -1
  12. package/dist/containers/Heatmap/Histogram/Histogram.js +1 -1
  13. package/dist/containers/Node/NodeStructure/Pdisk.tsx +2 -2
  14. package/dist/containers/Node/NodeStructure/Vdisk.tsx +5 -2
  15. package/dist/containers/Nodes/getNodesColumns.tsx +2 -2
  16. package/dist/containers/Storage/PDisk/PDisk.tsx +1 -1
  17. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +1 -1
  18. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +1 -1
  19. package/dist/containers/Storage/VDisk/VDisk.tsx +1 -1
  20. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +1 -1
  21. package/dist/containers/Tablet/TabletInfo/TabletInfo.tsx +1 -1
  22. package/dist/containers/Tablet/TabletTable/TabletTable.tsx +1 -1
  23. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +1 -1
  24. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +1 -1
  25. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +3 -3
  26. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +1 -1
  27. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +1 -1
  28. package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +1 -1
  29. package/dist/containers/Tenant/Diagnostics/TenantOverview/OldTenantOverview.tsx +1 -1
  30. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +1 -1
  31. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +1 -1
  32. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
  33. package/dist/containers/Tenants/Tenants.tsx +5 -1
  34. package/dist/containers/UserSettings/i18n/en.json +1 -1
  35. package/dist/containers/UserSettings/i18n/ru.json +1 -1
  36. package/dist/containers/UserSettings/settings.ts +2 -2
  37. package/dist/containers/Versions/NodesTable/NodesTable.tsx +2 -2
  38. package/dist/services/api.ts +1 -1
  39. package/dist/store/reducers/clusterNodes/clusterNodes.tsx +1 -1
  40. package/dist/store/reducers/node/selectors.ts +1 -1
  41. package/dist/store/reducers/nodes/selectors.ts +1 -1
  42. package/dist/store/reducers/nodes/types.ts +1 -1
  43. package/dist/store/reducers/nodes/utils.ts +1 -1
  44. package/dist/store/reducers/settings/settings.ts +4 -4
  45. package/dist/store/reducers/storage/types.ts +1 -1
  46. package/dist/store/reducers/storage/utils.ts +1 -1
  47. package/dist/store/reducers/tenants/utils.ts +2 -2
  48. package/dist/types/api/vdisk.ts +1 -1
  49. package/dist/utils/bytesParsers/formatBytes.ts +1 -1
  50. package/dist/utils/constants.ts +2 -1
  51. package/dist/utils/dataFormatters/dataFormatters.ts +128 -0
  52. package/dist/utils/dataFormatters/i18n/en.json +3 -0
  53. package/dist/utils/dataFormatters/i18n/ru.json +3 -0
  54. package/dist/utils/index.js +0 -102
  55. package/package.json +1 -1
  56. package/dist/components/ProgressViewer/ProgressViewer.js +0 -92
  57. package/dist/utils/formatCPU/formatCPU.ts +0 -20
  58. package/dist/utils/formatCPU/i18n/en.json +0 -3
  59. package/dist/utils/formatCPU/i18n/ru.json +0 -3
  60. /package/dist/utils/{formatCPU → dataFormatters}/i18n/index.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.18.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.17.0...v4.18.0) (2023-09-25)
4
+
5
+
6
+ ### Features
7
+
8
+ * **ProgressViewer:** add custom threasholds to ProgressViewer ([#540](https://github.com/ydb-platform/ydb-embedded-ui/issues/540)) ([3553065](https://github.com/ydb-platform/ydb-embedded-ui/commit/35530655581357f4a79c277a5bf9846b3befb784))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **Authentication:** enable page redirect ([#539](https://github.com/ydb-platform/ydb-embedded-ui/issues/539)) ([721883c](https://github.com/ydb-platform/ydb-embedded-ui/commit/721883cc7f4ca60e64d4a5f77b939dbb8e960855))
14
+ * **Healthcheck:** add merge_records request param ([#538](https://github.com/ydb-platform/ydb-embedded-ui/issues/538)) ([6a47481](https://github.com/ydb-platform/ydb-embedded-ui/commit/6a474814f71c3318715a8ce638fd522a770d8038))
15
+ * **Nodes:** use nodes endpoint by default ([#535](https://github.com/ydb-platform/ydb-embedded-ui/issues/535)) ([12d4fef](https://github.com/ydb-platform/ydb-embedded-ui/commit/12d4fefde7a6663bb1a11f46b4e94fb24b23e966))
16
+ * rename flag for display metrics cards for database diagnostics ([#536](https://github.com/ydb-platform/ydb-embedded-ui/issues/536)) ([957e1fa](https://github.com/ydb-platform/ydb-embedded-ui/commit/957e1fafbbc43928498ae9e8d0bc119bcda5288d))
17
+
3
18
  ## [4.17.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.16.2...v4.17.0) (2023-09-18)
4
19
 
5
20
 
@@ -3,10 +3,10 @@ import cn from 'bem-cn-lite';
3
3
  import type {TSystemStateInfo} from '../../types/api/nodes';
4
4
 
5
5
  import {LOAD_AVERAGE_TIME_INTERVALS} from '../../utils/constants';
6
- import {calcUptime} from '../../utils';
6
+ import {calcUptime} from '../../utils/dataFormatters/dataFormatters';
7
7
 
8
8
  import InfoViewer from '../InfoViewer/InfoViewer';
9
- import ProgressViewer from '../ProgressViewer/ProgressViewer';
9
+ import {ProgressViewer} from '../ProgressViewer/ProgressViewer';
10
10
  import {PoolUsage} from '../PoolUsage/PoolUsage';
11
11
 
12
12
  import './FullNodeViewer.scss';
@@ -1,5 +1,5 @@
1
1
  import type {TDirEntry} from '../../../types/api/schema';
2
- import {formatDateTime} from '../../../utils';
2
+ import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
3
3
 
4
4
  import {createInfoFormatter} from '../utils';
5
5
 
@@ -4,7 +4,7 @@ import {
4
4
  TPQPartitionConfig,
5
5
  TPQTabletConfig,
6
6
  } from '../../../types/api/schema';
7
- import {formatBps, formatBytes, formatNumber} from '../../../utils';
7
+ import {formatBps, formatBytes, formatNumber} from '../../../utils/dataFormatters/dataFormatters';
8
8
  import {HOUR_IN_SECONDS} from '../../../utils/constants';
9
9
 
10
10
  import {createInfoFormatter} from '../utils';
@@ -1,6 +1,11 @@
1
1
  import type {TFollowerGroup, TPartitionConfig, TTableStats} from '../../../types/api/schema';
2
2
  import type {TMetrics} from '../../../types/api/tenant';
3
- import {formatCPU, formatNumber, formatBps, formatDateTime} from '../../../utils';
3
+ import {
4
+ formatBps,
5
+ formatCPU,
6
+ formatDateTime,
7
+ formatNumber,
8
+ } from '../../../utils/dataFormatters/dataFormatters';
4
9
  import {toFormattedSize} from '../../FormattedBytes/utils';
5
10
 
6
11
  import {createInfoFormatter} from '../utils';
@@ -0,0 +1,99 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import type {ValueOf} from '../../types/common';
4
+
5
+ import './ProgressViewer.scss';
6
+
7
+ const b = cn('progress-viewer');
8
+
9
+ export const PROGRESS_VIEWER_SIZE_IDS = {
10
+ xs: 'xs',
11
+ s: 's',
12
+ ns: 'ns',
13
+ m: 'm',
14
+ n: 'n',
15
+ l: 'l',
16
+ head: 'head',
17
+ } as const;
18
+
19
+ type ProgressViewerSize = ValueOf<typeof PROGRESS_VIEWER_SIZE_IDS>;
20
+
21
+ /*
22
+
23
+ Props description:
24
+ 1) value - the amount of progress
25
+ 2) capacity - maximum possible progress value
26
+ 3) formatValues - function for formatting the value and capacity
27
+ 4) percents - display progress in percents
28
+ 5) colorizeProgress - change the color of the progress bar depending on its value
29
+ 6) inverseColorize - invert the colors of the progress bar
30
+ 7) warningThreshold - the percentage of fullness at which the color of the progress bar changes to yellow
31
+ 8) dangerThreshold - the percentage of fullness at which the color of the progress bar changes to red
32
+ */
33
+
34
+ interface ProgressViewerProps {
35
+ value?: number | string;
36
+ capacity?: number | string;
37
+ formatValues?: (value?: number, capacity?: number) => (string | undefined)[];
38
+ percents?: boolean;
39
+ className?: string;
40
+ size?: ProgressViewerSize;
41
+ colorizeProgress?: boolean;
42
+ inverseColorize?: boolean;
43
+ warningThreshold?: number;
44
+ dangerThreshold?: number;
45
+ }
46
+
47
+ export function ProgressViewer({
48
+ value,
49
+ capacity = 100,
50
+ formatValues,
51
+ percents,
52
+ className,
53
+ size = PROGRESS_VIEWER_SIZE_IDS.xs,
54
+ colorizeProgress,
55
+ inverseColorize,
56
+ warningThreshold = 60,
57
+ dangerThreshold = 80,
58
+ }: ProgressViewerProps) {
59
+ let fillWidth = Math.round((parseFloat(String(value)) / parseFloat(String(capacity))) * 100);
60
+ fillWidth = fillWidth > 100 ? 100 : fillWidth;
61
+ let valueText: number | string | undefined = Math.round(Number(value)),
62
+ capacityText: number | string | undefined = capacity,
63
+ divider = '/';
64
+ if (formatValues) {
65
+ [valueText, capacityText] = formatValues(Number(value), Number(capacity));
66
+ } else if (percents) {
67
+ valueText = fillWidth + '%';
68
+ capacityText = '';
69
+ divider = '';
70
+ }
71
+
72
+ let bg = inverseColorize ? 'scarlet' : 'apple';
73
+ if (colorizeProgress) {
74
+ if (fillWidth > warningThreshold && fillWidth <= dangerThreshold) {
75
+ bg = 'saffron';
76
+ } else if (fillWidth > dangerThreshold) {
77
+ bg = inverseColorize ? 'apple' : 'scarlet';
78
+ }
79
+ }
80
+
81
+ const lineStyle = {
82
+ width: fillWidth + '%',
83
+ };
84
+
85
+ const text = fillWidth > 60 ? 'contrast0' : 'contrast70';
86
+
87
+ if (!isNaN(fillWidth)) {
88
+ return (
89
+ <div className={b({size}, className)}>
90
+ <div className={b('line', {bg})} style={lineStyle}></div>
91
+ <span
92
+ className={b('text', {text})}
93
+ >{`${valueText} ${divider} ${capacityText}`}</span>
94
+ </div>
95
+ );
96
+ }
97
+
98
+ return <div className={`${b({size})} ${className} error`}>no data</div>;
99
+ }
@@ -1,6 +1,6 @@
1
1
  import type {TTabletStateInfo} from '../../../types/api/tablet';
2
2
 
3
- import {calcUptime} from '../../../utils';
3
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
4
4
  import {InfoViewer, createInfoFormatter, formatObject} from '../../InfoViewer';
5
5
 
6
6
  const formatTablet = createInfoFormatter<TTabletStateInfo>({
@@ -50,7 +50,9 @@ function YbdInternalUser({ydbUser, logout}: YbdInternalUserProps) {
50
50
  const history = useHistory();
51
51
 
52
52
  const handleLoginClick = () => {
53
- history.push(createHref(routes.auth, undefined, {returnUrl: encodeURI(location.href)}));
53
+ history.push(
54
+ createHref(routes.auth, undefined, {returnUrl: encodeURIComponent(location.href)}),
55
+ );
54
56
  };
55
57
 
56
58
  return (
@@ -1,12 +1,13 @@
1
1
  import {KeyboardEvent, useEffect, useState} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
- import {useHistory} from 'react-router';
3
+ import {useHistory, useLocation} from 'react-router';
4
4
  import cn from 'bem-cn-lite';
5
5
 
6
6
  import {Button, TextInput, Icon, Link as ExternalLink} from '@gravity-ui/uikit';
7
7
 
8
8
  import {authenticate} from '../../store/reducers/authentication/authentication';
9
9
  import {useTypedSelector} from '../../utils/hooks';
10
+ import {parseQuery} from '../../routes';
10
11
 
11
12
  import ydbLogoIcon from '../../assets/icons/ydb.svg';
12
13
  import showIcon from '../../assets/icons/show.svg';
@@ -18,13 +19,15 @@ import './Authentication.scss';
18
19
  const b = cn('authentication');
19
20
 
20
21
  interface AuthenticationProps {
21
- returnUrl?: string;
22
22
  closable?: boolean;
23
23
  }
24
24
 
25
- function Authentication({returnUrl, closable = false}: AuthenticationProps) {
25
+ function Authentication({closable = false}: AuthenticationProps) {
26
26
  const dispatch = useDispatch();
27
27
  const history = useHistory();
28
+ const location = useLocation();
29
+
30
+ const {returnUrl} = parseQuery(location);
28
31
 
29
32
  const {error} = useTypedSelector((state) => state.authentication);
30
33
 
@@ -58,7 +61,14 @@ function Authentication({returnUrl, closable = false}: AuthenticationProps) {
58
61
  // typed dispatch required, remove error expectation after adding it
59
62
  dispatch(authenticate(login, pass)).then(() => {
60
63
  if (returnUrl) {
61
- history.replace(decodeURI(returnUrl));
64
+ const decodedUrl = decodeURIComponent(returnUrl.toString());
65
+
66
+ // to prevent page reload we use router history
67
+ // history navigates relative to origin
68
+ // so we remove origin to make it work properly
69
+ const url = new URL(decodedUrl);
70
+ const path = url.pathname + url.search;
71
+ history.replace(path);
62
72
  }
63
73
  });
64
74
  };
@@ -3,7 +3,7 @@ import block from 'bem-cn-lite';
3
3
  import {Skeleton} from '@gravity-ui/uikit';
4
4
 
5
5
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
6
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
6
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
7
7
  import InfoViewer, {InfoViewerItem} from '../../../components/InfoViewer/InfoViewer';
8
8
  import {Tags} from '../../../components/Tags';
9
9
  import {Tablet} from '../../../components/Tablet';
@@ -16,7 +16,7 @@ import type {AdditionalClusterProps, ClusterLink} from '../../../types/additiona
16
16
  import type {VersionValue} from '../../../types/versions';
17
17
  import type {TClusterInfo} from '../../../types/api/cluster';
18
18
  import {backend, customBackend} from '../../../store';
19
- import {formatStorageValues} from '../../../utils';
19
+ import {formatStorageValues} from '../../../utils/dataFormatters/dataFormatters';
20
20
  import {useSetting, useTypedSelector} from '../../../utils/hooks';
21
21
  import {
22
22
  CLUSTER_DEFAULT_TITLE,
@@ -7,7 +7,7 @@ import {Checkbox, Select} from '@gravity-ui/uikit';
7
7
  import type {IHeatmapMetricValue} from '../../types/store/heatmap';
8
8
  import {getTabletsInfo, setHeatmapOptions} from '../../store/reducers/heatmap';
9
9
  import {showTooltip, hideTooltip} from '../../store/reducers/tooltip';
10
- import {formatNumber} from '../../utils';
10
+ import {formatNumber} from '../../utils/dataFormatters/dataFormatters';
11
11
  import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
12
12
 
13
13
  import {Loader} from '../../components/Loader';
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
5
  import {getColorRange, getCurrentMetricLimits} from '../util';
6
- import {formatNumber} from '../../../utils';
6
+ import {formatNumber} from '../../../utils/dataFormatters/dataFormatters';
7
7
 
8
8
  import './Histogram.scss';
9
9
 
@@ -8,12 +8,12 @@ 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';
11
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
11
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
12
12
  import {Icon} from '../../../components/Icon';
13
13
  import {Vdisk} from './Vdisk';
14
14
 
15
15
  import {bytesToGB, pad9} from '../../../utils/utils';
16
- import {formatStorageValuesToGb} from '../../../utils';
16
+ import {formatStorageValuesToGb} from '../../../utils/dataFormatters/dataFormatters';
17
17
  import {getPDiskType} from '../../../utils/pdisk';
18
18
 
19
19
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
@@ -1,8 +1,11 @@
1
1
  import React from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
5
- import {formatStorageValuesToGb, stringifyVdiskId} from '../../../utils';
4
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
5
+ import {
6
+ formatStorageValuesToGb,
7
+ stringifyVdiskId,
8
+ } from '../../../utils/dataFormatters/dataFormatters';
6
9
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
7
10
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
8
11
  import {valueIsDefined} from './NodeStructure';
@@ -2,12 +2,12 @@ import DataTable, {Column} from '@gravity-ui/react-data-table';
2
2
  import {Popover} from '@gravity-ui/uikit';
3
3
 
4
4
  import {PoolsGraph} from '../../components/PoolsGraph/PoolsGraph';
5
- import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
5
+ import {ProgressViewer} from '../../components/ProgressViewer/ProgressViewer';
6
6
  import {TabletsStatistic} from '../../components/TabletsStatistic';
7
7
  import {NodeHostWrapper} from '../../components/NodeHostWrapper/NodeHostWrapper';
8
8
 
9
9
  import {isSortableNodesProperty} from '../../utils/nodes';
10
- import {formatBytesToGigabyte} from '../../utils/index';
10
+ import {formatBytesToGigabyte} from '../../utils/dataFormatters/dataFormatters';
11
11
 
12
12
  import type {NodesPreparedEntity} from '../../store/reducers/nodes/types';
13
13
 
@@ -7,7 +7,7 @@ import {Stack} from '../../../components/Stack/Stack';
7
7
  import routes, {createHref} from '../../../routes';
8
8
  import {selectVDisksForPDisk} from '../../../store/reducers/storage/selectors';
9
9
  import {TPDiskStateInfo, TPDiskState} from '../../../types/api/pdisk';
10
- import {stringifyVdiskId} from '../../../utils';
10
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
11
11
  import {useTypedSelector} from '../../../utils/hooks';
12
12
  import {getPDiskType} from '../../../utils/pdisk';
13
13
  import {isFullVDiskData} from '../../../utils/storage';
@@ -9,7 +9,7 @@ import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
9
9
 
10
10
  import {EFlag} from '../../../types/api/enums';
11
11
  import {TPDiskStateInfo} from '../../../types/api/pdisk';
12
- import {getPDiskId} from '../../../utils';
12
+ import {getPDiskId} from '../../../utils/dataFormatters/dataFormatters';
13
13
  import {getPDiskType} from '../../../utils/pdisk';
14
14
  import {bytesToGB} from '../../../utils/utils';
15
15
 
@@ -10,7 +10,7 @@ import type {HandleSort} from '../../../utils/hooks/useTableSort';
10
10
 
11
11
  import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
12
12
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
13
- import {stringifyVdiskId} from '../../../utils';
13
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
14
14
  import {getUsage, isFullVDiskData, isSortableStorageProperty} from '../../../utils/storage';
15
15
 
16
16
  import shieldIcon from '../../../assets/icons/shield.svg';
@@ -8,7 +8,7 @@ import {InternalLink} from '../../../components/InternalLink';
8
8
  import routes, {createHref} from '../../../routes';
9
9
  import {EFlag} from '../../../types/api/enums';
10
10
  import {EVDiskState, TVDiskStateInfo} from '../../../types/api/vdisk';
11
- import {stringifyVdiskId} from '../../../utils';
11
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
12
12
  import {isFullVDiskData} from '../../../utils/storage';
13
13
 
14
14
  import {STRUCTURE} from '../../Node/NodePages';
@@ -9,7 +9,7 @@ import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
9
9
 
10
10
  import {EFlag} from '../../../types/api/enums';
11
11
  import type {TVDiskStateInfo} from '../../../types/api/vdisk';
12
- import {stringifyVdiskId} from '../../../utils';
12
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
13
13
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
14
14
  import {isFullVDiskData} from '../../../utils/storage';
15
15
 
@@ -5,7 +5,7 @@ import {Link as UIKitLink} from '@gravity-ui/uikit';
5
5
  import {ETabletState, TTabletStateInfo} from '../../../types/api/tablet';
6
6
  import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
7
7
  import routes, {createHref} from '../../../routes';
8
- import {calcUptime} from '../../../utils';
8
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
9
9
  import {getDefaultNodePath} from '../../Node/NodePages';
10
10
 
11
11
  import {b} from '../Tablet';
@@ -4,7 +4,7 @@ import EntityStatus from '../../../components/EntityStatus/EntityStatus';
4
4
  import {InternalLink} from '../../../components/InternalLink/InternalLink';
5
5
 
6
6
  import type {ITabletPreparedHistoryItem} from '../../../types/store/tablet';
7
- import {calcUptime} from '../../../utils';
7
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
8
8
  import {getDefaultNodePath} from '../../Node/NodePages';
9
9
 
10
10
  import {b} from '../Tablet';
@@ -1,7 +1,7 @@
1
1
  import block from 'bem-cn-lite';
2
2
 
3
3
  import type {IPreparedTopicStats} from '../../../../../types/store/topic';
4
- import {formatMsToUptime} from '../../../../../utils';
4
+ import {formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters';
5
5
  import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
6
6
 
7
7
  import './ConsumersTopicStats.scss';
@@ -6,7 +6,7 @@ import type {IPreparedConsumerData} from '../../../../../types/store/topic';
6
6
  import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
7
7
  import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
8
8
  import {InternalLink} from '../../../../../components/InternalLink';
9
- import {formatMsToUptime} from '../../../../../utils';
9
+ import {formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters';
10
10
  import routes, {createHref} from '../../../../../routes';
11
11
 
12
12
  import {TenantTabsGroups} from '../../../TenantPages';
@@ -8,7 +8,7 @@ import type {EPathType} from '../../../../types/api/schema';
8
8
  import type {AdditionalTenantsProps} from '../../../../types/additionalProps';
9
9
  import {Icon} from '../../../../components/Icon';
10
10
  import {useSetting} from '../../../../utils/hooks';
11
- import {ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN} from '../../../../utils/constants';
11
+ import {DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS} from '../../../../utils/constants';
12
12
  import Overview from '../Overview/Overview';
13
13
  import {Healthcheck} from '../OldHealthcheck';
14
14
  import {TenantOverview} from '../TenantOverview/TenantOverview';
@@ -32,7 +32,7 @@ function DetailedOverview(props: DetailedOverviewProps) {
32
32
 
33
33
  const {currentSchemaPath} = useSelector((state: any) => state.schema);
34
34
 
35
- const [newTenantDiagnostics] = useSetting(ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN);
35
+ const [displayMetricsCards] = useSetting(DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS);
36
36
 
37
37
  const openModalHandler = () => {
38
38
  setIsModalVisible(true);
@@ -59,7 +59,7 @@ function DetailedOverview(props: DetailedOverviewProps) {
59
59
  };
60
60
 
61
61
  const renderTenantOverview = () => {
62
- if (newTenantDiagnostics) {
62
+ if (displayMetricsCards) {
63
63
  return (
64
64
  <div className={b('section')}>
65
65
  <TenantOverview
@@ -8,7 +8,7 @@ import type {
8
8
  import type {KeyValueRow} from '../../../../../types/api/query';
9
9
  import {EPathType} from '../../../../../types/api/schema';
10
10
  import {isNumeric} from '../../../../../utils/utils';
11
- import {formatBytes, formatNumber} from '../../../../../utils';
11
+ import {formatBytes, formatNumber} from '../../../../../utils/dataFormatters/dataFormatters';
12
12
  import {formatDurationToShortTimeFormat} from '../../../../../utils/timeParsers';
13
13
  import {formatObject, InfoViewerItem} from '../../../../../components/InfoViewer';
14
14
  import {
@@ -11,7 +11,7 @@ import {ResponseError} from '../../../../../components/Errors/ResponseError';
11
11
 
12
12
  import {useTypedSelector} from '../../../../../utils/hooks';
13
13
  import {formatDurationToShortTimeFormat} from '../../../../../utils/timeParsers';
14
- import {formatBps, formatBytes} from '../../../../../utils';
14
+ import {formatBps, formatBytes} from '../../../../../utils/dataFormatters/dataFormatters';
15
15
 
16
16
  import {selectPreparedTopicStats} from '../../../../../store/reducers/topic';
17
17
 
@@ -4,7 +4,7 @@ import block from 'bem-cn-lite';
4
4
  import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
5
5
  import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
6
6
  import {getDefaultNodePath} from '../../../../Node/NodePages';
7
- import {formatBytes, formatMsToUptime} from '../../../../../utils';
7
+ import {formatBytes, formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters';
8
8
  import {isNumeric} from '../../../../../utils/utils';
9
9
 
10
10
  import {
@@ -8,7 +8,7 @@ import {InfoViewer} from '../../../../components/InfoViewer';
8
8
  import {PoolUsage} from '../../../../components/PoolUsage/PoolUsage';
9
9
  import {Tablet} from '../../../../components/Tablet';
10
10
  import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
11
- import {formatCPU} from '../../../../utils';
11
+ import {formatCPU} from '../../../../utils/dataFormatters/dataFormatters';
12
12
  import {TABLET_STATES, TENANT_DEFAULT_TITLE} from '../../../../utils/constants';
13
13
  import {bytesToGB} from '../../../../utils/utils';
14
14
  import {mapDatabaseTypeToDBName} from '../../utils/schema';
@@ -28,7 +28,7 @@ import {
28
28
  TENANT_PAGES_IDS,
29
29
  TENANT_QUERY_TABS_ID,
30
30
  } from '../../../../store/reducers/tenant/constants';
31
- import {formatDateTime, formatNumber} from '../../../../utils';
31
+ import {formatDateTime, formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
32
32
  import {HOUR_IN_SECONDS} from '../../../../utils/constants';
33
33
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
34
34
  import {prepareQueryError} from '../../../../utils/query';
@@ -22,7 +22,7 @@ import {EShardsWorkloadMode, IShardsWorkloadFilters} from '../../../../types/sto
22
22
  import type {EPathType} from '../../../../types/api/schema';
23
23
  import type {CellValue, KeyValueRow} from '../../../../types/api/query';
24
24
 
25
- import {formatDateTime, formatNumber} from '../../../../utils';
25
+ import {formatDateTime, formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
26
26
  import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
27
27
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
28
28
  import {prepareQueryError} from '../../../../utils/query';
@@ -24,7 +24,7 @@ import {
24
24
  TColumnTableDescription,
25
25
  } from '../../../types/api/schema';
26
26
  import routes, {createHref} from '../../../routes';
27
- import {formatDateTime} from '../../../utils';
27
+ import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
28
28
  import {useTypedSelector} from '../../../utils/hooks';
29
29
  import {
30
30
  DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED,
@@ -26,7 +26,11 @@ import {
26
26
  ProblemFilterValues,
27
27
  selectProblemFilter,
28
28
  } from '../../store/reducers/settings/settings';
29
- import {formatCPU, formatBytesToGigabyte, formatNumber} from '../../utils';
29
+ import {
30
+ formatBytesToGigabyte,
31
+ formatCPU,
32
+ formatNumber,
33
+ } from '../../utils/dataFormatters/dataFormatters';
30
34
  import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
31
35
  import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
32
36
  import {clusterName} from '../../store';
@@ -25,6 +25,6 @@
25
25
  "settings.enableAdditionalQueryModes.title": "Enable additional query modes",
26
26
  "settings.enableAdditionalQueryModes.popover": "Adds 'Data', 'YQL - QueryService' and 'PostgreSQL' modes. May not work on some versions",
27
27
 
28
- "settings.tenantDiagnostics.title": "Enable new database diagnostics design",
28
+ "settings.tenantDiagnostics.title": "Display metrics cards for database diagnostics",
29
29
  "settings.tenantDiagnostics.popover": "Adds indicators of database resources usage. Incomplete data may be displayed for some databases"
30
30
  }
@@ -25,6 +25,6 @@
25
25
  "settings.enableAdditionalQueryModes.title": "Включить дополнительные режимы выполнения запросов",
26
26
  "settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data', 'YQL - QueryService' и 'PostgreSQL'. Может работать некорректно на некоторых версиях",
27
27
 
28
- "settings.tenantDiagnostics.title": "Включить новый дизайн для диагностики баз данных",
28
+ "settings.tenantDiagnostics.title": "Показывать карточки с метриками в диагностике базы данных",
29
29
  "settings.tenantDiagnostics.popover": "Добавляет индикаторы использования ресурсов базы данных. Для некоторых баз могут отображаться неполные данные"
30
30
  }
@@ -5,7 +5,7 @@ import flaskIcon from '../../assets/icons/flask.svg';
5
5
 
6
6
  import {
7
7
  ENABLE_ADDITIONAL_QUERY_MODES,
8
- ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN,
8
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
9
9
  INVERTED_DISKS_KEY,
10
10
  LANGUAGE_KEY,
11
11
  THEME_KEY,
@@ -96,7 +96,7 @@ export const enableQueryModesForExplainSetting: SettingProps = {
96
96
  helpPopoverContent: i18n('settings.enableAdditionalQueryModes.popover'),
97
97
  };
98
98
  export const enableNewTenantDiagnosticsDesign: SettingProps = {
99
- settingKey: ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN,
99
+ settingKey: DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
100
100
  title: i18n('settings.tenantDiagnostics.title'),
101
101
  helpPopoverContent: i18n('settings.tenantDiagnostics.popover'),
102
102
  };
@@ -3,10 +3,10 @@ import DataTable, {Column} from '@gravity-ui/react-data-table';
3
3
  import type {PreparedClusterNode} from '../../../store/reducers/clusterNodes/types';
4
4
  import {isUnavailableNode} from '../../../utils/nodes';
5
5
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
6
- import {formatBytes} from '../../../utils';
6
+ import {formatBytes} from '../../../utils/dataFormatters/dataFormatters';
7
7
  import {getDefaultNodePath} from '../../Node/NodePages';
8
8
 
9
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
9
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
10
10
  import {PoolsGraph} from '../../../components/PoolsGraph/PoolsGraph';
11
11
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
12
12
 
@@ -352,7 +352,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
352
352
  }
353
353
  getHealthcheckInfo(database: string, {concurrentId}: AxiosOptions = {}) {
354
354
  return this.get<HealthCheckAPIResponse>(
355
- this.getPath('/viewer/json/healthcheck'),
355
+ this.getPath('/viewer/json/healthcheck?merge_records=true'),
356
356
  {tenant: database},
357
357
  {concurrentId},
358
358
  );
@@ -5,7 +5,7 @@ import {createRequestActionTypes, createApiRequest} from '../../utils';
5
5
  import type {ClusterNodesAction, ClusterNodesState, PreparedClusterNode} from './types';
6
6
 
7
7
  import '../../../services/api';
8
- import {calcUptime} from '../../../utils';
8
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
9
9
 
10
10
  export const FETCH_CLUSTER_NODES = createRequestActionTypes('cluster', 'FETCH_CLUSTER_NODES');
11
11
 
@@ -1,7 +1,7 @@
1
1
  import type {Selector} from 'reselect';
2
2
  import {createSelector} from 'reselect';
3
3
 
4
- import {stringifyVdiskId} from '../../../utils';
4
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
5
5
 
6
6
  import type {
7
7
  NodeStateSlice,
@@ -1,7 +1,7 @@
1
1
  import {Selector, createSelector} from 'reselect';
2
2
 
3
3
  import {EFlag} from '../../../types/api/enums';
4
- import {calcUptimeInSeconds} from '../../../utils';
4
+ import {calcUptimeInSeconds} from '../../../utils/dataFormatters/dataFormatters';
5
5
  import {HOUR_IN_SECONDS} from '../../../utils/constants';
6
6
  import {NodesUptimeFilterValues} from '../../../utils/nodes';
7
7
  import {prepareSearchValue} from '../../../utils/filters';
@@ -64,7 +64,7 @@ export interface NodesGeneralRequestParams extends NodesSortParams {
64
64
  uptime?: number; // return nodes with less uptime in seconds
65
65
  problems_only?: boolean; // return nodes with SystemState !== EFlag.Green
66
66
 
67
- offser?: number;
67
+ offset?: number;
68
68
  limit?: number;
69
69
  }
70
70
 
@@ -1,6 +1,6 @@
1
1
  import type {TComputeInfo, TComputeNodeInfo} from '../../../types/api/compute';
2
2
  import type {TNodesInfo} from '../../../types/api/nodes';
3
- import {calcUptime} from '../../../utils';
3
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
4
4
 
5
5
  import type {NodesHandledResponse, NodesPreparedEntity} from './types';
6
6
 
@@ -15,7 +15,7 @@ import {
15
15
  LAST_USED_QUERY_ACTION_KEY,
16
16
  USE_BACKEND_PARAMS_FOR_TABLES_KEY,
17
17
  LANGUAGE_KEY,
18
- ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN,
18
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
19
19
  } from '../../../utils/constants';
20
20
  import '../../../services/api';
21
21
  import {parseJson} from '../../../utils/utils';
@@ -50,14 +50,14 @@ export const initialState = {
50
50
  [INVERTED_DISKS_KEY]: readSavedSettingsValue(INVERTED_DISKS_KEY, 'false'),
51
51
  [USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY]: readSavedSettingsValue(
52
52
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
53
- 'false',
53
+ 'true',
54
54
  ),
55
55
  [ENABLE_ADDITIONAL_QUERY_MODES]: readSavedSettingsValue(
56
56
  ENABLE_ADDITIONAL_QUERY_MODES,
57
57
  'false',
58
58
  ),
59
- [ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN]: readSavedSettingsValue(
60
- ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN,
59
+ [DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS]: readSavedSettingsValue(
60
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
61
61
  'false',
62
62
  ),
63
63
  [SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
@@ -63,7 +63,7 @@ export interface StorageSortParams {
63
63
  export interface StorageSortAndFilterParams extends StorageSortParams {
64
64
  filter?: string; // PoolName or GroupId
65
65
 
66
- offser?: number;
66
+ offset?: number;
67
67
  limit?: number;
68
68
  }
69
69
 
@@ -9,7 +9,7 @@ import {TPDiskState} from '../../../types/api/pdisk';
9
9
  import {EFlag} from '../../../types/api/enums';
10
10
  import {getPDiskType} from '../../../utils/pdisk';
11
11
  import {getUsage} from '../../../utils/storage';
12
- import {calcUptime} from '../../../utils';
12
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
13
13
 
14
14
  import type {PreparedStorageGroup, PreparedStorageNode, PreparedStorageResponse} from './types';
15
15
 
@@ -1,6 +1,6 @@
1
1
  import type {TTenant} from '../../../types/api/tenant';
2
2
  import {formatBytes} from '../../../utils/bytesParsers';
3
- import {formatCPU} from '../../../utils/formatCPU/formatCPU';
3
+ import {formatCPUWithLabel} from '../../../utils/dataFormatters/dataFormatters';
4
4
  import {isNumeric} from '../../../utils/utils';
5
5
  import {METRIC_STATUS} from './contants';
6
6
 
@@ -158,7 +158,7 @@ export const formatTenantMetrics = ({
158
158
  storage?: number;
159
159
  memory?: number;
160
160
  }) => ({
161
- cpu: formatCPU(cpu),
161
+ cpu: formatCPUWithLabel(cpu),
162
162
  storage: formatBytes({value: storage, significantDigits: 2}) || undefined,
163
163
  memory: formatBytes({value: memory, significantDigits: 2}) || undefined,
164
164
  });
@@ -99,7 +99,7 @@ interface TVDiskSatisfactionRank {
99
99
  LevelRank?: TRank;
100
100
  }
101
101
 
102
- interface TVDiskID {
102
+ export interface TVDiskID {
103
103
  GroupID?: number;
104
104
  GroupGeneration?: number;
105
105
  Ring?: number;
@@ -1,4 +1,4 @@
1
- import {formatNumber} from '..';
1
+ import {formatNumber} from '../dataFormatters/dataFormatters';
2
2
  import {GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE} from '../constants';
3
3
  import {isNumeric} from '../utils';
4
4
 
@@ -88,7 +88,8 @@ export const LANGUAGE_KEY = 'language';
88
88
  export const INVERTED_DISKS_KEY = 'invertedDisks';
89
89
  export const USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY = 'useNodesEndpointInDiagnostics';
90
90
  export const ENABLE_ADDITIONAL_QUERY_MODES = 'enableAdditionalQueryModes';
91
- export const ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN = 'enableNewTenantDiagnosticsDesign';
91
+ // Remain key name "enableNewTenantDiagnosticsDesign" for backward compatibility
92
+ export const DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS = 'enableNewTenantDiagnosticsDesign';
92
93
  export const SAVED_QUERIES_KEY = 'saved_queries';
93
94
  export const ASIDE_HEADER_COMPACT_KEY = 'asideHeaderCompact';
94
95
  export const QUERIES_HISTORY_KEY = 'queries_history';
@@ -0,0 +1,128 @@
1
+ import {dateTimeParse} from '@gravity-ui/date-utils';
2
+
3
+ import type {TVDiskID, TVSlotId} from '../../types/api/vdisk';
4
+ import {DAY_IN_SECONDS, GIGABYTE, TERABYTE} from '../constants';
5
+ import {configuredNumeral} from '../numeral';
6
+ import {isNumeric} from '../utils';
7
+
8
+ import i18n from './i18n';
9
+
10
+ // Here you can't control displayed size and precision
11
+ // If you need more custom format, use formatBytesCustom instead
12
+ export const formatBytes = (bytes?: string | number) => {
13
+ if (!isNumeric(bytes)) {
14
+ return '';
15
+ }
16
+
17
+ // by agreement, display byte values in decimal scale
18
+ return configuredNumeral(bytes).format('0 b');
19
+ };
20
+
21
+ export const formatBps = (bytes?: string | number) => {
22
+ const formattedBytes = formatBytes(bytes);
23
+
24
+ if (!formattedBytes) {
25
+ return '';
26
+ }
27
+
28
+ return formattedBytes + '/s';
29
+ };
30
+
31
+ export const formatBytesToGigabyte = (bytes: number | string) => {
32
+ return `${Math.floor(Number(bytes) / GIGABYTE)} GB`;
33
+ };
34
+
35
+ export const stringifyVdiskId = (id?: TVDiskID | TVSlotId) => {
36
+ return id ? Object.values(id).join('-') : '';
37
+ };
38
+
39
+ export const getPDiskId = (info: {NodeId?: number; PDiskId?: number}) => {
40
+ return info.NodeId && info.PDiskId ? `${info.NodeId}-${info.PDiskId}` : undefined;
41
+ };
42
+
43
+ export const formatUptime = (seconds: number) => {
44
+ const days = Math.floor(seconds / DAY_IN_SECONDS);
45
+ const remain = seconds % DAY_IN_SECONDS;
46
+
47
+ const uptime = [days && `${days}d`, configuredNumeral(remain).format('00:00:00')]
48
+ .filter(Boolean)
49
+ .join(' ');
50
+
51
+ return uptime;
52
+ };
53
+
54
+ export const formatMsToUptime = (ms?: number) => {
55
+ return ms && formatUptime(ms / 1000);
56
+ };
57
+
58
+ export const formatStorageValues = (value?: number, total?: number) => {
59
+ return [
60
+ value ? String(Math.floor(value / TERABYTE)) : undefined,
61
+ total ? `${Math.floor(total / TERABYTE)} TB` : undefined,
62
+ ];
63
+ };
64
+ export const formatStorageValuesToGb = (value?: number, total?: number): (string | undefined)[] => {
65
+ return [
66
+ value ? String(Math.floor(value / 1000000000)) : undefined,
67
+ total ? `${Math.floor(total / 1000000000)} GB` : undefined,
68
+ ];
69
+ };
70
+
71
+ export const formatNumber = (number?: unknown) => {
72
+ if (!isNumeric(number)) {
73
+ return '';
74
+ }
75
+
76
+ return configuredNumeral(number).format();
77
+ };
78
+
79
+ const normalizeCPU = (value: number | string) => {
80
+ const rawCores = Number(value) / 1000000;
81
+ let cores = rawCores.toPrecision(3);
82
+ if (rawCores >= 1000) {
83
+ cores = rawCores.toFixed();
84
+ }
85
+ if (rawCores < 0.001) {
86
+ cores = '0';
87
+ }
88
+
89
+ return Number(cores);
90
+ };
91
+
92
+ export const formatCPU = (value?: number | string) => {
93
+ if (value === undefined) {
94
+ return undefined;
95
+ }
96
+
97
+ return configuredNumeral(normalizeCPU(value)).format('0.[000]');
98
+ };
99
+
100
+ export const formatCPUWithLabel = (value?: number) => {
101
+ if (value === undefined) {
102
+ return undefined;
103
+ }
104
+ const cores = normalizeCPU(value);
105
+ const localizedCores = configuredNumeral(cores).format('0.[000]');
106
+
107
+ return `${localizedCores} ${i18n('format-cpu.cores', {count: cores})}`;
108
+ };
109
+
110
+ export const formatDateTime = (value?: number | string) => {
111
+ if (!isNumeric(value)) {
112
+ return '';
113
+ }
114
+
115
+ const formattedData = dateTimeParse(Number(value))?.format('YYYY-MM-DD HH:mm');
116
+
117
+ return Number(value) > 0 && formattedData ? formattedData : 'N/A';
118
+ };
119
+
120
+ export const calcUptimeInSeconds = (milliseconds: number | string) => {
121
+ const currentDate = new Date();
122
+ const diff = currentDate.getTime() - Number(milliseconds);
123
+ return diff <= 0 ? 0 : diff / 1000;
124
+ };
125
+
126
+ export const calcUptime = (milliseconds?: number | string) => {
127
+ return formatUptime(calcUptimeInSeconds(Number(milliseconds)));
128
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "format-cpu.cores": ["core", "cores", "cores", "cores"]
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "format-cpu.cores": ["ядро", "ядра", "ядер", "ядер"]
3
+ }
@@ -1,105 +1,3 @@
1
- import {dateTimeParse} from '@gravity-ui/date-utils';
2
-
3
- import {MEGABYTE, TERABYTE, GIGABYTE, DAY_IN_SECONDS} from './constants';
4
- import {isNumeric} from './utils';
5
- import {configuredNumeral} from './numeral';
6
-
7
- // Here you can't control displayed size and precision
8
- // If you need more custom format, use formatBytesCustom instead
9
- export const formatBytes = (bytes) => {
10
- if (!isNumeric(bytes)) {
11
- return '';
12
- }
13
-
14
- // by agreement, display byte values in decimal scale
15
- return configuredNumeral(bytes).format('0 b');
16
- };
17
-
18
- export const formatBps = (bytes) => {
19
- const formattedBytes = formatBytes(bytes);
20
-
21
- if (!formattedBytes) {
22
- return '';
23
- }
24
-
25
- return formattedBytes + '/s';
26
- };
27
-
28
- export const formatBytesToGigabyte = (bytes) => {
29
- return `${Math.floor(bytes / GIGABYTE)} GB`;
30
- };
31
-
32
- export const stringifyVdiskId = (id) => {
33
- return Object.values(id).join('-');
34
- };
35
- export const getPDiskId = (info) => {
36
- return `${info.NodeId}-${info.PDiskId}`;
37
- };
38
-
39
- export const formatUptime = (seconds) => {
40
- const days = Math.floor(seconds / DAY_IN_SECONDS);
41
- const remain = seconds % DAY_IN_SECONDS;
42
-
43
- const uptime = [days && `${days}d`, configuredNumeral(remain).format('00:00:00')]
44
- .filter(Boolean)
45
- .join(' ');
46
-
47
- return uptime;
48
- };
49
-
50
- export const formatMsToUptime = (ms) => {
51
- return formatUptime(ms / 1000);
52
- };
53
-
54
- export const formatIOPS = (value, capacity) => {
55
- return [Math.floor(value), Math.floor(capacity) + ' IOPS'];
56
- };
57
-
58
- export const formatStorageValues = (value, total) => {
59
- return [Math.floor(value / TERABYTE), `${Math.floor(total / TERABYTE)} TB`];
60
- };
61
- export const formatStorageValuesToGb = (value, total) => {
62
- return [Math.floor(value / 1000000000), `${Math.floor(total / 1000000000)} GB`];
63
- };
64
-
65
- export const formatThroughput = (value, total) => {
66
- return [(value / MEGABYTE).toFixed(2), (total / MEGABYTE).toFixed(1) + ' MB/s'];
67
- };
68
-
69
- export const formatNumber = (number) => {
70
- if (!isNumeric(number)) {
71
- return '';
72
- }
73
-
74
- return configuredNumeral(number).format();
75
- };
76
-
77
- export const formatCPU = (value) => {
78
- if (!isNumeric(value)) {
79
- return '';
80
- }
81
-
82
- return configuredNumeral(value / 1000000).format('0.00');
83
- };
84
-
85
- export const formatDateTime = (value) => {
86
- if (!isNumeric(value)) {
87
- return '';
88
- }
89
-
90
- return value > 0 ? dateTimeParse(Number(value)).format('YYYY-MM-DD HH:mm') : 'N/A';
91
- };
92
-
93
- export const calcUptimeInSeconds = (milliseconds) => {
94
- const currentDate = new Date();
95
- const diff = currentDate - Number(milliseconds);
96
- return diff <= 0 ? 0 : diff / 1000;
97
- };
98
-
99
- export const calcUptime = (milliseconds) => {
100
- return formatUptime(calcUptimeInSeconds(milliseconds));
101
- };
102
-
103
1
  // determine how many nodes have status Connected "true"
104
2
  export const getConnectedNodesCount = (nodeStateInfo) => {
105
3
  return nodeStateInfo?.reduce((acc, item) => (item.Connected ? acc + 1 : acc), 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.17.0",
3
+ "version": "4.18.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -1,92 +0,0 @@
1
- import React from 'react';
2
- import cn from 'bem-cn-lite';
3
- import './ProgressViewer.scss';
4
- import PropTypes from 'prop-types';
5
- const b = cn('progress-viewer');
6
- /*
7
-
8
- Описание props:
9
- 1) value - величина прогресса
10
- 2) capacity - предельно возможный прогресс
11
- 3) formatValues - функция форматирования value и capacity
12
- 4) percents - отображать ли заполненость в виде процентов
13
- 5) className - кастомный класс
14
- 6) colorizeProgress - менять ли цвет полосы прогресса в зависимости от его величины
15
- 7) inverseColorize - инвертировать ли цвета при разукрашивании прогресса
16
- */
17
- export class ProgressViewer extends React.Component {
18
- static propTypes = {
19
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
20
- capacity: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
21
- formatValues: PropTypes.func,
22
- percents: PropTypes.bool,
23
- className: PropTypes.string,
24
- size: PropTypes.oneOf(['xs', 's', 'ns', 'm', 'n', 'l', 'head']),
25
- colorizeProgress: PropTypes.bool,
26
- inverseColorize: PropTypes.bool,
27
- };
28
-
29
- static defaultProps = {
30
- size: 'xs',
31
- colorizeProgress: false,
32
- capacity: 100,
33
- inverseColorize: false,
34
- };
35
-
36
- render() {
37
- const {
38
- value,
39
- capacity,
40
- formatValues,
41
- percents,
42
- size,
43
- className,
44
- colorizeProgress,
45
- inverseColorize,
46
- } = this.props;
47
-
48
- let fillWidth = Math.round((parseFloat(value) / parseFloat(capacity)) * 100);
49
- fillWidth = fillWidth > 100 ? 100 : fillWidth;
50
-
51
- let valueText = Math.round(value),
52
- capacityText = capacity,
53
- divider = '/';
54
- if (formatValues) {
55
- [valueText, capacityText] = formatValues(value, capacity);
56
- } else if (percents) {
57
- valueText = fillWidth + '%';
58
- capacityText = '';
59
- divider = '';
60
- }
61
-
62
- let bg = inverseColorize ? 'scarlet' : 'apple';
63
- if (colorizeProgress) {
64
- if (fillWidth > 60 && fillWidth <= 80) {
65
- bg = 'saffron';
66
- } else if (fillWidth > 80) {
67
- bg = inverseColorize ? 'apple' : 'scarlet';
68
- }
69
- }
70
-
71
- const lineStyle = {
72
- width: fillWidth + '%',
73
- };
74
-
75
- const text = fillWidth > 60 ? 'contrast0' : 'contrast70';
76
-
77
- if (!isNaN(fillWidth)) {
78
- return (
79
- <div className={b({size}, className)}>
80
- <div className={b('line', {bg})} style={lineStyle}></div>
81
- <span className={b('text', {text})}>
82
- {`${valueText} ${divider} ${capacityText}`}
83
- </span>
84
- </div>
85
- );
86
- }
87
-
88
- return <div className={`${b({size})} ${className} error`}>no data</div>;
89
- }
90
- }
91
-
92
- export default ProgressViewer;
@@ -1,20 +0,0 @@
1
- import {configuredNumeral} from '../numeral';
2
- import i18n from './i18n';
3
-
4
- export const formatCPU = (value?: number) => {
5
- if (value === undefined) {
6
- return undefined;
7
- }
8
-
9
- const rawCores = value / 1000000;
10
- let cores = rawCores.toPrecision(3);
11
- if (rawCores >= 1000) {
12
- cores = rawCores.toFixed();
13
- }
14
- if (rawCores < 0.001) {
15
- cores = '0';
16
- }
17
- const localizedCores = configuredNumeral(Number(cores)).format('0.[000]');
18
-
19
- return `${localizedCores} ${i18n('cores', {count: cores})}`;
20
- };
@@ -1,3 +0,0 @@
1
- {
2
- "cores": ["core", "cores", "cores", "cores"]
3
- }
@@ -1,3 +0,0 @@
1
- {
2
- "cores": ["ядро", "ядра", "ядер", "ядер"]
3
- }