diff --git a/src/assets/images/gpustack-logo-2.png b/src/assets/images/gpustack-logo-2.png new file mode 100644 index 00000000..df995fc9 Binary files /dev/null and b/src/assets/images/gpustack-logo-2.png differ diff --git a/src/assets/images/gpustack-logo.png b/src/assets/images/gpustack-logo.png index df995fc9..3783e68d 100644 Binary files a/src/assets/images/gpustack-logo.png and b/src/assets/images/gpustack-logo.png differ diff --git a/src/components/scroller-modal/gs-drawer.tsx b/src/components/scroller-modal/gs-drawer.tsx index 2d3c55e8..001f9074 100644 --- a/src/components/scroller-modal/gs-drawer.tsx +++ b/src/components/scroller-modal/gs-drawer.tsx @@ -3,7 +3,7 @@ import { CloseOutlined } from '@ant-design/icons'; import { Button, Drawer, type DrawerProps } from 'antd'; const ScrollerModal = (props: DrawerProps) => { - const { title, closable = true, ...restProps } = props; + const { title, closable = true, maskClosable = false, ...restProps } = props; const { EscHint } = useEscHint({ enabled: !props.keyboard && props.open }); @@ -15,6 +15,7 @@ const ScrollerModal = (props: DrawerProps) => { >; } + interface InitialStateType { + fetchUserInfo: () => Promise; + currentUser?: UserInfo; + } + type SearchParams = Pagination & { search?: string }; type MessageType = 'transition' | 'warning' | 'danger' | 'success' | 'info'; diff --git a/src/hooks/use-table-fetch.ts b/src/hooks/use-table-fetch.ts index 7cbf2f83..7bfda6e3 100644 --- a/src/hooks/use-table-fetch.ts +++ b/src/hooks/use-table-fetch.ts @@ -21,6 +21,7 @@ export default function useTableFetch( contentForDelete?: string; defaultData?: any[]; events?: EventsType[]; + defaultQueryParams?: Record; } & WatchConfig ) { const { @@ -31,7 +32,8 @@ export default function useTableFetch( polling = false, watch, defaultData = [], - events = ['UPDATE', 'DELETE'] + events = ['UPDATE', 'DELETE'], + defaultQueryParams = {} } = options; const pollingRef = useRef(null); const chunkRequedtRef = useRef(null); @@ -60,7 +62,8 @@ export default function useTableFetch( const [queryParams, setQueryParams] = useState({ page: 1, perPage: 10, - search: '' + search: '', + ...defaultQueryParams }); const { setChunkRequest } = useSetChunkRequest(); diff --git a/src/layouts/Logo.tsx b/src/layouts/Logo.tsx index a436dd84..b7f50223 100644 --- a/src/layouts/Logo.tsx +++ b/src/layouts/Logo.tsx @@ -4,7 +4,7 @@ import SmallLogo from '@/assets/images/small-logo-200x200.png'; import React from 'react'; const LogoIcon: React.FC = () => { - return logo; + return logo; }; const SLogoIcon: React.FC = () => { return logo; diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 0c3ef090..3292fed8 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -180,11 +180,6 @@ export default (props: any) => { showShortcuts: () => { return showShortcuts(); }, - setTheme: setTheme, - toggleTheme: () => { - const newTheme = userSettings.theme === 'realDark' ? 'light' : 'realDark'; - setTheme(newTheme); - }, notFound: 404 not found }; diff --git a/src/layouts/rightRender.tsx b/src/layouts/rightRender.tsx index 7271ebdc..ac011a9c 100644 --- a/src/layouts/rightRender.tsx +++ b/src/layouts/rightRender.tsx @@ -2,20 +2,17 @@ import avatarImg from '@/assets/images/avatar.png'; import IconFont from '@/components/icon-font'; import externalLinks from '@/constants/external-links'; -import langConfigMap from '@/locales/lang-config-map'; import { DiscordOutlined, GithubOutlined, HomeOutlined, InfoCircleOutlined, LogoutOutlined, - MoonOutlined, MoreOutlined, ReadOutlined, - SettingOutlined, - SunOutlined + SettingOutlined } from '@ant-design/icons'; -import { getAllLocales, history, setLocale } from '@umijs/max'; +import { getAllLocales, history } from '@umijs/max'; import { Avatar, Menu, Spin } from 'antd'; import _ from 'lodash'; import React from 'react'; @@ -191,64 +188,6 @@ export const getRightRenderContent = (opts: { ] }; - const langMenu = { - selectedKeys: [], - className: collapsed - ? 'user-menu-container user-menu-collapsed' - : 'user-menu-container', - mode: 'vertical', - expandIcon: false, - // inlineCollapsed: collapsed, - triggerSubMenuAction: 'hover', - items: [ - { - key: 'lang', - icon: , - label: ( - - {intl?.formatMessage?.({ id: 'common.settings.language' })} - - ), - children: allLocals.map((key) => ({ - key, - label: ( - - {_.get(langConfigMap, [key, 'label'])} - - ), - onClick: () => { - setLocale(key, false); - } - })) - } - ] - }; - - const themeMenu = { - selectedKeys: [], - className: collapsed - ? 'user-menu-container user-menu-collapsed' - : 'user-menu-container', - mode: 'vertical', - expandIcon: false, - // inlineCollapsed: collapsed, - triggerSubMenuAction: 'click', - items: [ - { - key: 'theme', - icon: isDarkTheme ? : , - label: ( - - {intl?.formatMessage?.({ id: 'common.appearance' })} - - ), - onClick: () => { - opts.runtimeConfig.toggleTheme(); - } - } - ] - }; - const userMenu = { selectedKeys: [], className: collapsed diff --git a/src/layouts/sider-menu.tsx b/src/layouts/sider-menu.tsx index a28f9f59..9439595f 100644 --- a/src/layouts/sider-menu.tsx +++ b/src/layouts/sider-menu.tsx @@ -17,7 +17,7 @@ interface MenuItem { interface SiderMenuProps { menuData: MenuItem[]; collapsed?: boolean; - initialState: Global.InitialState; + initialState: Global.InitialStateType; } const useStyles = createStyles(({ css, token }) => { diff --git a/src/locales/en-US/resources.ts b/src/locales/en-US/resources.ts index 0fb9918c..e238edff 100644 --- a/src/locales/en-US/resources.ts +++ b/src/locales/en-US/resources.ts @@ -69,6 +69,7 @@ export default { 'Waiting for the download to complete...', 'resources.filter.worker': 'Filter by worker', 'resources.filter.source': 'Filter by Source', + 'resources.filter.status': 'Filter by Status', 'resources.modelfiles.delete.tips': 'Also delete the file from disk', 'resources.modelfiles.copy.tips': 'Copy Full Path', 'resources.filter.path': 'Filter by path', diff --git a/src/locales/ja-JP/resources.ts b/src/locales/ja-JP/resources.ts index 276cc085..a587f0d2 100644 --- a/src/locales/ja-JP/resources.ts +++ b/src/locales/ja-JP/resources.ts @@ -70,6 +70,7 @@ export default { 'ダウンロード完了を待っています...', 'resources.filter.worker': 'ワーカーでフィルタ', 'resources.filter.source': 'ソースでフィルタ', + 'resources.filter.status': 'Filter by Status', 'resources.modelfiles.delete.tips': 'ディスクからファイルも削除します', 'resources.modelfiles.copy.tips': 'フルパスをコピー', 'resources.filter.path': 'パスでフィルタ', @@ -105,5 +106,6 @@ export default { // 8. 'resources.register.install.title': 'Install GPUStack on {os}', // 9. 'resources.register.download':'Download and install the installer. Only supported: {versions}.', // 10. 'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+', -// 11. 'resource.register.windows.support': 'win 10, win 11' +// 11. 'resource.register.windows.support': 'win 10, win 11', +// 12. 'resources.filter.status': 'Filter by Status', // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/resources.ts b/src/locales/ru-RU/resources.ts index 17495a84..1e1a8109 100644 --- a/src/locales/ru-RU/resources.ts +++ b/src/locales/ru-RU/resources.ts @@ -68,6 +68,7 @@ export default { 'resources.modelfiles.storagePath.holder': 'Ожидание завершения загрузки...', 'resources.filter.worker': 'Фильтровать по узлу', 'resources.filter.source': 'Фильтровать по источнику', + 'resources.filter.status': 'Фильтровать по статусу', 'resources.modelfiles.delete.tips': 'Также удалить файл с диска', 'resources.modelfiles.copy.tips': 'Скопировать полный путь', 'resources.filter.path': 'Фильтрация по пути', diff --git a/src/locales/zh-CN/resources.ts b/src/locales/zh-CN/resources.ts index 6e27cb2d..543f71e0 100644 --- a/src/locales/zh-CN/resources.ts +++ b/src/locales/zh-CN/resources.ts @@ -67,6 +67,7 @@ export default { 'resources.modelfiles.storagePath.holder': '等待下载完成...', 'resources.filter.worker': '按 Worker 筛选', 'resources.filter.source': '按来源筛选', + 'resources.filter.status': '按状态筛选', 'resources.modelfiles.delete.tips': '同时从磁盘删除文件', 'resources.modelfiles.copy.tips': '复制完整路径', 'resources.filter.path': '路径查询', diff --git a/src/pages/cluster-management/config/index.ts b/src/pages/cluster-management/config/index.ts index 4beec8e6..0de8547a 100644 --- a/src/pages/cluster-management/config/index.ts +++ b/src/pages/cluster-management/config/index.ts @@ -73,7 +73,7 @@ export const clusterActionList = [ }, { key: 'details', - label: 'common.button.view', + label: 'common.button.detail', icon: icons.DetailInfo }, { diff --git a/src/pages/llmodels/components/instance-item.tsx b/src/pages/llmodels/components/instance-item.tsx index 08acea5f..8719c1ad 100644 --- a/src/pages/llmodels/components/instance-item.tsx +++ b/src/pages/llmodels/components/instance-item.tsx @@ -39,6 +39,14 @@ import { } from '../config/types'; import '../style/instance-item.less'; +interface InstanceItemProps { + instanceData: ModelInstanceListItem; + workerList: WorkerListItem[]; + modelData?: any; + defaultOpenId: string; + handleChildSelect: (val: string, item: ModelInstanceListItem) => void; +} + const fieldList = [ { label: 'CPU', @@ -250,14 +258,6 @@ const InstanceStatusTag = ( ); }; -interface InstanceItemProps { - instanceData: ModelInstanceListItem; - workerList: WorkerListItem[]; - modelData?: any; - defaultOpenId: string; - handleChildSelect: (val: string, item: ModelInstanceListItem) => void; -} - const childActionList = [ { label: 'common.button.viewlog', diff --git a/src/pages/llmodels/config/index.ts b/src/pages/llmodels/config/index.ts index 4f3fc139..90266994 100644 --- a/src/pages/llmodels/config/index.ts +++ b/src/pages/llmodels/config/index.ts @@ -2,86 +2,6 @@ import { StatusMaps } from '@/config'; import { EditOutlined } from '@ant-design/icons'; import _ from 'lodash'; -export const ollamaModelOptions = [ - { - label: 'qwen2.5-coder', - value: 'qwen2.5-coder', - name: 'qwen2.5-coder', - id: 'qwen2.5-coder', - tags: [] - }, - { - label: 'llama3.2', - value: 'llama3.2', - name: 'llama3.2', - id: 'llama3.2', - tags: ['Tools', '1B', '3B'] - }, - { - label: 'nomic-embed-text', - value: 'nomic-embed-text', - name: 'nomic-embed-text', - id: 'nomic-embed-text', - tags: [] - }, - { - label: 'qwen2.5', - value: 'qwen2.5', - name: 'qwen2.5', - tags: ['Tools', '0.5B', '1.5B', '3B', '7B', '14B', '32B', '72B'], - id: 'gemma2' - }, - { - label: 'gemma2', - value: 'gemma2', - name: 'gemma2', - tags: ['2B', '9B', '27B'], - id: 'gemma2' - }, - { - label: 'mistral-nemo', - value: 'mistral-nemo', - name: 'mistral-nemo', - tags: ['12B'], - id: 'mistral-nemo' - }, - { - label: 'mistral-large', - value: 'mistral-large', - name: 'mistral-large', - tags: ['123B'], - id: 'mistral-large' - }, - { - label: 'mistral', - value: 'mistral', - name: 'mistral', - tags: ['7B'], - id: 'mistral' - }, - { - label: 'phi3.5', - value: 'phi3.5', - name: 'phi3.5', - tags: ['3B'], - id: 'phi3.5' - }, - { - label: 'codellama', - value: 'codellama', - name: 'codellama', - tags: ['7B', '13B', '34B', '70B'], - id: 'codellama' - }, - { - label: 'deepseek-coder-v2', - value: 'deepseek-coder-v2', - name: 'deepseek-coder-v2', - tags: ['16B', '236B'], - id: 'deepseek-coder-v2' - } -]; - export const backendTipsList = [ { title: 'vLLM', diff --git a/src/pages/resources/components/worker-detail-content.tsx b/src/pages/resources/components/worker-detail-content.tsx new file mode 100644 index 00000000..4df7d18f --- /dev/null +++ b/src/pages/resources/components/worker-detail-content.tsx @@ -0,0 +1,125 @@ +import { FilterBar } from '@/components/page-tools'; +import useTableFetch from '@/hooks/use-table-fetch'; +import { queryClusterList } from '@/pages/cluster-management/apis'; +import { + MODEL_INSTANCE_API, + queryModelsInstances +} from '@/pages/llmodels/apis'; +import { InstanceStatusMap } from '@/pages/llmodels/config'; +import { ModelInstanceListItem } from '@/pages/llmodels/config/types'; +import { useIntl } from '@umijs/max'; +import { ConfigProvider, Empty, Table } from 'antd'; +import React, { useEffect, useState } from 'react'; +import useInstanceColumns from '../hooks/use-instances-columns'; + +const statusList = Object.entries(InstanceStatusMap).map(([key, value]) => ({ + label: key, + value: value +})); + +const WorkerDetailContent: React.FC<{ worker_id: number | undefined }> = ({ + worker_id +}) => { + const { + dataSource, + queryParams, + handlePageChange, + handleTableChange, + handleSearch, + handleQueryChange, + handleNameChange + } = useTableFetch({ + fetchAPI: queryModelsInstances, + polling: false, + API: MODEL_INSTANCE_API, + defaultQueryParams: { + worker_id + } + }); + + const intl = useIntl(); + const [clusterList, setClusterList] = useState[]>( + [] + ); + + const getClusterList = async () => { + try { + const res = await queryClusterList({ page: 1, perPage: 100 }); + const list = res.items?.map((item) => ({ + label: item.name, + value: item.id + })); + setClusterList(list || []); + } catch (error) { + setClusterList([]); + } + }; + + const renderEmpty = (type?: string) => { + if (type !== 'Table') return; + if ( + !dataSource.loading && + dataSource.loadend && + !dataSource.dataList.length + ) { + return ; + } + return
; + }; + + const handleSelectChange = (value: string) => { + handleQueryChange({ state: value }); + }; + + const columns = useInstanceColumns({ + clusterList + }); + + useEffect(() => { + console.log('columns changed!'); + }, [columns]); + + useEffect(() => { + getClusterList(); + }, []); + + return ( + <> + + +
+
+ + ); +}; + +export default WorkerDetailContent; diff --git a/src/pages/resources/components/worker-detail-modal.tsx b/src/pages/resources/components/worker-detail-modal.tsx new file mode 100644 index 00000000..48818a45 --- /dev/null +++ b/src/pages/resources/components/worker-detail-modal.tsx @@ -0,0 +1,34 @@ +import GSDrawer from '@/components/scroller-modal/gs-drawer'; +import React from 'react'; +import { ListItem } from '../config/types'; +import WorkerDetailContent from './worker-detail-content'; + +interface WorkerDetailModalProps { + open: boolean; + onClose?: () => void; + currentData: ListItem | null; +} + +const WorkerDetailModal: React.FC = ({ + open, + onClose, + currentData +}) => { + const handleOnClose = () => { + onClose?.(); + }; + + return ( + + + + ); +}; + +export default WorkerDetailModal; diff --git a/src/pages/resources/components/workers.tsx b/src/pages/resources/components/workers.tsx index aeea95a3..0f92fa2d 100644 --- a/src/pages/resources/components/workers.tsx +++ b/src/pages/resources/components/workers.tsx @@ -16,6 +16,7 @@ import { import { ListItem } from '../config/types'; import useWorkerColumns from '../hooks/use-worker-columns'; import UpdateLabels from './update-labels'; +import WorkerDetailModal from './worker-detail-modal'; const Workers: React.FC = () => { const { @@ -55,6 +56,13 @@ const Workers: React.FC = () => { list: [], data: {} }); + const [workerDetailStatus, setWorkerDetailStatus] = useState<{ + open: boolean; + currentData: ListItem | null; + }>({ + open: false, + currentData: null + }); const getClusterList = async () => { try { @@ -118,6 +126,13 @@ const Workers: React.FC = () => { }); }; + const handleViewDetail = (record: ListItem) => { + setWorkerDetailStatus({ + open: true, + currentData: record + }); + }; + const handleSelect = useMemoizedFn((val: any, record: ListItem) => { if (val === 'edit') { handleUpdateLabels(record); @@ -126,6 +141,9 @@ const Workers: React.FC = () => { if (val === 'delete') { handleDelete(record); } + if (val === 'details') { + handleViewDetail(record); + } }); const renderEmpty = (type?: string) => { @@ -149,8 +167,8 @@ const Workers: React.FC = () => { const columns = useWorkerColumns({ clusterData, - dataSource, - extraStatus, + loadend: dataSource.loadend, + firstLoad: extraStatus.firstLoad, handleSelect }); @@ -216,6 +234,13 @@ const Workers: React.FC = () => { labels: updateLabelsData.data.labels }} > + + setWorkerDetailStatus({ currentData: null, open: false }) + } + /> ); diff --git a/src/pages/resources/hooks/use-instances-columns.tsx b/src/pages/resources/hooks/use-instances-columns.tsx new file mode 100644 index 00000000..0664b23b --- /dev/null +++ b/src/pages/resources/hooks/use-instances-columns.tsx @@ -0,0 +1,344 @@ +import AutoTooltip from '@/components/auto-tooltip'; +import { TooltipOverlayScroller } from '@/components/overlay-scroller'; +import SimpleTabel, { ColumnProps } from '@/components/simple-table'; +import InfoColumn from '@/components/simple-table/info-column'; +import StatusTag from '@/components/status-tag'; +import ThemeTag from '@/components/tags-wrapper/theme-tag'; +import { + InstanceStatusMap, + InstanceStatusMapValue, + status +} from '@/pages/llmodels/config'; +import { + DistributedServerItem, + ModelInstanceListItem as ListItem, + ModelInstanceListItem +} from '@/pages/llmodels/config/types'; +import { convertFileSize } from '@/utils'; +import { InfoCircleOutlined } from '@ant-design/icons'; +import { useIntl } from '@umijs/max'; +import { Button, Tooltip } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import _ from 'lodash'; +import { useMemo } from 'react'; +import styled from 'styled-components'; + +const GPUIndexWrapper = styled.span` + display: flex; + flex-direction: column; + gap: 2px; +`; + +const StatusWrapper = styled.div` + display: flex; + align-items: center; + gap: 4px; +`; + +const fieldList = [ + { + label: 'CPU', + key: 'cpuoffload', + locale: false + }, + { + label: 'GPU', + key: 'gpuoffload', + locale: false + } +]; + +const renderGpuIndexs = (gpuIndexes: number[]) => { + return ( + + {_.chunk(gpuIndexes, 8).map((item: number[], index: number) => { + return {item.join(',')}; + })} + + ); +}; + +const distributeCols: ColumnProps[] = [ + { + title: 'Worker', + key: 'worker_name', + style: { + wordBreak: 'break-word' + } + }, + { + title: 'IP', + key: 'worker_ip', + render: ({ row }) => { + return row.port ? `${row.worker_ip}:${row.port}` : row.worker_ip; + } + }, + { + title: 'models.table.gpuindex', + locale: true, + key: 'gpu_index', + render: ({ row }) => { + const list = _.sortBy(row.gpu_index, (item: number) => item); + return row.is_main ? ( + <> + {renderGpuIndexs(list)} + (main) + + ) : ( + renderGpuIndexs(list) + ); + } + }, + { + title: 'models.table.vram.allocated', + locale: true, + key: 'vram', + render: ({ rowIndex, row, dataList }) => { + return convertFileSize(row.vram, 1); + } + } +]; + +const calcTotalVram = (vram: Record) => { + return _.sum(_.values(vram)); +}; + +const renderDistributedServer = ( + severList: any[], + instanceData: ModelInstanceListItem +) => { + // const list = _.map(severList, (item: any) => { + // const data = _.find(workerList, { id: item.worker_id }); + // return { + // worker_name: data?.name, + // worker_ip: data?.ip, + // port: '', + // is_main: false, + // vram: calcTotalVram(item.computed_resource_claim?.vram || {}), + // gpu_index: _.keys(item.computed_resource_claim?.vram) + // }; + // }); + + const mainWorker = [ + { + worker_name: `${instanceData.worker_name}`, + worker_ip: `${instanceData.worker_ip}`, + port: '', + vram: calcTotalVram(instanceData.computed_resource_claim?.vram || {}), + is_main: true, + gpu_index: instanceData.gpu_indexes + } + ]; + + return ( +
+ +
+ ); +}; + +const DistributionInfo: React.FC<{ + instanceData: ModelInstanceListItem; +}> = ({ instanceData }) => { + const intl = useIntl(); + const distributed_servers = instanceData.distributed_servers; + const severList: DistributedServerItem[] = + distributed_servers?.subordinate_workers || []; + + if (!severList.length) { + return null; + } + + return ( + + + + {intl.formatMessage({ + id: 'models.table.acrossworker' + })} + + + ); +}; + +const OffloadInfo: React.FC<{ + instanceData: ListItem; +}> = (props) => { + const intl = useIntl(); + const { instanceData } = props; + const total_layers = instanceData.computed_resource_claim?.total_layers; + const offload_layers = instanceData.computed_resource_claim?.offload_layers; + if (total_layers === offload_layers || !total_layers) { + return null; + } + + const offloadData = { + cpuoffload: `${ + _.subtract( + instanceData.computed_resource_claim?.total_layers, + instanceData.computed_resource_claim?.offload_layers + ) || 0 + } ${intl.formatMessage({ + id: 'models.table.layers' + })}`, + gpuoffload: `${instanceData.computed_resource_claim?.offload_layers} ${intl.formatMessage( + { + id: 'models.table.layers' + } + )}` + }; + return ( + } + > + + + {intl.formatMessage({ + id: 'models.table.cpuoffload' + })} + + + ); +}; + +const InstanceStatusTag: React.FC<{ + instanceData: ListItem; +}> = (props) => { + const intl = useIntl(); + const { instanceData } = props; + if (!instanceData.state) { + return null; + } + return ( + <> + + {intl.formatMessage({ id: 'models.list.more.logs' })} + + ) : null + } + statusValue={{ + status: + instanceData.state === InstanceStatusMap.Downloading && + instanceData.download_progress === 100 + ? status[InstanceStatusMap.Running] + : status[instanceData.state], + text: InstanceStatusMapValue[instanceData.state], + message: + instanceData.state === InstanceStatusMap.Downloading && + instanceData.download_progress === 100 + ? '' + : instanceData.state_message + }} + /> + + ); +}; + +const useInstanceColumns = (props: { + clusterList: Global.BaseOption[]; +}): ColumnsType => { + const { clusterList } = props; + const intl = useIntl(); + + return useMemo(() => { + return [ + { + title: intl.formatMessage({ id: 'common.table.name' }), + dataIndex: 'name', + width: 240, + render: (text: string, record: ListItem) => ( + + {text} + + ) + }, + { + title: 'Worker', + dataIndex: 'worker_name', + render: (text: string, record: ListItem) => ( + {text} + ) + }, + { + title: intl.formatMessage({ id: 'models.title' }), + dataIndex: 'model_name', + render: (text: string, record: ListItem) => ( + {text} + ) + }, + { + title: intl.formatMessage({ id: 'clusters.title' }), + dataIndex: 'cluster_id', + render: (text: number, record: ListItem) => ( + + {clusterList.find((item) => item.value === text)?.label} + + ) + }, + { + title: intl.formatMessage({ id: 'common.table.status' }), + dataIndex: 'state', + render: (text: string, record: ListItem) => ( + + + + + + ) + } + ]; + }, [clusterList]); +}; + +export default useInstanceColumns; diff --git a/src/pages/resources/hooks/use-worker-columns.tsx b/src/pages/resources/hooks/use-worker-columns.tsx index f0728767..e358dba6 100644 --- a/src/pages/resources/hooks/use-worker-columns.tsx +++ b/src/pages/resources/hooks/use-worker-columns.tsx @@ -17,7 +17,7 @@ import { ColumnsType } from 'antd/lib/table'; import _ from 'lodash'; import { useMemo } from 'react'; import styled from 'styled-components'; -import { WorkerStatusMapValue, status } from '../config'; +import { WorkerStatusMap, WorkerStatusMapValue, status } from '../config'; import { Filesystem, GPUDeviceItem, ListItem } from '../config/types'; const LabelsWrapper = styled.div` @@ -28,6 +28,11 @@ const LabelsWrapper = styled.div` const ActionList = [ { label: 'common.button.edit', key: 'edit', icon: }, + { + label: 'common.button.detail', + key: 'details', + icon: + }, { label: 'common.button.logs', locale: false, @@ -43,6 +48,15 @@ const ActionList = [ } ]; +const setActions = (row: ListItem) => { + return ActionList.filter((action) => { + if (action.key === 'details' && row.state === WorkerStatusMap.ready) { + return true; + } + return true; + }); +}; + const fieldList = [ { label: 'resources.table.total', @@ -115,14 +129,14 @@ const VRAMCell = ({ devices, intl, rIndex, - dataSource, - extraStatus + loadend, + firstLoad }: { devices: GPUDeviceItem[]; intl: any; rIndex: number; - dataSource: any; - extraStatus: any; + loadend: boolean; + firstLoad: boolean; }) => ( {_.map( @@ -133,12 +147,7 @@ const VRAMCell = ({ [{item.index}] { const useWorkerColumns = ({ clusterData, - dataSource, - extraStatus, + loadend, + firstLoad, handleSelect }: { clusterData: { list: Global.BaseOption[]; data: Record; }; - dataSource: any; - extraStatus: any; + loadend: boolean; + firstLoad: boolean; handleSelect: (action: string, record: ListItem) => void; }): ColumnsType => { const intl = useIntl(); @@ -290,8 +299,8 @@ const useWorkerColumns = ({ devices={record?.status?.gpu_devices} intl={intl} rIndex={rIndex} - dataSource={dataSource} - extraStatus={extraStatus} + loadend={loadend} + firstLoad={firstLoad} /> ) }, @@ -305,13 +314,13 @@ const useWorkerColumns = ({ key: 'operation', render: (_, record) => ( handleSelect(val, record)} /> ) } ], - [intl, clusterData, dataSource, extraStatus, handleSelect] + [intl, clusterData, loadend, firstLoad, handleSelect] ); };