Skip to content

Commit 8a752e0

Browse files
authored
feat: update TenantOverview design (#527)
1 parent 0fc6c46 commit 8a752e0

File tree

31 files changed

+767
-84
lines changed

31 files changed

+767
-84
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.ydb-circular-progress-bar {
2+
display: block;
3+
4+
max-width: 100%;
5+
6+
transform: rotate(90deg);
7+
8+
&__wrapper {
9+
position: relative;
10+
11+
display: inline;
12+
}
13+
&__content {
14+
position: absolute;
15+
top: 50%;
16+
left: 50%;
17+
18+
overflow: hidden;
19+
20+
max-width: 100%;
21+
22+
transform: translate(-50%, -50%);
23+
}
24+
25+
&__circle-bg {
26+
stroke: var(--yc-color-base-simple-hover);
27+
28+
fill: none;
29+
30+
transition: stroke 0.1s;
31+
}
32+
33+
&__circle {
34+
fill: none;
35+
36+
&_status_good {
37+
stroke: var(--yc-color-private-green-550);
38+
}
39+
&_status_warning {
40+
stroke: var(--yc-color-private-yellow-550);
41+
}
42+
&_status_danger {
43+
stroke: var(--yc-color-private-red-550);
44+
}
45+
}
46+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import cn from 'bem-cn-lite';
2+
import type {ReactNode} from 'react';
3+
4+
import type {MetricStatus} from '../../store/reducers/tenants/types';
5+
import {normalizeProgress} from '../../store/reducers/tenants/utils';
6+
7+
import './CircularProgressBar.scss';
8+
9+
const b = cn('ydb-circular-progress-bar');
10+
11+
interface CircularProgressBarProps {
12+
size?: number;
13+
progress?: number;
14+
strokeWidth?: number;
15+
content?: ReactNode;
16+
status?: MetricStatus;
17+
circleBgClassName?: string;
18+
}
19+
20+
export function CircularProgressBar({
21+
size = 100,
22+
progress = 0,
23+
strokeWidth = 10,
24+
content,
25+
status,
26+
circleBgClassName,
27+
}: CircularProgressBarProps) {
28+
const center = size / 2;
29+
30+
const radius = size / 2 - strokeWidth / 2;
31+
const circumference = 2 * Math.PI * radius;
32+
33+
const normalizedProgress = normalizeProgress(progress);
34+
35+
const offset = ((100 - normalizedProgress) / 100) * circumference;
36+
return (
37+
<div className={b('wrapper')}>
38+
{content && <div className={b('content')}>{content}</div>}
39+
<svg className={b()} width={size} height={size}>
40+
<circle
41+
className={b('circle-bg', circleBgClassName)}
42+
cx={center}
43+
cy={center}
44+
r={radius}
45+
strokeWidth={strokeWidth}
46+
/>
47+
<circle
48+
className={b('circle', {status: status?.toLocaleLowerCase()})}
49+
cx={center}
50+
cy={center}
51+
r={radius}
52+
strokeWidth={strokeWidth}
53+
strokeDasharray={circumference}
54+
strokeDashoffset={offset}
55+
/>
56+
</svg>
57+
</div>
58+
);
59+
}
Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
1-
.diagnostic-card {
2-
min-width: 200px;
3-
max-width: 350px;
1+
.ydb-diagnostic-card {
2+
flex-shrink: 0;
3+
4+
width: 206px;
45
padding: 16px;
6+
padding-bottom: 28px;
7+
8+
border: 1px solid var(--yc-color-line-generic);
9+
border-radius: 8px;
10+
background-color: transparent;
11+
12+
transition: all 0.1s;
13+
transition-property: box-shadow, background-color, border-color;
14+
15+
&_active {
16+
border-color: var(--yc-color-base-info-heavy);
17+
background-color: var(--yc-color-base-selection);
18+
}
19+
20+
&:hover {
21+
cursor: pointer;
22+
23+
box-shadow: 0px 1px 5px var(--yc-color-sfx-shadow);
24+
}
525
}
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
import {ReactNode} from 'react';
1+
import type {ReactNode} from 'react';
22
import cn from 'bem-cn-lite';
33

4-
import {Card} from '@gravity-ui/uikit';
5-
64
import './DiagnosticCard.scss';
75

8-
const b = cn('diagnostic-card');
6+
const b = cn('ydb-diagnostic-card');
97

108
interface DiagnosticCardProps {
119
children?: ReactNode;
1210
className?: string;
11+
active?: boolean;
1312
}
1413

15-
export function DiagnosticCard({children, className}: DiagnosticCardProps) {
16-
return <Card className={b(null, className)}>{children}</Card>;
14+
export function DiagnosticCard({children, className, active}: DiagnosticCardProps) {
15+
return <div className={b({active}, className)}>{children}</div>;
1716
}

src/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ $section-title-line-height: 24px;
1010

1111
&__section {
1212
display: flex;
13-
overflow-x: hidden;
14-
flex: 0 0 calc(50% - 10px);
13+
flex: 1 0 calc(50% - 10px);
1514
flex-direction: column;
1615

1716
min-width: 300px;

src/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import {Button, Modal} from '@gravity-ui/uikit';
77
import type {EPathType} from '../../../../types/api/schema';
88
import type {AdditionalTenantsProps} from '../../../../types/additionalProps';
99
import {Icon} from '../../../../components/Icon';
10+
import {useSetting} from '../../../../utils/hooks';
11+
import {ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN} from '../../../../utils/constants';
1012
import Overview from '../Overview/Overview';
1113
import {Healthcheck} from '../Healthcheck';
1214
import {TenantOverview} from '../TenantOverview/TenantOverview';
15+
import {OldTenantOverview} from '../TenantOverview/OldTenantOverview';
1316

1417
import './DetailedOverview.scss';
1518

@@ -23,10 +26,14 @@ interface DetailedOverviewProps {
2326
const b = cn('kv-detailed-overview');
2427

2528
function DetailedOverview(props: DetailedOverviewProps) {
29+
const {type, tenantName, additionalTenantProps} = props;
30+
2631
const [isModalVisible, setIsModalVisible] = useState(false);
2732

2833
const {currentSchemaPath} = useSelector((state: any) => state.schema);
2934

35+
const [newTenantDiagnostics] = useSetting(ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN);
36+
3037
const openModalHandler = () => {
3138
setIsModalVisible(true);
3239
};
@@ -51,27 +58,44 @@ function DetailedOverview(props: DetailedOverviewProps) {
5158
);
5259
};
5360

61+
const renderTenantOverview = () => {
62+
if (newTenantDiagnostics) {
63+
return (
64+
<div className={b('section')}>
65+
<TenantOverview
66+
tenantName={tenantName}
67+
additionalTenantProps={additionalTenantProps}
68+
showMoreHandler={openModalHandler}
69+
/>
70+
</div>
71+
);
72+
}
73+
74+
return (
75+
<>
76+
<div className={b('section')}>
77+
<OldTenantOverview
78+
tenantName={tenantName}
79+
additionalTenantProps={additionalTenantProps}
80+
/>
81+
</div>
82+
<div className={b('section')}>
83+
<Healthcheck
84+
tenant={tenantName}
85+
preview={true}
86+
showMoreHandler={openModalHandler}
87+
/>
88+
</div>
89+
</>
90+
);
91+
};
92+
5493
const renderContent = () => {
55-
const {type, tenantName, additionalTenantProps} = props;
5694
const isTenant = tenantName === currentSchemaPath;
5795
return (
5896
<div className={b()}>
5997
{isTenant ? (
60-
<>
61-
<div className={b('section')}>
62-
<TenantOverview
63-
tenantName={tenantName}
64-
additionalTenantProps={additionalTenantProps}
65-
/>
66-
</div>
67-
<div className={b('section')}>
68-
<Healthcheck
69-
tenant={tenantName}
70-
preview={true}
71-
showMoreHandler={openModalHandler}
72-
/>
73-
</div>
74-
</>
98+
renderTenantOverview()
7599
) : (
76100
<Overview type={type} tenantName={tenantName} />
77101
)}

src/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,34 +53,42 @@
5353
margin-left: 10px;
5454
}
5555

56-
&__status-wrapper {
57-
display: flex;
58-
56+
&__preview-header {
5957
margin-bottom: detailedOverview.$section-title-margin;
6058
gap: 8px;
6159
}
6260

6361
&__preview-title {
6462
font-weight: 600;
65-
line-height: detailedOverview.$section-title-line-height;
63+
@include lead-typography();
6664
}
6765

6866
&__preview-content {
6967
line-height: 24px;
7068
}
7169

70+
&__preview-title-wrapper {
71+
display: flex;
72+
align-items: center;
73+
74+
margin-bottom: 4px;
75+
76+
gap: 8px;
77+
}
78+
7279
&__issues-statistics {
7380
display: flex;
7481
flex-wrap: wrap;
7582
align-items: center;
7683

77-
margin: 10px 0;
84+
margin: 8px 0;
7885

79-
column-gap: 26px;
80-
row-gap: 16px;
86+
gap: 10px;
8187
}
8288

8389
&__self-check-status-indicator {
90+
display: inline-block;
91+
8492
padding: 0 8px;
8593

8694
font-size: 13px;

src/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,23 @@ interface HealthcheckProps {
2424
preview?: boolean;
2525
fetchData?: boolean;
2626
showMoreHandler?: VoidFunction;
27+
active?: boolean;
2728
}
2829

2930
const b = cn('healthcheck');
3031

3132
export const Healthcheck = (props: HealthcheckProps) => {
32-
const {tenant, preview, fetchData = true, showMoreHandler} = props;
33+
const {tenant, preview, fetchData = true, showMoreHandler, active} = props;
3334

3435
const dispatch = useDispatch();
3536

3637
const {data, loading, wasLoaded, error} = useTypedSelector((state) => state.healthcheckInfo);
37-
const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
38-
3938
const issuesStatistics = useTypedSelector(selectIssuesStatistics);
4039
const issueTrees = useTypedSelector(selectIssuesTrees);
4140
const {autorefresh} = useTypedSelector((state) => state.schema);
4241

42+
const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
43+
4344
const fetchHealthcheck = useCallback(
4445
(isBackground = true) => {
4546
if (!isBackground) {
@@ -77,6 +78,7 @@ export const Healthcheck = (props: HealthcheckProps) => {
7778
onShowMore={showMoreHandler}
7879
onUpdate={fetchHealthcheck}
7980
error={error}
81+
active={active}
8082
/>
8183
) : (
8284
<Details

src/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,28 @@ interface PreviewProps {
2121
onShowMore?: VoidFunction;
2222
onUpdate: VoidFunction;
2323
error?: IResponseError;
24+
active?: boolean;
2425
}
2526

2627
export const Preview = (props: PreviewProps) => {
27-
const {selfCheckResult, issuesStatistics, loading, onShowMore, onUpdate, error} = props;
28+
const {selfCheckResult, issuesStatistics, loading, onShowMore, onUpdate, error, active} = props;
2829

2930
const isStatusOK = selfCheckResult === SelfCheckResult.GOOD;
3031

31-
const renderStatus = () => {
32+
const renderHeader = () => {
3233
const modifier = selfCheckResult.toLowerCase();
3334

3435
return (
35-
<div className={b('status-wrapper')}>
36-
<div className={b('preview-title')}>{i18n('title.healthcheck')}</div>
36+
<div className={b('preview-header')}>
37+
<div className={b('preview-title-wrapper')}>
38+
<div className={b('preview-title')}>{i18n('title.healthcheck')}</div>
39+
<Button size="s" onClick={onUpdate} loading={loading} view="flat-secondary">
40+
<Icon data={updateArrow} width={20} height={20} />
41+
</Button>
42+
</div>
3743
<div className={b('self-check-status-indicator', {[modifier]: true})}>
3844
{selfCheckResult}
3945
</div>
40-
<Button size="s" onClick={onUpdate} loading={loading} view="flat-secondary">
41-
<Icon data={updateArrow} width={20} height={20} />
42-
</Button>
4346
</div>
4447
);
4548
};
@@ -55,7 +58,7 @@ export const Preview = (props: PreviewProps) => {
5558
i18n('status_message.ok')
5659
) : (
5760
<>
58-
<div>Issues:</div>
61+
<div>{i18n('label.issues')}</div>
5962
<div className={b('issues-statistics')}>
6063
{issuesStatistics.map(([status, count]) => (
6164
<EntityStatus
@@ -75,8 +78,8 @@ export const Preview = (props: PreviewProps) => {
7578
};
7679

7780
return (
78-
<DiagnosticCard className={b('preview')}>
79-
{renderStatus()}
81+
<DiagnosticCard active={active}>
82+
{renderHeader()}
8083
{renderContent()}
8184
</DiagnosticCard>
8285
);

0 commit comments

Comments
 (0)