Skip to content

Commit 11e3cc1

Browse files
committed
feat(FR-1368): add shared deployment UI components — ReplicaStatusTag, DeploymentStatusTag, DeploymentOwnerInfo (#6910)
Resolves #6910 ## Summary Consolidates shared display components used across the deployment list, detail, and launcher UIs (FR-2667, FR-2668, FR-2669): - **FR-2667**: `ReplicaStatusTag` — color-coded badge for replica health status with tooltip; includes Storybook story - **FR-2668**: `DeploymentStatusTag` — antd Tag with semantic color mapping for deployment lifecycle states - **FR-2669**: `DeploymentOwnerInfo` — Relay-fragment component showing owner avatar and name for a deployment All three components are purely presentational and carry no side effects.
1 parent e730270 commit 11e3cc1

3 files changed

Lines changed: 257 additions & 0 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
@license
3+
Copyright (c) 2015-2026 Lablup Inc. All rights reserved.
4+
*/
5+
import { DeploymentOwnerInfo_deployment$key } from '../__generated__/DeploymentOwnerInfo_deployment.graphql';
6+
import { UserOutlined } from '@ant-design/icons';
7+
import { Avatar, Tooltip, Typography, theme } from 'antd';
8+
import { BAIFlex } from 'backend.ai-ui';
9+
import React from 'react';
10+
import { useTranslation } from 'react-i18next';
11+
import { graphql, useFragment } from 'react-relay';
12+
13+
interface DeploymentOwnerInfoProps {
14+
deploymentFrgmt: DeploymentOwnerInfo_deployment$key | null | undefined;
15+
}
16+
17+
const DeploymentOwnerInfo: React.FC<DeploymentOwnerInfoProps> = ({
18+
deploymentFrgmt,
19+
}) => {
20+
'use memo';
21+
const { t } = useTranslation();
22+
const { token } = theme.useToken();
23+
24+
const deployment = useFragment(
25+
graphql`
26+
fragment DeploymentOwnerInfo_deployment on ModelDeployment {
27+
id
28+
creator @since(version: "26.4.3") {
29+
id
30+
basicInfo {
31+
email
32+
username
33+
fullName
34+
}
35+
}
36+
}
37+
`,
38+
deploymentFrgmt,
39+
);
40+
41+
const email = deployment?.creator?.basicInfo?.email ?? '';
42+
const fullName = deployment?.creator?.basicInfo?.fullName ?? '';
43+
const username = deployment?.creator?.basicInfo?.username ?? '';
44+
45+
if (!email) {
46+
return <Typography.Text type="secondary">-</Typography.Text>;
47+
}
48+
49+
const initial = (fullName || username || email)
50+
.trim()
51+
.charAt(0)
52+
.toUpperCase();
53+
const tooltipLines = [
54+
t('deployment.CreatedBy'),
55+
fullName || username || email,
56+
email,
57+
]
58+
.filter(Boolean)
59+
.join('\n');
60+
61+
return (
62+
<BAIFlex gap="xs" align="center">
63+
<Tooltip
64+
title={<span style={{ whiteSpace: 'pre-line' }}>{tooltipLines}</span>}
65+
>
66+
<Avatar
67+
size="small"
68+
style={{
69+
backgroundColor: token.colorFillSecondary,
70+
color: token.colorText,
71+
flexShrink: 0,
72+
}}
73+
icon={!initial ? <UserOutlined /> : undefined}
74+
>
75+
{initial || null}
76+
</Avatar>
77+
</Tooltip>
78+
<Typography.Text ellipsis={{ tooltip: email }} style={{ maxWidth: 200 }}>
79+
{email}
80+
</Typography.Text>
81+
</BAIFlex>
82+
);
83+
};
84+
85+
export default DeploymentOwnerInfo;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
@license
3+
Copyright (c) 2015-2026 Lablup Inc. All rights reserved.
4+
*/
5+
import type { TagProps } from 'antd';
6+
import { BAITag, type SemanticColor } from 'backend.ai-ui';
7+
import React from 'react';
8+
import { useTranslation } from 'react-i18next';
9+
10+
export type DeploymentStatus =
11+
| 'HEALTHY'
12+
| 'UNHEALTHY'
13+
| 'DEGRADED'
14+
| 'NOT_CHECKED'
15+
| 'DEPLOYING'
16+
| 'SCALING'
17+
| 'STOPPED'
18+
| 'STOPPING'
19+
| 'TERMINATED'
20+
| 'PENDING'
21+
| 'READY';
22+
23+
/**
24+
* Maps each deployment status to a Tag color.
25+
*
26+
* - success: HEALTHY, READY — fully operational.
27+
* - processing: DEPLOYING, SCALING, PENDING — transient, in-flight states.
28+
* - warning: DEGRADED, UNHEALTHY, STOPPING — attention needed or transitioning away.
29+
* - default: NOT_CHECKED, STOPPED, TERMINATED — neutral / inactive.
30+
*/
31+
const deploymentStatusColorMap: Record<DeploymentStatus, SemanticColor> = {
32+
HEALTHY: 'success',
33+
READY: 'success',
34+
DEPLOYING: 'info',
35+
SCALING: 'info',
36+
PENDING: 'info',
37+
DEGRADED: 'warning',
38+
UNHEALTHY: 'warning',
39+
STOPPING: 'warning',
40+
NOT_CHECKED: 'default',
41+
STOPPED: 'default',
42+
TERMINATED: 'default',
43+
};
44+
45+
const deploymentStatusI18nMap: Record<DeploymentStatus, string> = {
46+
HEALTHY: 'deployment.status.Healthy',
47+
UNHEALTHY: 'deployment.status.Unhealthy',
48+
DEGRADED: 'deployment.status.Degraded',
49+
NOT_CHECKED: 'deployment.status.NotChecked',
50+
DEPLOYING: 'deployment.status.Deploying',
51+
SCALING: 'deployment.status.Scaling',
52+
STOPPED: 'deployment.status.Stopped',
53+
STOPPING: 'deployment.status.Stopping',
54+
TERMINATED: 'deployment.status.Terminated',
55+
PENDING: 'deployment.status.Pending',
56+
READY: 'deployment.status.Ready',
57+
};
58+
59+
export interface DeploymentStatusTagProps extends Omit<TagProps, 'color'> {
60+
/**
61+
* The deployment-level status to display. Consolidates lifecycle (e.g.
62+
* `DEPLOYING`, `STOPPED`, `TERMINATED`) and health (e.g. `HEALTHY`,
63+
* `UNHEALTHY`, `DEGRADED`) into a single tag.
64+
*/
65+
status: DeploymentStatus;
66+
}
67+
68+
/**
69+
* DeploymentStatusTag — consolidated lifecycle + health status tag for a
70+
* deployment. Used in list rows and detail page headers.
71+
*/
72+
const DeploymentStatusTag: React.FC<DeploymentStatusTagProps> = ({
73+
status,
74+
...tagProps
75+
}) => {
76+
'use memo';
77+
const { t } = useTranslation();
78+
79+
return (
80+
<BAITag {...tagProps} color={deploymentStatusColorMap[status]}>
81+
{t(deploymentStatusI18nMap[status])}
82+
</BAITag>
83+
);
84+
};
85+
86+
export default DeploymentStatusTag;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
@license
3+
Copyright (c) 2015-2026 Lablup Inc. All rights reserved.
4+
*/
5+
import { Tooltip } from 'antd';
6+
import type { TagProps } from 'antd';
7+
import { BAITag, type SemanticColor } from 'backend.ai-ui';
8+
import React from 'react';
9+
import { useTranslation } from 'react-i18next';
10+
11+
export type ReplicaStatus =
12+
| 'HEALTHY'
13+
| 'UNHEALTHY'
14+
| 'DEGRADED'
15+
| 'NOT_CHECKED'
16+
| 'PROVISIONING'
17+
| 'TERMINATING'
18+
| 'TERMINATED';
19+
20+
export interface ReplicaStatusTagProps extends Omit<TagProps, 'color'> {
21+
/**
22+
* Replica health/lifecycle state.
23+
* Health states: `HEALTHY`, `UNHEALTHY`, `DEGRADED`, `NOT_CHECKED`.
24+
* Lifecycle states: `PROVISIONING`, `TERMINATING`, `TERMINATED`.
25+
*/
26+
status: ReplicaStatus;
27+
/**
28+
* When true, wraps the tag in a tooltip explaining the state.
29+
* @default true
30+
*/
31+
showTooltip?: boolean;
32+
}
33+
34+
const replicaStatusColorMap: Record<ReplicaStatus, SemanticColor> = {
35+
HEALTHY: 'success',
36+
UNHEALTHY: 'error',
37+
DEGRADED: 'warning',
38+
NOT_CHECKED: 'default',
39+
PROVISIONING: 'info',
40+
TERMINATING: 'warning',
41+
TERMINATED: 'default',
42+
};
43+
44+
const replicaStatusI18nKey: Record<ReplicaStatus, string> = {
45+
HEALTHY: 'Healthy',
46+
UNHEALTHY: 'Unhealthy',
47+
DEGRADED: 'Degraded',
48+
NOT_CHECKED: 'NotChecked',
49+
PROVISIONING: 'Provisioning',
50+
TERMINATING: 'Terminating',
51+
TERMINATED: 'Terminated',
52+
};
53+
54+
const ReplicaStatusTag: React.FC<ReplicaStatusTagProps> = ({
55+
status,
56+
showTooltip = true,
57+
...tagProps
58+
}) => {
59+
'use memo';
60+
const { t } = useTranslation();
61+
62+
const color = replicaStatusColorMap[status];
63+
const i18nKey = replicaStatusI18nKey[status];
64+
const label = t(`replicaStatus.${i18nKey}`);
65+
const tooltipTitle = showTooltip
66+
? t(`replicaStatus.tooltip.${i18nKey}`, { defaultValue: '' })
67+
: undefined;
68+
69+
const tag = (
70+
<BAITag {...tagProps} color={color}>
71+
{label}
72+
</BAITag>
73+
);
74+
75+
if (!showTooltip || !tooltipTitle) {
76+
return tag;
77+
}
78+
79+
return (
80+
<Tooltip title={tooltipTitle}>
81+
<span>{tag}</span>
82+
</Tooltip>
83+
);
84+
};
85+
86+
export default ReplicaStatusTag;

0 commit comments

Comments
 (0)