feat(cluster-detail): add cluster switcher; consume core-ui header-slot
page-box now imports the header-slot bridge from core-ui and re-exports it, plus a dev warning when a header slot has multiple owners. cluster-detail breadcrumb becomes a BaseSelect to switch clusters, and the tab tables are keyed by id so they refetch on switch.
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import { clusterDetailAtom } from '@/atoms/clusters';
|
||||
import GPUList from '@/pages/resources/components/gpus';
|
||||
import WorkerList from '@/pages/resources/components/workers';
|
||||
import { getGPUStackPlugin } from '@/plugins';
|
||||
import { IconFont } from '@gpustack/core-ui';
|
||||
import { BaseSelect, IconFont } from '@gpustack/core-ui';
|
||||
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
||||
import { Tabs, type TabsProps } from 'antd';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import { HeaderLeft } from '../_components/page-box';
|
||||
import PageBreadcrumb from '../_components/page-breadcrumb';
|
||||
import ClusterBasic from './components/detail/cluster-basic';
|
||||
import ClusterSystemLoad from './components/detail/cluster-system-load';
|
||||
import { useQueryClusterList } from './services/use-query-cluster-list';
|
||||
|
||||
const ClusterDetailModal = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -17,7 +17,18 @@ const ClusterDetailModal = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const id = searchParams.get('id');
|
||||
const clusterName = searchParams.get('name');
|
||||
const clusterDetailData = useAtomValue(clusterDetailAtom);
|
||||
const { clusterList, fetchClusterList, cancelRequest } =
|
||||
useQueryClusterList();
|
||||
|
||||
// Switch to another cluster's detail from the breadcrumb. Navigating with a
|
||||
// new ``id`` re-renders this page in place; the detail children refetch on
|
||||
// their ``clusterId`` prop change, so the whole view updates.
|
||||
const handleOnChange = (value: number, option: any) => {
|
||||
navigate(
|
||||
`/resources/clusters/detail?id=${option.value}&name=${option.label}&page=clusters`,
|
||||
{ replace: true }
|
||||
);
|
||||
};
|
||||
|
||||
const breadcrumbItems = [
|
||||
{
|
||||
@@ -25,10 +36,27 @@ const ClusterDetailModal = () => {
|
||||
onClick: () => navigate(-1)
|
||||
},
|
||||
{
|
||||
title: clusterName
|
||||
title: (
|
||||
<BaseSelect
|
||||
size="small"
|
||||
variant="borderless"
|
||||
options={clusterList}
|
||||
value={clusterName}
|
||||
style={{ minWidth: 100 }}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={handleOnChange}
|
||||
></BaseSelect>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
fetchClusterList({ page: -1 });
|
||||
return () => {
|
||||
cancelRequest();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Extension slot: a registered plugin may inject additional tab
|
||||
// items (e.g. per-cluster access / quota panels) by exporting
|
||||
// ``clusterDetail.extraTabs(clusterId, intl)`` that returns an
|
||||
@@ -62,14 +90,20 @@ const ClusterDetailModal = () => {
|
||||
label: intl.formatMessage({ id: 'resources.nodes' }),
|
||||
icon: <IconFont type="icon-resources" />,
|
||||
children: (
|
||||
<WorkerList clusterId={Number(id)} source="clusterDetail" />
|
||||
<WorkerList
|
||||
key={id}
|
||||
clusterId={Number(id)}
|
||||
source="clusterDetail"
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'gpus',
|
||||
label: intl.formatMessage({ id: 'menu.resources.gpus' }),
|
||||
icon: <IconFont type="icon-gpu1" />,
|
||||
children: <GPUList clusterId={Number(id)} source="clusterDetail" />
|
||||
children: (
|
||||
<GPUList key={id} clusterId={Number(id)} source="clusterDetail" />
|
||||
)
|
||||
},
|
||||
...extraTabs
|
||||
]}
|
||||
|
||||
Reference in New Issue
Block a user