From 2dfdd4a0d99ee58c5551c82aa24dfe86f587c612 Mon Sep 17 00:00:00 2001 From: jialin Date: Fri, 16 Jan 2026 19:06:24 +0800 Subject: [PATCH] feat: add provider menu --- config/routes.ts | 10 + src/components/icon-font/index.tsx | 2 +- src/locales/en-US/menu.ts | 1 + src/locales/en-US/no-result.ts | 5 +- src/locales/ja-JP/menu.ts | 2 + src/locales/ja-JP/no-result.ts | 5 +- src/locales/ru-RU/menu.ts | 2 + src/locales/ru-RU/no-result.ts | 8 +- src/locales/zh-CN/menu.ts | 1 + src/locales/zh-CN/no-result.ts | 5 +- src/pages/maas-provider/apis/index.ts | 50 ++++ .../components/add-provider-modal.tsx | 62 +++++ .../components/provider-models.tsx | 11 + src/pages/maas-provider/config/index.ts | 73 +++++ src/pages/maas-provider/config/types.ts | 13 + src/pages/maas-provider/forms/index.tsx | 32 +++ .../hooks/use-create-provider.ts | 39 +++ .../hooks/use-provider-columns.tsx | 127 +++++++++ src/pages/maas-provider/index.tsx | 256 ++++++++++++++++++ src/pages/resources/components/gpus.tsx | 2 +- src/pages/resources/components/workers.tsx | 2 +- 21 files changed, 701 insertions(+), 7 deletions(-) create mode 100644 src/pages/maas-provider/apis/index.ts create mode 100644 src/pages/maas-provider/components/add-provider-modal.tsx create mode 100644 src/pages/maas-provider/components/provider-models.tsx create mode 100644 src/pages/maas-provider/config/index.ts create mode 100644 src/pages/maas-provider/config/types.ts create mode 100644 src/pages/maas-provider/forms/index.tsx create mode 100644 src/pages/maas-provider/hooks/use-create-provider.ts create mode 100644 src/pages/maas-provider/hooks/use-provider-columns.tsx create mode 100644 src/pages/maas-provider/index.tsx diff --git a/config/routes.ts b/config/routes.ts index af629e3c..6b4b405e 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -105,6 +105,16 @@ export default [ access: 'canSeeAdmin', component: './llmodels/index' }, + { + name: 'provider', + path: '/models/providers', + key: 'modelProviders', + icon: 'icon-extension-outline', + selectedIcon: 'icon-extension-filled', + defaultIcon: 'icon-extension-outline', + access: 'canSeeAdmin', + component: './maas-provider/index' + }, { name: 'userModels', path: '/models/user-models', diff --git a/src/components/icon-font/index.tsx b/src/components/icon-font/index.tsx index fb4f8e9e..63ea7fe4 100644 --- a/src/components/icon-font/index.tsx +++ b/src/components/icon-font/index.tsx @@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons'; // import './iconfont/iconfont.js'; const IconFont = createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/c/font_4613488_lqs6kytf9t8.js' + scriptUrl: '//at.alicdn.com/t/c/font_4613488_qm5jah0zmp8.js' }); export default IconFont; diff --git a/src/locales/en-US/menu.ts b/src/locales/en-US/menu.ts index e0f1bda6..3da05664 100644 --- a/src/locales/en-US/menu.ts +++ b/src/locales/en-US/menu.ts @@ -16,6 +16,7 @@ export default { 'menu.models.userModels': 'My Models', 'menu.models.benchmark': 'Benchmarks', 'menu.models.benchmarkDetail': 'Benchmark Details', + 'menu.models.provider': 'Provider', 'menu.modelCatalog': 'Catalog', 'menu.resources': 'Resources', 'menu.apikeys': 'API Keys', diff --git a/src/locales/en-US/no-result.ts b/src/locales/en-US/no-result.ts index b18153ec..404d70d1 100644 --- a/src/locales/en-US/no-result.ts +++ b/src/locales/en-US/no-result.ts @@ -42,5 +42,8 @@ export default { 'noresult.resources.gotoworker': 'Add Worker', 'noresult.benchmark.title': 'No Benchmarks', 'noresult.benchmark.subTitle': 'No benchmarks have been added yet.', - 'noresult.benchmark.nofound': 'No matching benchmarks found.' + 'noresult.benchmark.nofound': 'No matching benchmarks found.', + 'noresult.provider.title': 'No Providers', + 'noresult.provider.subTitle': 'No providers have been added yet.', + 'noresult.provider.nofound': 'No matching providers found.' }; diff --git a/src/locales/ja-JP/menu.ts b/src/locales/ja-JP/menu.ts index 11a52a63..84f16bcd 100644 --- a/src/locales/ja-JP/menu.ts +++ b/src/locales/ja-JP/menu.ts @@ -16,6 +16,7 @@ export default { 'menu.models.benchmark': 'Benchmarks', 'menu.models.benchmarkDetail': 'Benchmark Details', 'menu.modelCatalog': 'カタログ', + 'menu.models.provider': 'Provider', 'menu.resources': 'リソース', 'menu.apikeys': 'APIキー', 'menu.users': 'ユーザー', @@ -54,4 +55,5 @@ export default { // 13. 'menu.clusterManagement.clusterCreate': 'Create Cluster', // 14. 'menu.resources.backendsList': 'Inference Backends', // 15. 'menu.models.benchmark': 'Benchmarks', +// 15. 'menu.models.provider': 'Provider', // ========== End of To-Do List ========== diff --git a/src/locales/ja-JP/no-result.ts b/src/locales/ja-JP/no-result.ts index b18153ec..404d70d1 100644 --- a/src/locales/ja-JP/no-result.ts +++ b/src/locales/ja-JP/no-result.ts @@ -42,5 +42,8 @@ export default { 'noresult.resources.gotoworker': 'Add Worker', 'noresult.benchmark.title': 'No Benchmarks', 'noresult.benchmark.subTitle': 'No benchmarks have been added yet.', - 'noresult.benchmark.nofound': 'No matching benchmarks found.' + 'noresult.benchmark.nofound': 'No matching benchmarks found.', + 'noresult.provider.title': 'No Providers', + 'noresult.provider.subTitle': 'No providers have been added yet.', + 'noresult.provider.nofound': 'No matching providers found.' }; diff --git a/src/locales/ru-RU/menu.ts b/src/locales/ru-RU/menu.ts index ade2c0f6..295e55bd 100644 --- a/src/locales/ru-RU/menu.ts +++ b/src/locales/ru-RU/menu.ts @@ -15,6 +15,7 @@ export default { 'menu.models.deployment': 'Запуск', 'menu.models.benchmark': 'Benchmarks', 'menu.models.benchmarkDetail': 'Benchmark Details', + 'menu.models.provider': 'Provider', 'menu.modelCatalog': 'Каталог', 'menu.resources': 'Ресурсы', 'menu.apikeys': 'API-ключи', @@ -41,4 +42,5 @@ export default { // ========== To-Do: Translate Keys (Remove After Translation) ========== // 1. 'menu.models.benchmark': 'Benchmarks', // 2. 'menu.models.benchmarkDetail': 'Benchmark Details', +// 1. 'menu.models.provider': 'Provider', // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/no-result.ts b/src/locales/ru-RU/no-result.ts index 5ccf0148..8c4fb545 100644 --- a/src/locales/ru-RU/no-result.ts +++ b/src/locales/ru-RU/no-result.ts @@ -43,7 +43,10 @@ export default { 'noresult.resources.gotoworker': 'Add Worker', 'noresult.benchmark.title': 'No Benchmarks', 'noresult.benchmark.subTitle': 'No benchmarks have been added yet.', - 'noresult.benchmark.nofound': 'No matching benchmarks found.' + 'noresult.benchmark.nofound': 'No matching benchmarks found.', + 'noresult.provider.title': 'No Providers', + 'noresult.provider.subTitle': 'No providers have been added yet.', + 'noresult.provider.nofound': 'No matching providers found.' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -54,4 +57,7 @@ export default { // 5. 'noresult.benchmark.title': 'No Benchmarks', // 6. 'noresult.benchmark.subTitle': 'No benchmarks have been added yet.', // 7. 'noresult.benchmark.nofound': 'No matching benchmarks found.' +// 5. 'noresult.provider.title': 'No Providers', +// 6. 'noresult.provider.subTitle': 'No providers have been added yet.', +// 7. 'noresult.provider.nofound': 'No matching providers found.' // ======================================================================== diff --git a/src/locales/zh-CN/menu.ts b/src/locales/zh-CN/menu.ts index f6dd1d86..17948eee 100644 --- a/src/locales/zh-CN/menu.ts +++ b/src/locales/zh-CN/menu.ts @@ -15,6 +15,7 @@ export default { 'menu.models.userModels': '我的模型', 'menu.models.benchmark': '基准测试', 'menu.models.benchmarkDetail': '基准测试详情', + 'menu.models.provider': '渠道', 'menu.modelCatalog': '模型库', 'menu.models.catalog': '模型库', 'menu.resources': '资源', diff --git a/src/locales/zh-CN/no-result.ts b/src/locales/zh-CN/no-result.ts index 42128220..c74d392d 100644 --- a/src/locales/zh-CN/no-result.ts +++ b/src/locales/zh-CN/no-result.ts @@ -40,5 +40,8 @@ export default { 'noresult.resources.gotoworker': '添加节点', 'noresult.benchmark.title': '暂无基准测试', 'noresult.benchmark.subTitle': '尚未添加任何基准测试。', - 'noresult.benchmark.nofound': '未找到匹配的基准测试' + 'noresult.benchmark.nofound': '未找到匹配的基准测试', + 'noresult.provider.title': '暂无渠道', + 'noresult.provider.subTitle': '尚未添加任何渠道。', + 'noresult.provider.nofound': '未找到匹配的渠道' }; diff --git a/src/pages/maas-provider/apis/index.ts b/src/pages/maas-provider/apis/index.ts new file mode 100644 index 00000000..0cde8090 --- /dev/null +++ b/src/pages/maas-provider/apis/index.ts @@ -0,0 +1,50 @@ +import { request } from '@umijs/max'; +import { FormData, MaasProviderItem } from '../config/types'; + +export const MAAS_PROVIDERS_API = '/maas-providers'; + +export const PROVIDER_MODELS_API = '/maas-provider-models'; + +export async function queryMaasProviders( + params: Global.SearchParams, + options?: any +) { + return request>(MAAS_PROVIDERS_API, { + params, + method: 'GET', + cancelToken: options?.token + }); +} + +export async function createProvider(params: { data: FormData }) { + return request(`${MAAS_PROVIDERS_API}`, { + method: 'POST', + data: params.data + }); +} + +export async function updateProvider(params: { id: number; data: FormData }) { + return request(`${MAAS_PROVIDERS_API}/${params.id}`, { + method: 'PUT', + data: params.data + }); +} + +export async function deleteProvider(id: number) { + return request(`${MAAS_PROVIDERS_API}/${id}`, { + method: 'DELETE' + }); +} + +export async function queryProviderModels( + params: { id: string }, + options?: any +) { + return request[]>( + `${MAAS_PROVIDERS_API}/${params.id}/models`, + { + method: 'GET', + cancelToken: options?.token + } + ); +} diff --git a/src/pages/maas-provider/components/add-provider-modal.tsx b/src/pages/maas-provider/components/add-provider-modal.tsx new file mode 100644 index 00000000..5a56a837 --- /dev/null +++ b/src/pages/maas-provider/components/add-provider-modal.tsx @@ -0,0 +1,62 @@ +import { PageActionType } from '@/config/types'; +import FormDrawer from '@/pages/_components/form-drawer'; +import React, { useRef } from 'react'; +import { FormData, MaasProviderItem as ListItem } from '../config/types'; + +import { maasProviderType } from '../config'; +import ProviderForm from '../forms'; + +type AddModalProps = { + title: string; + action: PageActionType; + open: boolean; + currentData?: ListItem; // Used when action is EDIT + provider: maasProviderType | null; + onOk: (values: FormData) => void; + onCancel: () => void; +}; +const AddProvider: React.FC = ({ + title, + action, + open, + provider, + currentData, + onOk, + onCancel +}) => { + const form = useRef(null); + + const handleSubmit = () => { + form.current?.submit(); + }; + + const handleOk = async (data: FormData) => { + onOk({ + ...data + }); + }; + + const handleCancel = () => { + form.current?.resetFields(); + onCancel(); + }; + + return ( + + + + ); +}; + +export default AddProvider; diff --git a/src/pages/maas-provider/components/provider-models.tsx b/src/pages/maas-provider/components/provider-models.tsx new file mode 100644 index 00000000..81f1b8f1 --- /dev/null +++ b/src/pages/maas-provider/components/provider-models.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +interface ProviderModelProps { + name: string; +} + +const ProviderModels: React.FC = () => { + return
Provider Models Component
; +}; + +export default ProviderModels; diff --git a/src/pages/maas-provider/config/index.ts b/src/pages/maas-provider/config/index.ts new file mode 100644 index 00000000..ea32d85b --- /dev/null +++ b/src/pages/maas-provider/config/index.ts @@ -0,0 +1,73 @@ +import icons from '@/components/icon-font/icons'; +import { StatusMaps } from '@/config'; +import { StatusType } from '@/config/types'; + +export type maasProviderType = + | 'doubao' + | 'qwen' + | 'openai' + | 'deepseek' + | 'anthropic'; + +export const maasProviderValueMap = { + Doubao: 'doubao', + Qwen: 'qwen', + OpenAI: 'openai', + Deepseek: 'deepseek', + Anthropic: 'anthropic' +}; + +export const maasProviderLabelMap = { + [maasProviderValueMap.Doubao]: 'Doubao', + [maasProviderValueMap.Qwen]: 'Qwen', + [maasProviderValueMap.OpenAI]: 'OpenAI', + [maasProviderValueMap.Deepseek]: 'Deepseek', + [maasProviderValueMap.Anthropic]: 'Anthropic' +}; + +export const IconsMap = { + [maasProviderValueMap.Doubao]: 'icon-doubao', + [maasProviderValueMap.Qwen]: 'icon-qwen', + [maasProviderValueMap.OpenAI]: 'icon-openai', + [maasProviderValueMap.Deepseek]: 'icon-deepseek', + [maasProviderValueMap.Anthropic]: 'icon-anthropic' +}; + +export const maasProviderOptions = Object.keys(maasProviderValueMap).map( + (key) => ({ + label: maasProviderLabelMap[key], + value: maasProviderValueMap[key as keyof typeof maasProviderValueMap] + }) +); + +export const ProviderStatusValueMap: Record = { + Ready: 'Ready', + InActive: 'Inactive' +}; + +export const ProviderStatusLabelMap = { + [ProviderStatusValueMap.Ready]: 'Ready', + [ProviderStatusValueMap.InActive]: 'Inactive' +}; + +export const ProviderStatus: Record = { + [ProviderStatusValueMap.Ready]: StatusMaps.success, + [ProviderStatusValueMap.InActive]: StatusMaps.error +}; + +// actions for each row +export const rowActionList = [ + { + key: 'edit', + label: 'common.button.edit', + icon: icons.EditOutlined + }, + { + key: 'delete', + label: 'common.button.delete', + icon: icons.DeleteOutlined, + props: { + danger: true + } + } +]; diff --git a/src/pages/maas-provider/config/types.ts b/src/pages/maas-provider/config/types.ts new file mode 100644 index 00000000..9fcb9ced --- /dev/null +++ b/src/pages/maas-provider/config/types.ts @@ -0,0 +1,13 @@ +import { maasProviderType } from '.'; + +export interface MaasProviderItem { + id: number; + name: string; + state: string; + state_message: string; + provider: maasProviderType; +} + +export interface FormData { + name: string; +} diff --git a/src/pages/maas-provider/forms/index.tsx b/src/pages/maas-provider/forms/index.tsx new file mode 100644 index 00000000..100988eb --- /dev/null +++ b/src/pages/maas-provider/forms/index.tsx @@ -0,0 +1,32 @@ +import { PageActionType } from '@/config/types'; +import { Form } from 'antd'; +import { forwardRef, useImperativeHandle } from 'react'; +import { MaasProviderItem as ListItem } from '../config/types'; + +interface ProviderFormProps { + ref?: any; + action: PageActionType; + currentData?: ListItem; // Used when action is EDIT + onFinish: (values: FormData) => void; +} + +const ProviderForm: React.FC = forwardRef((props, ref) => { + const [form] = Form.useForm(); + + useImperativeHandle(ref, () => ({ + submit: () => { + form.submit(); + }, + resetFields: () => { + form.resetFields(); + } + })); + + return ( +
+ {/* Form fields go here */} +
+ ); +}); + +export default ProviderForm; diff --git a/src/pages/maas-provider/hooks/use-create-provider.ts b/src/pages/maas-provider/hooks/use-create-provider.ts new file mode 100644 index 00000000..84f8e0ab --- /dev/null +++ b/src/pages/maas-provider/hooks/use-create-provider.ts @@ -0,0 +1,39 @@ +import { PageAction } from '@/config'; +import { PageActionType } from '@/config/types'; +import { useState } from 'react'; +import { maasProviderType } from '../config'; +import { MaasProviderItem as ListItem } from '../config/types'; + +const useCreateProvider = (options: { refresh: () => void }) => { + const [openModalStatus, setOpenModalStatus] = useState<{ + open: boolean; + action: PageActionType; + currentData?: ListItem; + title: string; + provider: maasProviderType | null; + }>({ + open: false, + action: PageAction.CREATE, + currentData: undefined, + title: '', + provider: null + }); + + const openModal = () => { + setOpenModalStatus({ ...openModalStatus, open: true }); + }; + + const closeModal = () => { + setOpenModalStatus({ ...openModalStatus, open: false }); + options.refresh(); + }; + + return { + openProviderModalStatus: openModalStatus, + setOpenProviderModalStatus: setOpenModalStatus, + openProviderModal: openModal, + closeProviderModal: closeModal + }; +}; + +export default useCreateProvider; diff --git a/src/pages/maas-provider/hooks/use-provider-columns.tsx b/src/pages/maas-provider/hooks/use-provider-columns.tsx new file mode 100644 index 00000000..561f97fc --- /dev/null +++ b/src/pages/maas-provider/hooks/use-provider-columns.tsx @@ -0,0 +1,127 @@ +// columns.ts +import AutoTooltip from '@/components/auto-tooltip'; +import DropdownButtons from '@/components/drop-down-buttons'; +import { SealColumnProps } from '@/components/seal-table/types'; +import StatusTag from '@/components/status-tag'; +import { tableSorter } from '@/config/settings'; +import { StarFilled } from '@ant-design/icons'; +import { useIntl } from '@umijs/max'; +import { Tooltip } from 'antd'; +import dayjs from 'dayjs'; +import { useMemo } from 'react'; +import { + ProviderStatus, + ProviderStatusLabelMap, + rowActionList +} from '../config'; +import { MaasProviderItem } from '../config/types'; + +const useProviderColumns = ( + handleSelect: (val: string, record: MaasProviderItem) => void, + onCellClick?: (record: MaasProviderItem, dataIndex: string) => void +): SealColumnProps[] => { + const intl = useIntl(); + + return useMemo(() => { + return [ + { + title: intl.formatMessage({ id: 'common.table.name' }), + dataIndex: 'name', + sorter: tableSorter(1), + span: 3, + render: (text: string, record: MaasProviderItem) => ( + <> + + {text} + + {record.is_default && ( + + + + )} + + ) + }, + { + title: intl.formatMessage({ id: 'clusters.table.provider' }), + dataIndex: 'provider', + sorter: tableSorter(2), + span: 3, + render: (value: string) => ( + + {ProviderStatusLabelMap[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: MaasProviderItem) => ( + + {record.ready_workers} / {record.workers} + + ) + }, + { + title: intl.formatMessage({ id: 'common.table.status' }), + dataIndex: 'state', + span: 3, + render: (value: string, record: MaasProviderItem) => ( + + ) + }, + { + 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: MaasProviderItem) => ( + handleSelect(val, record)} + > + ) + } + ]; + }, [handleSelect, onCellClick]); +}; + +export default useProviderColumns; diff --git a/src/pages/maas-provider/index.tsx b/src/pages/maas-provider/index.tsx new file mode 100644 index 00000000..9d28695c --- /dev/null +++ b/src/pages/maas-provider/index.tsx @@ -0,0 +1,256 @@ +import { expandKeysAtom } from '@/atoms/clusters'; +import DeleteModal from '@/components/delete-modal'; +import IconFont from '@/components/icon-font'; +import { FilterBar } from '@/components/page-tools'; +import SealTable from '@/components/seal-table'; +import TableContext from '@/components/seal-table/table-context'; +import { TableOrder } from '@/components/seal-table/types'; +import { PageAction } from '@/config'; +import { TABLE_SORT_DIRECTIONS } from '@/config/settings'; +import useExpandedRowKeys from '@/hooks/use-expanded-row-keys'; +import useTableFetch from '@/hooks/use-table-fetch'; +import useWatchList from '@/hooks/use-watch-list'; +import { useIntl } from '@umijs/max'; +import { useMemoizedFn } from 'ahooks'; +import { message } from 'antd'; +import { useAtom } from 'jotai'; +import _ from 'lodash'; +import NoResult from '../_components/no-result'; +import PageBox from '../_components/page-box'; +import { + MAAS_PROVIDERS_API, + PROVIDER_MODELS_API, + deleteProvider, + queryMaasProviders, + queryProviderModels, + updateProvider +} from './apis'; +import AddMaasProvider from './components/add-provider-modal'; +import ProviderModels from './components/provider-models'; +import { + FormData, + MaasProviderItem as ListItem, + MaasProviderItem +} from './config/types'; +import useCreateProvider from './hooks/use-create-provider'; +import useProviderColumns from './hooks/use-provider-columns'; + +const MaasProvider: React.FC = () => { + const { + dataSource, + rowSelection, + queryParams, + modalRef, + handleTableChange, + handleDelete, + handleDeleteBatch, + fetchData, + handlePageChange, + handleSearch, + handleNameChange + } = useTableFetch({ + fetchAPI: queryMaasProviders, + deleteAPI: deleteProvider, + watch: true, + API: MAAS_PROVIDERS_API, + contentForDelete: 'menu.clusterManagement.clusters' + }); + const { watchDataList: allProviderModels } = + useWatchList(PROVIDER_MODELS_API); + const [expandAtom] = useAtom(expandKeysAtom); + const { handleExpandChange, handleExpandAll, expandedRowKeys } = + useExpandedRowKeys(expandAtom); + const intl = useIntl(); + const { + openProviderModalStatus, + setOpenProviderModalStatus, + openProviderModal, + closeProviderModal + } = useCreateProvider({ + refresh: handleSearch + }); + + const handleClickDropdown = () => { + openProviderModal(); + }; + + const handleModalOk = async (data: FormData) => { + const params = { + ...data + }; + try { + if (openProviderModalStatus.action === PageAction.EDIT) { + await updateProvider({ + data: params, + id: openProviderModalStatus.currentData!.id + }); + } + fetchData(); + setOpenProviderModalStatus({ + open: false, + action: PageAction.CREATE, + currentData: undefined, + title: '', + provider: null + }); + message.success(intl.formatMessage({ id: 'common.message.success' })); + } catch (error) {} + }; + + const handleModalCancel = () => { + console.log('handleModalCancel'); + setOpenProviderModalStatus({ + open: false, + action: PageAction.CREATE, + currentData: undefined, + title: '', + provider: null + }); + }; + + const handleEditProvider = (row: ListItem) => { + setOpenProviderModalStatus({ + open: true, + action: PageAction.EDIT, + currentData: row, + title: intl.formatMessage( + { id: 'clusters.edit.cluster' }, + { cluster: row.name } + ), + provider: row.provider + }); + }; + + const handleSelect = useMemoizedFn((val: any, row: ListItem) => { + if (val === 'edit') { + handleEditProvider(row); + } else if (val === 'delete') { + handleDelete({ ...row, name: row.name }); + } + }); + + const handleOnToggleExpandAll = () => { + // do nothing + }; + + const handleToggleExpandAll = useMemoizedFn((expanded: boolean) => { + const keys = dataSource.dataList?.map((item) => item.id); + handleExpandAll(expanded, keys); + if (expanded) { + handleOnToggleExpandAll(); + } + }); + + const loadChildrenData = useMemoizedFn( + async (row: MaasProviderItem, options?: any) => { + const params = { + cluster_id: row.id, + page: -1 + }; + const data = await queryProviderModels(params, { + token: options?.token + }); + return data?.items || []; + } + ); + + const handleOnSortChange = (order: TableOrder | Array) => { + handleTableChange({}, {}, order, { action: 'sort' }); + }; + + const renderChildren = ( + list: any, + options: { parent?: any; [key: string]: any } + ) => { + return ( + + ); + }; + + const columns = useProviderColumns(handleSelect); + + return ( + <> + + + + } + filters={_.omit(queryParams, ['sort_by'])} + noFoundText={intl.formatMessage({ + id: 'noresult.provider.nofound' + })} + title={intl.formatMessage({ id: 'noresult.provider.title' })} + subTitle={intl.formatMessage({ + id: 'noresult.provider.subTitle' + })} + onClick={handleClickDropdown} + buttonText={intl.formatMessage({ id: 'noresult.button.add' })} + > + } + pagination={{ + showSizeChanger: true, + pageSize: queryParams.perPage, + current: queryParams.page, + total: dataSource.total, + hideOnSinglePage: queryParams.perPage === 10, + onChange: handlePageChange + }} + > + + + + + + ); +}; + +export default MaasProvider; diff --git a/src/pages/resources/components/gpus.tsx b/src/pages/resources/components/gpus.tsx index 9b9cc8db..005382c2 100644 --- a/src/pages/resources/components/gpus.tsx +++ b/src/pages/resources/components/gpus.tsx @@ -13,7 +13,7 @@ import { GPU_DEVICES_API, queryGpuDevicesList } from '../apis'; import { GPUDeviceItem } from '../config/types'; import useGPUColumns from '../hooks/use-gpu-columns'; -const GPUList: React.FC<{ clusterId?: number; widths: { input: number } }> = ({ +const GPUList: React.FC<{ clusterId?: number; widths?: { input: number } }> = ({ clusterId, widths }) => { diff --git a/src/pages/resources/components/workers.tsx b/src/pages/resources/components/workers.tsx index 96f3b365..0d0d1b5e 100644 --- a/src/pages/resources/components/workers.tsx +++ b/src/pages/resources/components/workers.tsx @@ -27,7 +27,7 @@ import UpdateLabels from './update-labels'; import WorkerDetailModal from './worker-detail-modal'; const Workers: React.FC<{ - clusterId: string | number | null; + clusterId?: string | number | null; showSelect?: boolean; showAddButton?: boolean; widths?: { input: number };