// columns.ts import { systemConfigAtom } from '@/atoms/system'; import { tableSorter } from '@/config/settings'; import { getGPUStackPlugin } from '@/plugins'; import { StarFilled } from '@ant-design/icons'; import { AutoTooltip, DropdownButtons, GrafanaIcon, StatusTag, icons, type TableColumnProps as SealColumnProps } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { Tooltip, Typography } from 'antd'; import dayjs from 'dayjs'; import { useAtomValue } from 'jotai'; import { useMemo } from 'react'; import { ClusterStatus, ClusterStatusLabelMap, ProviderLabelMap, ProviderValueMap } from '../config'; import { ClusterListItem } from '../config/types'; const clusterActionList = [ { key: 'edit', label: 'common.button.edit', icon: icons.EditOutlined, order: 10 }, { label: 'resources.metrics.details', key: 'metrics', icon: ( ), order: 20 }, { key: 'add_worker', label: 'resources.button.create', provider: ProviderValueMap.Docker, locale: true, icon: icons.DockerOutlined, order: 30 }, { key: 'register_cluster', label: 'clusters.button.register', provider: ProviderValueMap.Kubernetes, locale: true, icon: icons.KubernetesOutlined, order: 40 }, { key: 'addPool', label: 'clusters.button.addNodePool', provider: ProviderValueMap.DigitalOcean, locale: true, icon: icons.Catalog1, order: 50 }, { key: 'isDefault', label: 'clusters.form.setDefault', icon: icons.StarOutlined, order: 60 }, { key: 'delete', label: 'common.button.delete', icon: icons.DeleteOutlined, props: { danger: true }, order: 99 } ]; const useClusterColumns = ( handleSelect: (val: string, record: ClusterListItem) => void, onCellClick?: (record: ClusterListItem, dataIndex: string) => void ): SealColumnProps[] => { const intl = useIntl(); const systemConfig = useAtomValue(systemConfigAtom); // The cluster-detail page is shipped in OSS source, but OSS keeps // it unreachable from the cluster list — the link is only // surfaced when a plugin opts in via // `clusterDetail.linkableName`. Without a plugin we render the // name as plain text (matches the pre-restore behaviour); with one // we use Typography.Link wired to the parent's `onCellClick`. const { nameLinkable, useGenerateActions } = getGPUStackPlugin()?.clusterDetail || {}; const actions: typeof clusterActionList = useGenerateActions?.({ actions: clusterActionList }) || clusterActionList; const setActionsItems = (row: ClusterListItem) => { return actions.filter((item) => { if (item.provider) { return item.provider === row.provider; } if (item.key === 'metrics') { return systemConfig?.showMonitoring; } return true; }); }; return useMemo(() => { return [ { title: intl.formatMessage({ id: 'common.table.name' }), dataIndex: 'name', sorter: tableSorter(1), span: 3, render: (text: string, record: ClusterListItem) => ( <> {nameLinkable ? ( onCellClick?.(record, 'name')}> {record.name} ) : ( {record.name} )} {record.is_default && ( )} ) }, { title: intl.formatMessage({ id: 'clusters.table.provider' }), dataIndex: 'provider', sorter: tableSorter(2), span: 3, render: (value: string) => ( {ProviderLabelMap[value]} ) }, { title: 'GPUs', dataIndex: 'gpus', span: 2, sorter: tableSorter(3), render: (value: number) => {value} }, { title: intl.formatMessage({ id: 'clusters.table.deployments' }), dataIndex: 'models', sorter: tableSorter(4), span: 3, render: (value: number) => {value} }, { title: intl.formatMessage({ id: 'resources.nodes' }), dataIndex: 'workers', sorter: tableSorter(5), span: 3, render: (value: number, record: ClusterListItem) => ( {record.ready_workers} / {record.workers} ) }, { title: intl.formatMessage({ id: 'common.table.status' }), dataIndex: 'state', span: 3, render: (value: number, record: ClusterListItem) => ( ) }, { title: intl.formatMessage({ id: 'common.table.createTime' }), dataIndex: 'created_at', sorter: tableSorter(6), span: 4, render: (value: string) => ( {dayjs(value).format('YYYY-MM-DD HH:mm:ss')} ) }, { title: intl.formatMessage({ id: 'common.table.operation' }), dataIndex: 'operations', span: 3, render: (value: string, record: ClusterListItem) => ( handleSelect(val, record)} > ) } ]; }, [handleSelect, onCellClick]); }; export default useClusterColumns;