Skip to content

Commit 52128b3

Browse files
committed
feat(FR-1371): Implement model deployment UI components
1 parent fb07eb9 commit 52128b3

File tree

9 files changed

+990
-4
lines changed

9 files changed

+990
-4
lines changed

packages/backend.ai-ui/src/components/Table/BAITable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { Resizable, ResizeCallbackData } from 'react-resizable';
2424
* Configuration interface for BAITable pagination
2525
* Extends Ant Design's TablePaginationConfig but omits 'position' property
2626
*/
27-
interface BAITablePaginationConfig
27+
export interface BAITablePaginationConfig
2828
extends Omit<TablePaginationConfig, 'position'> {
2929
/** Additional content to display in the pagination area */
3030
extraContent?: ReactNode;

packages/backend.ai-ui/src/components/Table/BAITableSettingModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ const BAITableSettingModal: React.FC<TableSettingProps> = ({
442442
style={{
443443
height: 330,
444444
}}
445-
scroll={{ x: 'max-content' }}
445+
scroll={{ x: 'max-content', y: 330 }}
446446
/>
447447
</SortableContext>
448448
</DndContext>

packages/backend.ai-ui/src/components/Table/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export type {
44
BAIColumnType,
55
BAIColumnsType,
66
BAITableSettings,
7+
BAITablePaginationConfig,
78
BAITableColumnOverrideItem,
89
} from './BAITable';
910
export {

react/src/App.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@ const ChatPage = React.lazy(() => import('./pages/ChatPage'));
8383
const AIAgentPage = React.lazy(() => import('./pages/AIAgentPage'));
8484

8585
// Deployment pages
86+
// const DeploymentListPage = React.lazy(
87+
// () => import('./pages/Deployments/DeploymentListPage'),
88+
// );
8689
const DeploymentListPage = React.lazy(
87-
() => import('./pages/Deployments/DeploymentListPage'),
90+
() => import('./pages/DeploymentListPage'),
8891
);
8992
const DeploymentDetailPage = React.lazy(
9093
() => import('./pages/Deployments/DeploymentDetailPage'),
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { ModelDeployment } from './TempDeploymentTypes';
2+
import WebUILink from './WebUILink';
3+
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
4+
import { Tag, theme, Tooltip, Typography } from 'antd';
5+
import {
6+
BAIColumnType,
7+
BAIFlex,
8+
BAITable,
9+
BAITablePaginationConfig,
10+
BAITableProps,
11+
} from 'backend.ai-ui';
12+
import dayjs from 'dayjs';
13+
import _ from 'lodash';
14+
import { ExternalLinkIcon } from 'lucide-react';
15+
import { useTranslation } from 'react-i18next';
16+
import { filterOutEmpty, filterOutNullAndUndefined } from 'src/helper';
17+
import { useSuspendedBackendaiClient } from 'src/hooks';
18+
19+
interface DeploymentListProps
20+
extends Omit<BAITableProps<ModelDeployment>, 'dataSource' | 'columns'> {
21+
deployments: ModelDeployment[];
22+
pagination: BAITablePaginationConfig;
23+
}
24+
25+
const DeploymentList: React.FC<DeploymentListProps> = ({
26+
deployments,
27+
pagination,
28+
...tableProps
29+
}) => {
30+
const { t } = useTranslation();
31+
const { token } = theme.useToken();
32+
const baiClient = useSuspendedBackendaiClient();
33+
34+
const filteredDeployments = filterOutNullAndUndefined(deployments);
35+
const columns = _.map(
36+
filterOutEmpty<BAIColumnType<ModelDeployment>>([
37+
{
38+
title: t('deployment.DeploymentName'),
39+
key: 'name',
40+
dataIndex: ['metadata', 'name'],
41+
fixed: 'left',
42+
render: (name, row) => (
43+
<WebUILink to={`/deployment/${row.id}`}>{name}</WebUILink>
44+
),
45+
sorter: true,
46+
},
47+
{
48+
title: t('deployment.EndpointURL'),
49+
key: 'endpointUrl',
50+
dataIndex: ['networkAccess', 'endpointUrl'],
51+
render: (url) => (
52+
<BAIFlex gap={'xxs'}>
53+
<Typography.Text copyable>{url}</Typography.Text>
54+
<a href={url} title="" target="_blank" rel="noopener noreferrer">
55+
<Tooltip title={t('common.OpenInNewTab')}>
56+
<ExternalLinkIcon />
57+
</Tooltip>
58+
</a>
59+
</BAIFlex>
60+
),
61+
},
62+
// {
63+
// title: t('deployment.Status'),
64+
// key: 'status',
65+
// dataIndex: ['status'],
66+
// },
67+
{
68+
title: t('deployment.NumberOfReplicas'),
69+
key: 'desiredReplicaCount',
70+
dataIndex: ['replicaState', 'desiredReplicaCount'],
71+
render: (count) => count,
72+
sorter: true,
73+
},
74+
{
75+
title: t('deployment.Public'),
76+
key: 'openToPublic',
77+
dataIndex: ['networkAccess', 'openToPublic'],
78+
render: (openToPublic) =>
79+
openToPublic ? (
80+
<CheckOutlined style={{ color: token.colorSuccess }} />
81+
) : (
82+
<CloseOutlined style={{ color: token.colorTextSecondary }} />
83+
),
84+
sorter: true,
85+
},
86+
// {
87+
// title: t('deployment.Resources'),
88+
// dataIndex: ['resourceConfig', 'resourceSlots'],
89+
// key: 'resourceSlots',
90+
// render: (resourceSlots) => (
91+
// <BAIFlex direction="row" gap="xs">
92+
// {_.map(resourceSlots, (value, key) => (
93+
// <ResourceNumber key={key} type={key} value={value.toString()} />
94+
// ))}
95+
// </BAIFlex>
96+
// ),
97+
// defaultHidden: true,
98+
// },
99+
{
100+
title: t('deployment.CreatedAt'),
101+
dataIndex: ['metadata', 'createdAt'],
102+
key: 'createdAt',
103+
render: (createdAt) => {
104+
return dayjs(createdAt).format('ll LT');
105+
},
106+
sorter: true,
107+
},
108+
{
109+
title: t('deployment.UpdatedAt'),
110+
dataIndex: ['metadata', 'updatedAt'],
111+
key: 'updatedAt',
112+
render: (updatedAt) => {
113+
return dayjs(updatedAt).format('ll LT');
114+
},
115+
sorter: true,
116+
defaultHidden: true,
117+
},
118+
baiClient.is_admin && {
119+
title: t('deployment.Owner'),
120+
dataIndex: ['createdUser', 'email'],
121+
key: 'owner',
122+
render: (email) => <Typography.Text>{email}</Typography.Text>,
123+
},
124+
{
125+
title: t('deployment.Tags'),
126+
dataIndex: ['metadata', 'tags'],
127+
key: 'tags',
128+
render: (tags) => _.map(tags, (tag) => <Tag key={tag}>{tag}</Tag>),
129+
defaultHidden: true,
130+
},
131+
{
132+
title: 'ID',
133+
dataIndex: 'id',
134+
key: 'id',
135+
render: (id) => <Typography.Text>{id}</Typography.Text>,
136+
defaultHidden: true,
137+
},
138+
{
139+
title: t('deployment.PreferredDomainName'),
140+
dataIndex: ['networkAccess', 'preferredDomainName'],
141+
key: 'preferredDomainName',
142+
render: (name) => <Typography.Text>{name}</Typography.Text>,
143+
defaultHidden: true,
144+
},
145+
{
146+
title: t('deployment.DeploymentStrategy'),
147+
dataIndex: ['deploymentStrategy', 'type'],
148+
key: 'type',
149+
render: (type) => (
150+
<Tag
151+
color={
152+
type === 'ROLLING'
153+
? 'default'
154+
: type === 'BLUE_GREEN'
155+
? 'blue'
156+
: 'yellow'
157+
}
158+
>
159+
{type}
160+
</Tag>
161+
),
162+
defaultHidden: true,
163+
},
164+
{
165+
title: t('deployment.ClusterSize'),
166+
dataIndex: ['clusterConfig', 'size'],
167+
key: 'clusterSize',
168+
render: (size) => <Typography.Text>{size}</Typography.Text>,
169+
sorter: true,
170+
defaultHidden: true,
171+
},
172+
]),
173+
);
174+
175+
return (
176+
<BAITable
177+
resizable
178+
rowKey={'id'}
179+
size="small"
180+
dataSource={filteredDeployments}
181+
columns={columns}
182+
pagination={pagination}
183+
scroll={{ x: 'max-content' }}
184+
{...tableProps}
185+
/>
186+
);
187+
};
188+
189+
export default DeploymentList;

0 commit comments

Comments
 (0)