From f04f1f828fbdb3978c39bddbba68b89dad8ffd42 Mon Sep 17 00:00:00 2001 From: jialin Date: Fri, 29 Aug 2025 18:23:14 +0800 Subject: [PATCH] fix: add do cluster --- src/components/label-cell/index.tsx | 32 +++ src/components/seal-form/seal-select.tsx | 1 - src/pages/cluster-management/apis/index.ts | 29 ++- src/pages/cluster-management/clusters.tsx | 70 ++++++- .../components/add-cluster.tsx | 17 +- .../components/add-credential.tsx | 16 -- .../components/add-pool.tsx | 66 +++++- .../components/cloud-provider-form.tsx | 106 +++++++--- .../components/cluster-detail-content.tsx | 47 +---- .../components/worker-pools.tsx | 197 ++++++++++++------ src/pages/cluster-management/config/index.ts | 22 ++ src/pages/cluster-management/config/types.ts | 2 + src/pages/resources/config/index.ts | 12 +- .../resources/hooks/use-worker-columns.tsx | 24 +-- 14 files changed, 419 insertions(+), 222 deletions(-) create mode 100644 src/components/label-cell/index.tsx diff --git a/src/components/label-cell/index.tsx b/src/components/label-cell/index.tsx new file mode 100644 index 00000000..bf0ff7cb --- /dev/null +++ b/src/components/label-cell/index.tsx @@ -0,0 +1,32 @@ +import AutoTooltip from '@/components/auto-tooltip'; +import _ from 'lodash'; +import React from 'react'; +import styled from 'styled-components'; + +const LabelsWrapper = styled.div` + display: flex; + flex-wrap: wrap; + gap: 6px; +`; + +interface LabelCellProps { + labels: Record; +} + +const LabelsCell: React.FC = ({ labels }) => ( + + {_.map(labels, (value: string, key: string) => ( + + {key} + :{value} + + ))} + +); + +export default LabelsCell; diff --git a/src/components/seal-form/seal-select.tsx b/src/components/seal-form/seal-select.tsx index aed1552f..dad5a012 100644 --- a/src/components/seal-form/seal-select.tsx +++ b/src/components/seal-form/seal-select.tsx @@ -23,7 +23,6 @@ const SealSelect: React.FC = (props) => { const intl = useIntl(); const [isFocus, setIsFocus] = useState(false); const inputRef = useRef(null); - const boxRef = useRef(null); let status = ''; // the status can be controlled by Form.Item diff --git a/src/pages/cluster-management/apis/index.ts b/src/pages/cluster-management/apis/index.ts index 70f0a3d3..f2e574c9 100644 --- a/src/pages/cluster-management/apis/index.ts +++ b/src/pages/cluster-management/apis/index.ts @@ -100,11 +100,10 @@ export async function queryClusterToken(params: { id: number }) { // ===================== Worker Pools ===================== export async function queryWorkerPools( - clusterId: number, - params?: Global.SearchParams + params?: Global.SearchParams & { cluster_id: string | number } ) { return request>( - `${CLUSTERS_API}/${clusterId}/${WORKER_POOLS_API}`, + `${WORKER_POOLS_API}?`, { method: 'GET', params @@ -112,28 +111,28 @@ export async function queryWorkerPools( ); } -export async function createWorkerPool( - clusterId: number, - params: { data: NodePoolFormData } -) { - return request(`${CLUSTERS_API}/${clusterId}/${WORKER_POOLS_API}`, { +export async function createWorkerPool(params: { + data: NodePoolFormData; + clusterId: number; +}) { + return request(`${CLUSTERS_API}/${params.clusterId}${WORKER_POOLS_API}`, { method: 'POST', data: params.data }); } -export async function updateWorkerPool( - clusterId: number, - params: { id: number; data: NodePoolFormData } -) { - return request(`/${WORKER_POOLS_API}/${params.id}`, { +export async function updateWorkerPool(params: { + id: number; + data: NodePoolFormData; +}) { + return request(`${WORKER_POOLS_API}/${params.id}`, { method: 'PUT', data: params.data }); } -export async function deleteWorkerPool(clusterId: number, id: number) { - return request(`/${WORKER_POOLS_API}/${id}`, { +export async function deleteWorkerPool(id: number) { + return request(`${WORKER_POOLS_API}/${id}`, { method: 'DELETE' }); } diff --git a/src/pages/cluster-management/clusters.tsx b/src/pages/cluster-management/clusters.tsx index 15e1cb62..b9197574 100644 --- a/src/pages/cluster-management/clusters.tsx +++ b/src/pages/cluster-management/clusters.tsx @@ -8,21 +8,25 @@ import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { Table, message } from 'antd'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { createCluster, + createWorkerPool, deleteCluster, queryClusterList, queryClusterToken, + queryCredentialList, updateCluster } from './apis'; import AddCluster from './components/add-cluster'; +import AddPool from './components/add-pool'; import ClusterDetailModal from './components/cluster-detail-modal'; import RegisterCluster from './components/register-cluster'; import { ProviderLabelMap, ProviderValueMap, addActions } from './config'; import { ClusterFormData as FormData, - ClusterListItem as ListItem + ClusterListItem as ListItem, + NodePoolFormData } from './config/types'; import useClusterColumns from './hooks/use-cluster-columns'; @@ -99,16 +103,22 @@ const Credentials: React.FC = () => { provider: '' }); + const [credentialList, setCredentialList] = useState< + Global.BaseOption[] + >([]); + const [addPoolStatus, setAddPoolStatus] = useState<{ open: boolean; action: PageActionType; title: string; provider: string; + clusterId: number; }>({ open: false, action: PageAction.CREATE, title: '', - provider: ProviderValueMap.DigitalOcean + provider: ProviderValueMap.DigitalOcean, + clusterId: 0 }); const handleAddCluster = (value: string) => { @@ -130,12 +140,13 @@ const Credentials: React.FC = () => { }); }; - const handleAddPool = (value: string) => { + const handleAddPool = (row: ListItem) => { setAddPoolStatus({ open: true, action: PageAction.CREATE, title: `Add Node Pool`, - provider: value + provider: row.provider, + clusterId: row.id }); }; @@ -233,7 +244,7 @@ const Credentials: React.FC = () => { } else if (val === 'add_worker') { handleAddWorker(row); } else if (val === 'addPool') { - handleAddPool(row.provider); + handleAddPool(row); } else if (val === 'details') { setOpenClusterDetail({ open: true, @@ -244,6 +255,34 @@ const Credentials: React.FC = () => { } }); + const handleSubmitWorkerPool = async (formdata: NodePoolFormData) => { + try { + await createWorkerPool({ + data: formdata, + clusterId: addPoolStatus.clusterId + }); + message.success(intl.formatMessage({ id: 'common.message.success' })); + } catch (error) { + // error + } + setAddPoolStatus({ + ...addPoolStatus, + open: false + }); + }; + + useEffect(() => { + const fetchCredentialList = async () => { + const data = await queryCredentialList({ page: 1, perPage: 100 }); + const list = data?.items?.map((item) => ({ + label: item.name, + value: item.id + })); + setCredentialList(list); + }; + fetchCredentialList(); + }, []); + const columns = useClusterColumns(handleSelect); return ( @@ -302,6 +341,7 @@ const Credentials: React.FC = () => { action={openAddModal.action} title={openAddModal.title} currentData={openAddModal.currentData} + credentialList={credentialList} onCancel={handleModalCancel} onOk={handleModalOk} > @@ -323,7 +363,7 @@ const Credentials: React.FC = () => { } > { @@ -338,6 +378,22 @@ const Credentials: React.FC = () => { }); }} > + { + setAddPoolStatus({ + open: false, + action: PageAction.CREATE, + title: '', + provider: ProviderValueMap.DigitalOcean, + clusterId: 0 + }); + }} + onOk={handleSubmitWorkerPool} + > ); diff --git a/src/pages/cluster-management/components/add-cluster.tsx b/src/pages/cluster-management/components/add-cluster.tsx index 76209072..183d0de2 100644 --- a/src/pages/cluster-management/components/add-cluster.tsx +++ b/src/pages/cluster-management/components/add-cluster.tsx @@ -6,7 +6,7 @@ import ContainerInstall from '@/pages/resources/components/container-install'; import { CheckCircleFilled } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { ProviderValueMap } from '../config'; import { ClusterFormData as FormData, @@ -20,7 +20,8 @@ type AddModalProps = { action: PageActionType; open: boolean; currentData?: ListItem; // Used when action is EDIT - provider: string; // 'kubernetes' | 'custom' | 'digitalocean'; + provider: string; + credentialList: Global.BaseOption[]; onOk: (values: FormData) => void; onCancel: () => void; }; @@ -30,6 +31,7 @@ const AddCluster: React.FC = ({ open, provider, currentData, + credentialList, onOk, onCancel }) => { @@ -81,6 +83,12 @@ const AddCluster: React.FC = ({ return title; }, [submissionStatus.success, title]); + useEffect(() => { + if (currentData) { + form.setFieldsValue(currentData); + } + }, [currentData]); + const renderFooter = () => { if (submissionStatus.success) { return ( @@ -141,7 +149,10 @@ const AddCluster: React.FC = ({ > {provider === ProviderValueMap.DigitalOcean && ( - + )} name="description" rules={[{ required: false }]}> = ({ {provider === ProviderValueMap.DigitalOcean && ( <> - {/* - name="key" - rules={[ - { - required: action === PageAction.CREATE, - message: intl.formatMessage({ - id: 'users.form.rule.password' - }) - } - ]} - > - - */} name="secret" rules={[ diff --git a/src/pages/cluster-management/components/add-pool.tsx b/src/pages/cluster-management/components/add-pool.tsx index bab02cc9..5a9dbdb8 100644 --- a/src/pages/cluster-management/components/add-pool.tsx +++ b/src/pages/cluster-management/components/add-pool.tsx @@ -7,7 +7,7 @@ import { PageActionType } from '@/config/types'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; import _ from 'lodash'; -import React from 'react'; +import React, { useEffect } from 'react'; import { NodePoolFormData as FormData, NodePoolListItem as ListItem @@ -18,8 +18,8 @@ type AddModalProps = { action: PageActionType; open: boolean; provider: string; // 'kubernetes' | 'custom' | 'digitalocean'; + currentData?: ListItem | null; onOk: (values: FormData) => void; - data?: ListItem; onCancel: () => void; }; const AddCluster: React.FC = ({ @@ -28,7 +28,7 @@ const AddCluster: React.FC = ({ open, provider, onOk, - data, + currentData, onCancel }) => { const [form] = Form.useForm(); @@ -38,11 +38,37 @@ const AddCluster: React.FC = ({ form.submit(); }; + const handleOnOk = async (data: FormData) => { + const { volumes, ...rest } = data; + + await onOk({ + ...rest, + cloud_options: !_.isEmpty(volumes) + ? { + volumes: [ + { + ...volumes + } + ] + } + : {} + }); + }; + const handleCancel = () => { form.resetFields(); onCancel(); }; + useEffect(() => { + if (currentData) { + form.setFieldsValue({ + ...currentData, + volumes: currentData.cloud_options?.volumes?.[0] || {} + }); + } + }, [currentData]); + return ( = ({ } > -
+ name="instance_type" rules={[ @@ -109,6 +143,22 @@ const AddCluster: React.FC = ({ > + + name="os_image" + rules={[ + { + required: true, + message: intl.formatMessage( + { id: 'common.form.rule.input' }, + { + name: 'OS Image' + } + ) + } + ]} + > + + name="labels" rules={[ @@ -140,7 +190,7 @@ const AddCluster: React.FC = ({ > - name="cloud_options" + name="volumes" rules={[ ({ getFieldValue }) => ({ validator(rule, value) { @@ -152,7 +202,7 @@ const AddCluster: React.FC = ({ id: 'common.validate.value' }, { - name: 'cloud_options' + name: 'Volumes' } ) ); @@ -164,8 +214,8 @@ const AddCluster: React.FC = ({ ]} > diff --git a/src/pages/cluster-management/components/cloud-provider-form.tsx b/src/pages/cluster-management/components/cloud-provider-form.tsx index 5349a056..fa683168 100644 --- a/src/pages/cluster-management/components/cloud-provider-form.tsx +++ b/src/pages/cluster-management/components/cloud-provider-form.tsx @@ -1,15 +1,82 @@ import SealSelect from '@/components/seal-form/seal-select'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; +import _ from 'lodash'; import React from 'react'; +import styled from 'styled-components'; +import { regionList } from '../config'; import { ClusterFormData as FormData } from '../config/types'; +type OptionData = { + label: string; + datacenter: string; + value: string; +}; + +const OptionItem = styled.div` + display: flex; + align-items: center; + .label { + font-weight: 500; + } + .dot { + width: 4px; + height: 4px; + border-radius: 50%; + background: var(--ant-color-text); + margin: 0 8px; + } + .datacenter, + .value { + color: var(--ant-color-text-secondary); + } +`; + interface CloudProviderProps { provider: string; // 'kubernetes' | 'digitalocean'; + credentialList: Global.BaseOption[]; } -const CloudProvider: React.FC = () => { +const optionRender = ( + option: Global.BaseOption< + number, + { + data: OptionData; + } + > +): React.ReactNode => { + const { value } = option; + const data = option.data!; + + return ( + + {data.label} + {data.datacenter}{' '} + {' '} + {_.toUpper(value)} + + ); +}; + +const labelRender = (props: { + label: string; + value: string; +}): React.ReactNode => { + const data = regionList.find((item) => item.value === props.value); + return ( + + {data?.label} + {data?.datacenter}{' '} + + {_.toUpper(data?.value)} + + ); +}; + +const CloudProvider: React.FC = (props) => { + const { credentialList } = props; const intl = useIntl(); + return ( <> @@ -29,12 +96,7 @@ const CloudProvider: React.FC = () => { ({ - label: item, - value: i - }) - )} + options={credentialList} > @@ -54,33 +116,9 @@ const CloudProvider: React.FC = () => { ({ - label: item, - value: item - }))} - > - - - name="zone" - rules={[ - { - required: true, - message: intl.formatMessage( - { id: 'common.form.rule.input' }, - { - name: 'Zone' - } - ) - } - ]} - > - ({ - label: item, - value: item - }))} + options={regionList} + labelRender={labelRender} + optionRender={optionRender} > diff --git a/src/pages/cluster-management/components/cluster-detail-content.tsx b/src/pages/cluster-management/components/cluster-detail-content.tsx index 9801bb64..3c85337a 100644 --- a/src/pages/cluster-management/components/cluster-detail-content.tsx +++ b/src/pages/cluster-management/components/cluster-detail-content.tsx @@ -8,7 +8,6 @@ import styled from 'styled-components'; import { queryClusterDetail } from '../apis'; import { ProviderValueMap } from '../config'; import { ClusterListItem, NodePoolListItem } from '../config/types'; -import AddPool from './add-pool'; import TrendChart from './trend-chart'; import WorkerPools from './worker-pools'; @@ -89,7 +88,9 @@ const ClusterDetail: React.FC = ({ data }) => { open: false, action: PageAction.CREATE, title: '', - provider: ProviderValueMap.DigitalOcean + provider: ProviderValueMap.DigitalOcean, + currentData: null as NodePoolListItem | null, + clusterId: 0 }); const [detailContent, setDetailContent] = useState<{ current: { @@ -109,18 +110,6 @@ const ClusterDetail: React.FC = ({ data }) => { history: {} }); - // pool action handler - const handleOnAction = (action: string, record: NodePoolListItem) => { - if (action === 'edit') { - setAddPoolStatus({ - open: true, - action: PageAction.CREATE, - title: 'Edit Worker Pool', - provider: data!.provider - }); - } - }; - const getClusterDetail = async () => { if (!data) { return; @@ -238,37 +227,9 @@ const ClusterDetail: React.FC = ({ data }) => { {data?.provider === ProviderValueMap.DigitalOcean && ( <> - Worker Pools - + )} - { - setAddPoolStatus({ - open: false, - action: PageAction.CREATE, - title: '', - provider: ProviderValueMap.DigitalOcean - }); - }} - onOk={() => { - setAddPoolStatus({ - open: false, - action: addPoolStatus.action, - title: '', - provider: ProviderValueMap.DigitalOcean - }); - }} - > ); }; diff --git a/src/pages/cluster-management/components/worker-pools.tsx b/src/pages/cluster-management/components/worker-pools.tsx index 18e00c9b..0f4bbcb7 100644 --- a/src/pages/cluster-management/components/worker-pools.tsx +++ b/src/pages/cluster-management/components/worker-pools.tsx @@ -1,11 +1,35 @@ import DeleteModal from '@/components/delete-modal'; import DropdownButtons from '@/components/drop-down-buttons'; +import LabelsCell from '@/components/label-cell'; +import { PageAction } from '@/config'; import useTableFetch from '@/hooks/use-table-fetch'; import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; -import { Table } from 'antd'; -import { useMemo } from 'react'; -import { WORKER_POOLS_API, deleteWorkerPool, queryWorkerPools } from '../apis'; -import { NodePoolListItem as ListItem } from '../config/types'; +import { useIntl } from '@umijs/max'; +import { message, Table } from 'antd'; +import dayjs from 'dayjs'; +import { useState } from 'react'; +import styled from 'styled-components'; +import { + createWorkerPool, + deleteWorkerPool, + queryWorkerPools, + updateWorkerPool, + WORKER_POOLS_API +} from '../apis'; +import { ProviderValueMap } from '../config'; +import { + ClusterListItem, + NodePoolListItem as ListItem, + NodePoolFormData +} from '../config/types'; +import AddPool from './add-pool'; + +const SubTitle = styled.div` + font-size: var(--font-size-middle); + font-weight: 700; + color: var(--ant-color-text); + margin-block: 24px 16px; +`; const actionItems = [ { @@ -25,19 +49,15 @@ const actionItems = [ ]; interface WorkerPoolsProps { - workerPools: ListItem[]; loading?: boolean; - provider: string; height?: string | number; - onAction?: (action: string, record: ListItem) => void; + clusterData: ClusterListItem | null; } const WorkerPools: React.FC = ({ - workerPools, loading = false, - provider, - height = 'auto', - onAction + clusterData, + height = 'auto' }) => { const { dataSource, @@ -57,18 +77,69 @@ const WorkerPools: React.FC = ({ deleteAPI: deleteWorkerPool, API: WORKER_POOLS_API, watch: false, - contentForDelete: 'resources.modelfiles.modelfile' + contentForDelete: 'resources.modelfiles.modelfile', + defaultQueryParams: { + cluster_id: clusterData!.id + } }); + const intl = useIntl(); + const [addPoolStatus, setAddPoolStatus] = useState({ + open: false, + action: PageAction.CREATE, + title: '', + provider: ProviderValueMap.DigitalOcean, + currentData: null as ListItem | null, + clusterId: 0 + }); + + // pool action handler + const handleEdit = (action: string, record: ListItem) => { + if (action === 'edit') { + setAddPoolStatus({ + open: true, + action: PageAction.EDIT, + title: 'Edit Worker Pool', + provider: clusterData!.provider, + currentData: record, + clusterId: clusterData!.id + }); + } + }; const onSelect = (key: string, record: ListItem) => { if (key === 'delete') { handleDelete({ ...record, name: record.instance_type }); } if (key === 'edit') { - onAction?.(key, record); + handleEdit(key, record); } }; + const handleSubmitWorkerPool = async (formdata: NodePoolFormData) => { + try { + if (addPoolStatus.action === PageAction.CREATE) { + await createWorkerPool({ + data: formdata, + clusterId: clusterData!.id + }); + } + if (addPoolStatus.action === PageAction.EDIT) { + await updateWorkerPool({ + data: formdata, + id: addPoolStatus.currentData!.id + }); + } + message.success(intl.formatMessage({ id: 'common.message.success' })); + handleSearch(); + } catch (error) { + // error + } + setAddPoolStatus({ + ...addPoolStatus, + open: false + }); + }; + const columns = [ { title: 'Instance Type', @@ -82,33 +153,22 @@ const WorkerPools: React.FC = ({ }, { title: 'Batch Size', - dataIndex: 'batchSize', - key: 'batchSize' + dataIndex: 'batch_size', + key: 'batch_size' }, { - title: 'GPU', - dataIndex: 'gpu', - key: 'gpu' - }, - { - title: 'Memory', - dataIndex: 'memory', - key: 'memory' - }, - { - title: 'CPU', - dataIndex: 'cpu', - key: 'cpu' - }, - { - title: 'Storage', - dataIndex: 'storage', - key: 'storage' + title: 'Labels', + dataIndex: 'labels', + key: 'labels', + render: (text: string, record: ListItem) => ( + + ) }, { title: 'Create Time', - dataIndex: 'createTime', - key: 'createTime' + dataIndex: 'create_at', + key: 'created_at', + render: (text: string) => dayjs(text).format('YYYY-MM-DD HH:mm:ss') }, { title: 'Operations', @@ -122,41 +182,40 @@ const WorkerPools: React.FC = ({ } ]; - // mock dataSource - const mockData = Array.from({ length: 3 }, (_, index) => ({ - id: index + 1, - key: index, - instance_type: `Type ${index + 1}`, - replicas: Math.floor(Math.random() * 10) + 1, - batchSize: Math.floor(Math.random() * 100) + 1, - gpu: `NVIDIA 4090`, - memory: `${Math.floor(Math.random() * 16) + 1} GB`, - cpu: `${Math.floor(Math.random() * 8) + 1} Cores`, - storage: `${Math.floor(Math.random() * 500) + 50} GB`, - createTime: new Date().toLocaleDateString() - })); - - const dataList = useMemo(() => { - if (workerPools && workerPools.length > 0) { - return workerPools; - } - if (dataSource.dataList && dataSource.dataList.length > 0) { - return dataSource.dataList; - } - return mockData; - }, [workerPools, dataSource.dataList]); - return ( -
- - - + <> + + Worker Pools + +
+
+ { + setAddPoolStatus({ + open: false, + action: PageAction.CREATE, + title: '', + provider: ProviderValueMap.DigitalOcean, + currentData: null, + clusterId: 0 + }); + }} + onOk={handleSubmitWorkerPool} + > + + + ); }; diff --git a/src/pages/cluster-management/config/index.ts b/src/pages/cluster-management/config/index.ts index be4d2b92..c4384809 100644 --- a/src/pages/cluster-management/config/index.ts +++ b/src/pages/cluster-management/config/index.ts @@ -122,3 +122,25 @@ export const clusterActionList = [ } } ]; + +export const regionList: { + label: string; + datacenter: string; + value: string; +}[] = [ + { label: 'New York', datacenter: 'Datacenter 1', value: 'nyc1' }, + { label: 'New York', datacenter: 'Datacenter 2', value: 'nyc2' }, + { label: 'New York', datacenter: 'Datacenter 3', value: 'nyc3' }, + { label: 'Toronto', datacenter: 'Datacenter 1', value: 'tor1' }, + { label: 'San Francisco', datacenter: 'Datacenter 1', value: 'sfo1' }, + { label: 'San Francisco', datacenter: 'Datacenter 2', value: 'sfo2' }, + { label: 'San Francisco', datacenter: 'Datacenter 3', value: 'sfo3' }, + { label: 'Atlanta', datacenter: 'Datacenter 1', value: 'atl1' }, + { label: 'Singapore', datacenter: 'Datacenter 1', value: 'sgp1' }, + { label: 'Bangalore', datacenter: 'Datacenter 1', value: 'blr1' }, + { label: 'London', datacenter: 'Datacenter 1', value: 'lon1' }, + { label: 'Amsterdam', datacenter: 'Datacenter 2', value: 'ams2' }, + { label: 'Amsterdam', datacenter: 'Datacenter 3', value: 'ams3' }, + { label: 'Frankfurt', datacenter: 'Datacenter 1', value: 'fra1' }, + { label: 'Sydney', datacenter: 'Datacenter 1', value: 'syd1' } +]; diff --git a/src/pages/cluster-management/config/types.ts b/src/pages/cluster-management/config/types.ts index 6e258b13..0b5c7c0e 100644 --- a/src/pages/cluster-management/config/types.ts +++ b/src/pages/cluster-management/config/types.ts @@ -42,10 +42,12 @@ export interface NodePoolListItem { export interface NodePoolFormData { instance_type: string; + os_image: string; replicas: number; batch_size: number; labels: Record; cloud_options: Record; + volumes?: Record; } export interface ClusterListItem { diff --git a/src/pages/resources/config/index.ts b/src/pages/resources/config/index.ts index bcb96d0b..11b24bb7 100644 --- a/src/pages/resources/config/index.ts +++ b/src/pages/resources/config/index.ts @@ -3,19 +3,25 @@ import { StatusMaps } from '@/config'; export const WorkerStatusMap = { ready: 'ready', not_ready: 'not_ready', - unreachable: 'unreachable' + unreachable: 'unreachable', + provisioning: 'provisioning', + deleting: 'deleting' }; export const WorkerStatusMapValue = { [WorkerStatusMap.ready]: 'Ready', [WorkerStatusMap.not_ready]: 'Not Ready', - [WorkerStatusMap.unreachable]: 'Unreachable' + [WorkerStatusMap.unreachable]: 'Unreachable', + [WorkerStatusMap.provisioning]: 'Provisioning', + [WorkerStatusMap.deleting]: 'Deleting' }; export const status: any = { [WorkerStatusMap.ready]: StatusMaps.success, [WorkerStatusMap.not_ready]: StatusMaps.error, - [WorkerStatusMap.unreachable]: StatusMaps.error + [WorkerStatusMap.unreachable]: StatusMaps.error, + [WorkerStatusMap.provisioning]: StatusMaps.transitioning, + [WorkerStatusMap.deleting]: StatusMaps.warning }; export const addWorkerGuide: Record = { diff --git a/src/pages/resources/hooks/use-worker-columns.tsx b/src/pages/resources/hooks/use-worker-columns.tsx index 5fadb5b5..6aa98d71 100644 --- a/src/pages/resources/hooks/use-worker-columns.tsx +++ b/src/pages/resources/hooks/use-worker-columns.tsx @@ -1,6 +1,7 @@ import AutoTooltip from '@/components/auto-tooltip'; import DropdownButtons from '@/components/drop-down-buttons'; import IconFont from '@/components/icon-font'; +import LabelsCell from '@/components/label-cell'; import ProgressBar from '@/components/progress-bar'; import InfoColumn from '@/components/simple-table/info-column'; import StatusTag from '@/components/status-tag'; @@ -17,16 +18,9 @@ import { Tooltip } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import _ from 'lodash'; import { useMemo } from 'react'; -import styled from 'styled-components'; import { WorkerStatusMap, WorkerStatusMapValue, status } from '../config'; import { Filesystem, GPUDeviceItem, ListItem } from '../config/types'; -const LabelsWrapper = styled.div` - display: flex; - flex-wrap: wrap; - gap: 6px; -`; - const ActionList = [ { label: 'common.button.edit', key: 'edit', icon: }, { @@ -90,22 +84,6 @@ const calcStorage = (files: Filesystem[]) => { return mountRoot ? formateUtilization(mountRoot.used, mountRoot.total) : 0; }; -const LabelsCell = ({ labels }: { labels: Record }) => ( - - {_.map(labels, (value: string, key: string) => ( - - {key} - :{value} - - ))} - -); - const GPUCell = ({ devices }: { devices: GPUDeviceItem[] }) => ( {_.map(