Skip to content

Commit 714e7c7

Browse files
authored
feat(Cluster): redesign cluster dashboard (#2176)
1 parent 9c77257 commit 714e7c7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1054
-717
lines changed

src/assets/icons/overview.svg

Lines changed: 1 addition & 0 deletions
Loading

src/assets/icons/user-check.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/components/DoughnutMetrics/DoughnutMetrics.scss

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
.ydb-doughnut-metrics {
2-
--doughnut-border: 11px;
2+
--doughnut-border: 16px;
33
--doughnut-color: var(--ydb-color-status-green);
4+
--doughnut-backdrop-color: var(--g-color-base-positive-light);
5+
--doughnut-overlap-color: var(--g-color-base-positive-heavy-hover);
46
&__doughnut {
57
position: relative;
68

7-
width: 172px;
9+
width: 100px;
810
aspect-ratio: 1;
911

1012
border-radius: 50%;
11-
background-color: var(--doughnut-color);
13+
14+
transform: rotate(180deg);
1215
&::before {
1316
display: block;
1417

@@ -25,9 +28,13 @@
2528
}
2629
&__doughnut_status_warning {
2730
--doughnut-color: var(--ydb-color-status-yellow);
31+
--doughnut-backdrop-color: var(--g-color-base-warning-light);
32+
--doughnut-overlap-color: var(--g-color-base-warning-heavy-hover);
2833
}
2934
&__doughnut_status_danger {
3035
--doughnut-color: var(--ydb-color-status-red);
36+
--doughnut-backdrop-color: var(--g-color-base-danger-light);
37+
--doughnut-overlap-color: var(--g-color-base-danger-heavy-hover);
3138
}
3239
&__text-wrapper {
3340
--wrapper-indent: calc(var(--doughnut-border) + 5px);
@@ -44,15 +51,15 @@
4451
width: calc(100% - calc(var(--wrapper-indent) * 2));
4552

4653
text-align: center;
54+
55+
transform: rotate(180deg);
4756
aspect-ratio: 1;
4857
}
4958
&__value {
5059
position: absolute;
5160
bottom: 20px;
5261
}
53-
&__legend {
54-
height: 50%;
55-
56-
white-space: pre-wrap;
62+
&__legend-note {
63+
display: flex;
5764
}
5865
}

src/components/DoughnutMetrics/DoughnutMetrics.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22

33
import type {TextProps} from '@gravity-ui/uikit';
4-
import {Text} from '@gravity-ui/uikit';
4+
import {Flex, HelpMark, Text} from '@gravity-ui/uikit';
55

66
import {cn} from '../../utils/cn';
77
import type {ProgressStatus} from '../../utils/progress';
@@ -13,18 +13,23 @@ const b = cn('ydb-doughnut-metrics');
1313
interface LegendProps {
1414
children?: React.ReactNode;
1515
variant?: TextProps['variant'];
16+
color?: TextProps['color'];
17+
note?: React.ReactNode;
1618
}
1719

18-
function Legend({children, variant = 'subheader-3'}: LegendProps) {
20+
function Legend({children, variant = 'subheader-3', color = 'primary', note}: LegendProps) {
1921
return (
20-
<Text variant={variant} color="secondary" className={b('legend')}>
21-
{children}
22-
</Text>
22+
<Flex gap={1} alignItems="center">
23+
<Text variant={variant} color={color} className={b('legend')} as="div">
24+
{children}
25+
</Text>
26+
{note && <HelpMark className={b('legend-note')}>{note}</HelpMark>}
27+
</Flex>
2328
);
2429
}
2530
function Value({children, variant = 'subheader-2'}: LegendProps) {
2631
return (
27-
<Text variant={variant} color="secondary" className={b('value')}>
32+
<Text variant={variant} className={b('value')}>
2833
{children}
2934
</Text>
3035
);
@@ -38,19 +43,21 @@ interface DoughnutProps {
3843
}
3944

4045
export function DoughnutMetrics({status, fillWidth, children, className}: DoughnutProps) {
41-
let gradientFill = 'var(--g-color-line-generic-solid)';
42-
let filledDegrees = fillWidth * 3.6 - 90;
46+
let filledDegrees = fillWidth * 3.6;
47+
let doughnutFillVar = 'var(--doughnut-color)';
48+
let doughnutBackdropVar = 'var(--doughnut-backdrop-color)';
4349

44-
if (fillWidth > 50) {
45-
gradientFill = 'var(--doughnut-color)';
46-
filledDegrees = fillWidth * 3.6 + 90;
50+
if (filledDegrees > 360) {
51+
filledDegrees -= 360;
52+
doughnutBackdropVar = 'var(--doughnut-color)';
53+
doughnutFillVar = 'var(--doughnut-overlap-color)';
4754
}
48-
const gradientDegrees = filledDegrees;
55+
4956
return (
5057
<div className={b(null, className)}>
5158
<div
5259
style={{
53-
backgroundImage: `linear-gradient(${gradientDegrees}deg, transparent 50%, ${gradientFill} 50%), linear-gradient(-90deg, var(--g-color-line-generic-solid) 50%, transparent 50%)`,
60+
background: `conic-gradient(${doughnutFillVar} 0deg ${filledDegrees}deg, ${doughnutBackdropVar} ${filledDegrees}deg 360deg)`,
5461
}}
5562
className={b('doughnut', {status})}
5663
>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.ydb-entity-status-new {
2+
.g-help-mark__button {
3+
color: inherit;
4+
}
5+
6+
&_orange.g-label {
7+
color: var(--g-color-private-orange-500);
8+
background-color: var(--g-color-private-orange-100);
9+
}
10+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React from 'react';
2+
3+
import type {LabelProps} from '@gravity-ui/uikit';
4+
import {ActionTooltip, Flex, HelpMark, Label} from '@gravity-ui/uikit';
5+
6+
import {EFlag} from '../../types/api/enums';
7+
import {cn} from '../../utils/cn';
8+
import {StatusIcon} from '../StatusIconNew/StatusIcon';
9+
10+
import i18n from './i18n';
11+
import {EFlagToDescription} from './utils';
12+
13+
import './EntityStatus.scss';
14+
15+
const b = cn('ydb-entity-status-new');
16+
17+
const EFlagToLabelTheme: Record<EFlag, LabelProps['theme'] | 'orange'> = {
18+
[EFlag.Red]: 'danger',
19+
[EFlag.Blue]: 'info',
20+
[EFlag.Green]: 'success',
21+
[EFlag.Grey]: 'unknown',
22+
[EFlag.Orange]: 'orange',
23+
[EFlag.Yellow]: 'warning',
24+
};
25+
26+
const EFlagToStatusName: Record<EFlag, string> = {
27+
get [EFlag.Red]() {
28+
return i18n('title_red');
29+
},
30+
get [EFlag.Yellow]() {
31+
return i18n('title_yellow');
32+
},
33+
get [EFlag.Orange]() {
34+
return i18n('title_orange');
35+
},
36+
get [EFlag.Green]() {
37+
return i18n('title_green');
38+
},
39+
get [EFlag.Grey]() {
40+
return i18n('title_grey');
41+
},
42+
get [EFlag.Blue]() {
43+
return i18n('title_blue');
44+
},
45+
};
46+
47+
interface EntityStatusLabelProps {
48+
status: EFlag;
49+
note?: React.ReactNode;
50+
children?: React.ReactNode;
51+
withStatusName?: boolean;
52+
size?: LabelProps['size'];
53+
iconSize?: number;
54+
}
55+
56+
function EntityStatusLabel({
57+
children,
58+
status,
59+
withStatusName = true,
60+
note,
61+
size = 'm',
62+
iconSize = 14,
63+
}: EntityStatusLabelProps) {
64+
const theme = EFlagToLabelTheme[status];
65+
return (
66+
<ActionTooltip title={EFlagToDescription[status]} disabled={Boolean(note)}>
67+
<Label
68+
theme={theme === 'orange' ? undefined : theme}
69+
icon={<StatusIcon size={iconSize} status={status} />}
70+
size={size}
71+
className={b({orange: theme === 'orange'})}
72+
>
73+
<Flex gap="2" wrap="nowrap">
74+
{children}
75+
{withStatusName ? EFlagToStatusName[status] : null}
76+
{note && <HelpMark>{note}</HelpMark>}
77+
</Flex>
78+
</Label>
79+
</ActionTooltip>
80+
);
81+
}
82+
83+
interface EntityStatusProps {
84+
children?: React.ReactNode;
85+
className?: string;
86+
}
87+
88+
export function EntityStatus({className, children}: EntityStatusProps) {
89+
return (
90+
<Flex gap="2" wrap="nowrap" alignItems="center" className={b(null, className)}>
91+
{children}
92+
</Flex>
93+
);
94+
}
95+
96+
EntityStatus.Label = EntityStatusLabel;
97+
EntityStatus.displayName = 'EntityStatus';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"title_red": "Failed",
3+
"title_blue": "Normal",
4+
"title_green": "Good",
5+
"title_grey": "Unknown",
6+
"title_orange": "Caution",
7+
"title_yellow": "Warning",
8+
"context_red": "Some systems are failed and not available",
9+
"context_yellow": "There are minor issues",
10+
"context_orange": "Critical state, requires immediate attention",
11+
"context_green": "Everything is working as expected",
12+
"context_grey": "The condition cannot be determined",
13+
"context_blue": "All good, some parts of the system are restoring"
14+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {registerKeysets} from '../../../utils/i18n';
2+
3+
import en from './en.json';
4+
5+
const COMPONENT = 'ydb-entity-status';
6+
7+
export default registerKeysets(COMPONENT, {en});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {EFlag} from '../../types/api/enums';
2+
3+
import i18n from './i18n';
4+
5+
export const EFlagToDescription: Record<EFlag, string> = {
6+
get [EFlag.Red]() {
7+
return i18n('context_red');
8+
},
9+
get [EFlag.Yellow]() {
10+
return i18n('context_yellow');
11+
},
12+
get [EFlag.Orange]() {
13+
return i18n('context_orange');
14+
},
15+
get [EFlag.Green]() {
16+
return i18n('context_green');
17+
},
18+
get [EFlag.Grey]() {
19+
return i18n('context_grey');
20+
},
21+
get [EFlag.Blue]() {
22+
return i18n('context_blue');
23+
},
24+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {
2+
CircleCheck,
3+
CircleExclamation,
4+
CircleInfo,
5+
PlugConnection,
6+
TriangleExclamation,
7+
} from '@gravity-ui/icons';
8+
import type {IconProps} from '@gravity-ui/uikit';
9+
import {Icon} from '@gravity-ui/uikit';
10+
11+
import {EFlag} from '../../types/api/enums';
12+
13+
const EFlagToIcon: Record<EFlag, (props: React.SVGProps<SVGSVGElement>) => React.JSX.Element> = {
14+
[EFlag.Blue]: CircleInfo,
15+
[EFlag.Yellow]: CircleExclamation,
16+
[EFlag.Orange]: TriangleExclamation,
17+
[EFlag.Red]: CircleExclamation,
18+
[EFlag.Green]: CircleCheck,
19+
[EFlag.Grey]: PlugConnection,
20+
};
21+
22+
interface StatusIconProps extends Omit<IconProps, 'data'> {
23+
status?: EFlag;
24+
}
25+
26+
export function StatusIcon({status, ...props}: StatusIconProps) {
27+
if (!status) {
28+
return null;
29+
}
30+
return <Icon {...props} data={EFlagToIcon[status]} />;
31+
}

0 commit comments

Comments
 (0)