From 97179365bbaaaa9c814e03f306bd9aed58fef90e Mon Sep 17 00:00:00 2001 From: jialin Date: Tue, 14 Jul 2026 16:02:34 +0800 Subject: [PATCH] feat(cluster): drop detail page; host Cluster Access drawer Remove the cluster-detail route and the name-link navigation; the cluster name is now plain text. Mount the plugin-provided clusterDetail.AccessDrawer so the enterprise row action can open it. --- config/routes.ts | 12 +------- src/pages/cluster-management/clusters.tsx | 19 ++++++------- .../hooks/use-cluster-columns.tsx | 28 ++++++------------- 3 files changed, 17 insertions(+), 42 deletions(-) diff --git a/config/routes.ts b/config/routes.ts index 1c7469e8..5b45a427 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -273,7 +273,7 @@ const baseRoutes = [ selectedIcon: 'icon-cluster2-filled', defaultIcon: 'icon-cluster2-outline', component: './cluster-management/clusters', - subMenu: ['/resources/clusters/detail', '/resources/clusters/create'] + subMenu: ['/resources/clusters/create'] }, { name: 'workers', @@ -301,16 +301,6 @@ const baseRoutes = [ selectedIcon: 'icon-credential-filled', defaultIcon: 'icon-credential-outline', component: './cluster-management/credentials' - }, - { - name: 'clusterDetail', - path: '/resources/clusters/detail', - key: 'clusterDetail', - icon: 'icon-cluster2-outline', - selectedIcon: 'icon-cluster2-filled', - defaultIcon: 'icon-cluster2-outline', - hideInMenu: true, - component: './cluster-management/cluster-detail' } ] }, diff --git a/src/pages/cluster-management/clusters.tsx b/src/pages/cluster-management/clusters.tsx index 23623af2..763dd9fd 100644 --- a/src/pages/cluster-management/clusters.tsx +++ b/src/pages/cluster-management/clusters.tsx @@ -5,6 +5,7 @@ import type { PageActionType } from '@/config/types'; import useExpandedRowKeys from '@/hooks/use-expanded-row-keys'; import useTableFetch from '@/hooks/use-table-fetch'; import useWatchList from '@/hooks/use-watch-list'; +import { getGPUStackPlugin } from '@/plugins'; import { DeleteModal, FilterBar, @@ -14,7 +15,7 @@ import { TableOrder, TableProvider } from '@gpustack/core-ui'; -import { useIntl, useNavigate } from '@umijs/max'; +import { useIntl } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { message } from 'antd'; import { useAtom } from 'jotai'; @@ -82,10 +83,13 @@ const Clusters: React.FC = () => { mine: true } }); - const navigate = useNavigate(); const { goToGrafana, ActionButton } = useGranfanaLink({ type: 'cluster' }); + // Cluster Access lives in the enterprise plugin: it contributes the + // row action and this self-controlled drawer, owning its own + // open/close state. OSS just mounts it (nothing without a plugin). + const AccessDrawer = getGPUStackPlugin()?.clusterDetail?.AccessDrawer; const { watchDataList: allWorkerPoolList } = useWatchList(WORKER_POOLS_API); const [expandAtom] = useAtom(expandKeysAtom); const [clusterSession, setClusterSession] = useAtom(clusterSessionAtom); @@ -272,14 +276,6 @@ const Clusters: React.FC = () => { ); }; - const handleOnCell = useMemoizedFn((record: ClusterListItem, dataIndex) => { - if (dataIndex === 'name') { - navigate( - `/resources/clusters/detail?id=${record.id}&name=${record.name}&page=clusters` - ); - } - }); - useEffect(() => { const fetchCredentialList = async () => { const data = await queryCredentialList({ page: -1 }); @@ -366,7 +362,7 @@ const Clusters: React.FC = () => { ); }; - const columns = useClusterColumns(handleSelect, handleOnCell); + const columns = useClusterColumns(handleSelect); return ( <> @@ -482,6 +478,7 @@ const Clusters: React.FC = () => { onClose={handleClusterModalClose} > {AddWorkerModal} + {AccessDrawer && } ); }; diff --git a/src/pages/cluster-management/hooks/use-cluster-columns.tsx b/src/pages/cluster-management/hooks/use-cluster-columns.tsx index c1ef413e..e8cd7bc5 100644 --- a/src/pages/cluster-management/hooks/use-cluster-columns.tsx +++ b/src/pages/cluster-management/hooks/use-cluster-columns.tsx @@ -13,7 +13,7 @@ import { type TableColumnProps as SealColumnProps } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; -import { Tooltip, Typography } from 'antd'; +import { Tooltip } from 'antd'; import dayjs from 'dayjs'; import { useAtomValue } from 'jotai'; import { useMemo } from 'react'; @@ -85,21 +85,15 @@ const clusterActionList = [ ]; const useClusterColumns = ( - handleSelect: (val: string, record: ClusterListItem, item?: any) => void, - onCellClick?: (record: ClusterListItem, dataIndex: string) => void + handleSelect: (val: string, record: ClusterListItem, item?: any) => void ): SealColumnProps[] => { const intl = useIntl(); const systemConfig = useAtomValue(systemConfigAtom); const pluginCols = usePluginListColumns('clusters'); - // 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 { linkableName: nameLinkable, useGenerateActions } = - getGPUStackPlugin()?.clusterDetail || {}; + // The cluster name is plain text: there is no cluster-detail page + // to route into. A plugin may still contribute extra row actions + // (topology, Cluster Access) via `clusterDetail.useGenerateActions`. + const { useGenerateActions } = getGPUStackPlugin()?.clusterDetail || {}; const actionList = useGenerateActions?.({ actions: clusterActionList }) || clusterActionList; @@ -160,13 +154,7 @@ const useClusterColumns = ( render: (text: string, record: ClusterListItem) => ( <> - {nameLinkable ? ( - onCellClick?.(record, 'name')}> - {record.name} - - ) : ( - {record.name} - )} + {record.name} {record.is_default && (