chore: providers ux
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
import {
|
import {
|
||||||
ApiOutlined,
|
ApiOutlined,
|
||||||
|
CopyOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
DockerOutlined,
|
DockerOutlined,
|
||||||
DownloadOutlined,
|
DownloadOutlined,
|
||||||
@@ -62,7 +63,8 @@ const icons = {
|
|||||||
CaptivePortal: React.createElement(IconFont, { type: 'icon-captive_portal' }),
|
CaptivePortal: React.createElement(IconFont, { type: 'icon-captive_portal' }),
|
||||||
StarOutlined: React.createElement(StarOutlined),
|
StarOutlined: React.createElement(StarOutlined),
|
||||||
Charger: React.createElement(IconFont, { type: 'icon-charger' }),
|
Charger: React.createElement(IconFont, { type: 'icon-charger' }),
|
||||||
Disabled: React.createElement(IconFont, { type: 'icon-disabled' })
|
Disabled: React.createElement(IconFont, { type: 'icon-disabled' }),
|
||||||
|
CopyOutlined: React.createElement(CopyOutlined)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default icons;
|
export default icons;
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ export default {
|
|||||||
'noresult.provider.title': 'No Providers',
|
'noresult.provider.title': 'No Providers',
|
||||||
'noresult.provider.subTitle': 'No providers have been added yet.',
|
'noresult.provider.subTitle': 'No providers have been added yet.',
|
||||||
'noresult.provider.nofound': 'No matching providers found.',
|
'noresult.provider.nofound': 'No matching providers found.',
|
||||||
|
'noresult.providers.title': 'No Providers',
|
||||||
|
'noresult.providers.subTitle': 'No providers have been added yet.',
|
||||||
|
'noresult.providers.nofound': 'No matching providers found.',
|
||||||
'noresult.accesses.title': 'No Accesses',
|
'noresult.accesses.title': 'No Accesses',
|
||||||
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
||||||
'noresult.accesses.nofound': 'No matching accesses found.'
|
'noresult.accesses.nofound': 'No matching accesses found.'
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ export default {
|
|||||||
'providers.title': 'Providers',
|
'providers.title': 'Providers',
|
||||||
'providers.button.add': 'Add Provider',
|
'providers.button.add': 'Add Provider',
|
||||||
'providers.table.providerName': 'Provider',
|
'providers.table.providerName': 'Provider',
|
||||||
'providers.table.modelsCounts': 'Models Count'
|
'providers.table.modelsCounts': 'Models Count',
|
||||||
|
'providers.table.proxy': 'Proxy'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ export default {
|
|||||||
'noresult.provider.title': 'No Providers',
|
'noresult.provider.title': 'No Providers',
|
||||||
'noresult.provider.subTitle': 'No providers have been added yet.',
|
'noresult.provider.subTitle': 'No providers have been added yet.',
|
||||||
'noresult.provider.nofound': 'No matching providers found.',
|
'noresult.provider.nofound': 'No matching providers found.',
|
||||||
|
'noresult.providers.title': 'No Providers',
|
||||||
|
'noresult.providers.subTitle': 'No providers have been added yet.',
|
||||||
|
'noresult.providers.nofound': 'No matching providers found.',
|
||||||
'noresult.accesses.title': 'No Accesses',
|
'noresult.accesses.title': 'No Accesses',
|
||||||
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
||||||
'noresult.accesses.nofound': 'No matching accesses found.'
|
'noresult.accesses.nofound': 'No matching accesses found.'
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ export default {
|
|||||||
'providers.title': 'Providers',
|
'providers.title': 'Providers',
|
||||||
'providers.button.add': 'Add Provider',
|
'providers.button.add': 'Add Provider',
|
||||||
'providers.table.providerName': 'Provider',
|
'providers.table.providerName': 'Provider',
|
||||||
'providers.table.modelsCounts': 'Models Count'
|
'providers.table.modelsCounts': 'Models Count',
|
||||||
|
'providers.table.proxy': 'Proxy'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ export default {
|
|||||||
'noresult.provider.title': 'No Providers',
|
'noresult.provider.title': 'No Providers',
|
||||||
'noresult.provider.subTitle': 'No providers have been added yet.',
|
'noresult.provider.subTitle': 'No providers have been added yet.',
|
||||||
'noresult.provider.nofound': 'No matching providers found.',
|
'noresult.provider.nofound': 'No matching providers found.',
|
||||||
|
'noresult.providers.title': 'No Providers',
|
||||||
|
'noresult.providers.subTitle': 'No providers have been added yet.',
|
||||||
|
'noresult.providers.nofound': 'No matching providers found.',
|
||||||
'noresult.accesses.title': 'No Accesses',
|
'noresult.accesses.title': 'No Accesses',
|
||||||
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
||||||
'noresult.accesses.nofound': 'No matching accesses found.'
|
'noresult.accesses.nofound': 'No matching accesses found.'
|
||||||
@@ -63,6 +66,9 @@ export default {
|
|||||||
// 5. 'noresult.provider.title': 'No Providers',
|
// 5. 'noresult.provider.title': 'No Providers',
|
||||||
// 6. 'noresult.provider.subTitle': 'No providers have been added yet.',
|
// 6. 'noresult.provider.subTitle': 'No providers have been added yet.',
|
||||||
// 7. 'noresult.provider.nofound': 'No matching providers found.',
|
// 7. 'noresult.provider.nofound': 'No matching providers found.',
|
||||||
|
// 5. 'noresult.providers.title': 'No Providers',
|
||||||
|
// 6. 'noresult.providers.subTitle': 'No providers have been added yet.',
|
||||||
|
// 7. 'noresult.providers.nofound': 'No matching providers found.',
|
||||||
// 8. 'noresult.accesses.title': 'No Accesses',
|
// 8. 'noresult.accesses.title': 'No Accesses',
|
||||||
// 9. 'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
// 9. 'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
||||||
// 10. 'noresult.accesses.nofound': 'No matching accesses found.'
|
// 10. 'noresult.accesses.nofound': 'No matching accesses found.'
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ export default {
|
|||||||
'providers.title': 'Providers',
|
'providers.title': 'Providers',
|
||||||
'providers.button.add': 'Add Provider',
|
'providers.button.add': 'Add Provider',
|
||||||
'providers.table.providerName': 'Provider',
|
'providers.table.providerName': 'Provider',
|
||||||
'providers.table.modelsCounts': 'Models Count'
|
'providers.table.modelsCounts': 'Models Count',
|
||||||
|
'providers.table.proxy': 'Proxy'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ export default {
|
|||||||
'menu.models.userModels': '我的模型',
|
'menu.models.userModels': '我的模型',
|
||||||
'menu.models.benchmark': '基准测试',
|
'menu.models.benchmark': '基准测试',
|
||||||
'menu.models.benchmarkDetail': '基准测试详情',
|
'menu.models.benchmarkDetail': '基准测试详情',
|
||||||
'menu.models.provider': '渠道',
|
'menu.models.providers': '提供商',
|
||||||
'menu.models.providers': '渠道',
|
|
||||||
'menu.models.accesses': '接入',
|
'menu.models.accesses': '接入',
|
||||||
'menu.modelCatalog': '模型库',
|
'menu.modelCatalog': '模型库',
|
||||||
'menu.models.catalog': '模型库',
|
'menu.models.catalog': '模型库',
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ export default {
|
|||||||
'noresult.benchmark.title': '暂无基准测试',
|
'noresult.benchmark.title': '暂无基准测试',
|
||||||
'noresult.benchmark.subTitle': '尚未添加任何基准测试。',
|
'noresult.benchmark.subTitle': '尚未添加任何基准测试。',
|
||||||
'noresult.benchmark.nofound': '未找到匹配的基准测试',
|
'noresult.benchmark.nofound': '未找到匹配的基准测试',
|
||||||
'noresult.provider.title': '暂无渠道',
|
'noresult.providers.title': '暂无提供商',
|
||||||
'noresult.provider.subTitle': '尚未添加任何渠道。',
|
'noresult.providers.subTitle': '尚未添加任何提供商。',
|
||||||
'noresult.provider.nofound': '未找到匹配的渠道',
|
'noresult.providers.nofound': '未找到匹配的提供商',
|
||||||
'noresult.accesses.title': '暂无接入',
|
'noresult.accesses.title': '暂无接入',
|
||||||
'noresult.accesses.subTitle': '尚未添加任何接入。',
|
'noresult.accesses.subTitle': '尚未添加任何接入。',
|
||||||
'noresult.accesses.nofound': '未找到匹配的接入'
|
'noresult.accesses.nofound': '未找到匹配的接入'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
'providers.title': '渠道',
|
'providers.title': '提供商',
|
||||||
'providers.button.add': '添加渠道',
|
'providers.button.add': '添加提供商',
|
||||||
'providers.table.providerName': '渠道',
|
'providers.table.providerName': '提供商',
|
||||||
'providers.table.modelsCounts': '模型数量'
|
'providers.table.modelsCounts': '模型数量',
|
||||||
|
'providers.table.proxy': '代理'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
WechatWorkOutlined
|
WechatWorkOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Tag } from 'antd';
|
import { Tag } from 'antd';
|
||||||
import { modelCategoriesMap } from '../config';
|
import { modelCategoriesMap } from '../llmodels/config';
|
||||||
|
|
||||||
export const categoryConfig = {
|
export const categoryConfig = {
|
||||||
[modelCategoriesMap.reranker]: {
|
[modelCategoriesMap.reranker]: {
|
||||||
@@ -53,7 +53,7 @@ const ModelTag: React.FC<ModelTagProps> = ({ categoryKey, size }) => {
|
|||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
icon={config.icon}
|
icon={config.icon}
|
||||||
variant="outlined"
|
variant={'outlined'}
|
||||||
style={{
|
style={{
|
||||||
height: size,
|
height: size,
|
||||||
margin: 0,
|
margin: 0,
|
||||||
@@ -7,10 +7,10 @@ import { Typography } from 'antd';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import { categoryConfig } from '../../_components/model-tag';
|
||||||
import { modelCategories } from '../config';
|
import { modelCategories } from '../config';
|
||||||
import { CatalogItem as CatalogItemType } from '../config/types';
|
import { CatalogItem as CatalogItemType } from '../config/types';
|
||||||
import '../style/catalog-item.less';
|
import '../style/catalog-item.less';
|
||||||
import { categoryConfig } from './model-tag';
|
|
||||||
|
|
||||||
interface CatalogItemProps {
|
interface CatalogItemProps {
|
||||||
activeId: number;
|
activeId: number;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Button } from 'antd';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { categoryConfig } from '../../_components/model-tag';
|
||||||
import {
|
import {
|
||||||
modelCategories,
|
modelCategories,
|
||||||
modelCategoriesMap,
|
modelCategoriesMap,
|
||||||
@@ -17,7 +18,6 @@ import {
|
|||||||
MyModelsStatusValueMap
|
MyModelsStatusValueMap
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { categoryToPathMap } from '../config/button-actions';
|
import { categoryToPathMap } from '../config/button-actions';
|
||||||
import { categoryConfig } from './model-tag';
|
|
||||||
|
|
||||||
const CardWrapper = styled.div`
|
const CardWrapper = styled.div`
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { Tooltip } from 'antd';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import ModelTag from '../components/model-tag';
|
import ModelTag from '../../_components/model-tag';
|
||||||
import { ActionList, generateSource } from '../config/button-actions';
|
import { ActionList, generateSource } from '../config/button-actions';
|
||||||
import { ListItem } from '../config/types';
|
import { ListItem } from '../config/types';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { categoryOptions, modelCategoriesMap } from '@/pages/llmodels/config';
|
import { modelCategoriesMap } from '@/pages/llmodels/config';
|
||||||
import {
|
import {
|
||||||
CheckCircleOutlined,
|
CheckCircleOutlined,
|
||||||
CloseCircleOutlined,
|
CloseCircleOutlined,
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Flex, Tag } from 'antd';
|
import { Flex, Tag } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { categoryConfig } from '../../_components/model-tag';
|
||||||
|
|
||||||
interface ProviderModelProps {
|
interface ProviderModelProps {
|
||||||
dataList: any[];
|
dataList: any[];
|
||||||
@@ -61,8 +62,10 @@ const ProviderModels: React.FC<ProviderModelProps> = () => {
|
|||||||
: 'gray'
|
: 'gray'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{model.name} (
|
{model.name}
|
||||||
{categoryOptions.find((item) => item.value === model.type)?.label})
|
<span style={{ marginLeft: 8 }}>
|
||||||
|
{categoryConfig[model.type]?.icon}
|
||||||
|
</span>
|
||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
interface FormContextProps {
|
||||||
|
providerType?: string;
|
||||||
|
action?: PageActionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormContext = createContext<FormContextProps>({});
|
||||||
|
|
||||||
|
export const useFormContext = () => {
|
||||||
|
const context = useContext(FormContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useFormContext must be used within a FormProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormContext;
|
||||||
@@ -44,8 +44,8 @@ export const maasProviderOptions = Object.entries(maasProviderValueMap).map(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const ProviderStatusValueMap: Record<string, string> = {
|
export const ProviderStatusValueMap: Record<string, string> = {
|
||||||
Ready: 'Ready',
|
Ready: 'ready',
|
||||||
InActive: 'Inactive'
|
InActive: 'inactive'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProviderStatusLabelMap = {
|
export const ProviderStatusLabelMap = {
|
||||||
@@ -65,6 +65,11 @@ export const rowActionList = [
|
|||||||
label: 'common.button.edit',
|
label: 'common.button.edit',
|
||||||
icon: icons.EditOutlined
|
icon: icons.EditOutlined
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'copy',
|
||||||
|
label: 'common.button.copy',
|
||||||
|
icon: icons.CopyOutlined
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
label: 'common.button.delete',
|
label: 'common.button.delete',
|
||||||
|
|||||||
@@ -9,23 +9,26 @@ export const mockDataList: MaasProviderItem[] = [
|
|||||||
models: 5,
|
models: 5,
|
||||||
state_message: '',
|
state_message: '',
|
||||||
provider: 'doubao',
|
provider: 'doubao',
|
||||||
|
proxy_config: {},
|
||||||
builtIn: false
|
builtIn: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
models: 3,
|
models: 3,
|
||||||
name: 'Qwen',
|
name: 'Qwen',
|
||||||
state: 'Inactive',
|
state: 'inactive',
|
||||||
state_message: 'Provider is inactive',
|
state_message: 'Provider is inactive',
|
||||||
provider: 'qwen',
|
provider: 'qwen',
|
||||||
|
proxy_config: false,
|
||||||
builtIn: true
|
builtIn: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
models: 8,
|
models: 8,
|
||||||
name: 'OpenAI',
|
name: 'OpenAI',
|
||||||
state: 'Ready',
|
state: 'ready',
|
||||||
state_message: '',
|
state_message: '',
|
||||||
|
proxy_config: null,
|
||||||
provider: 'openai',
|
provider: 'openai',
|
||||||
builtIn: false
|
builtIn: false
|
||||||
},
|
},
|
||||||
@@ -33,18 +36,20 @@ export const mockDataList: MaasProviderItem[] = [
|
|||||||
id: 4,
|
id: 4,
|
||||||
models: 2,
|
models: 2,
|
||||||
name: 'Deepseek',
|
name: 'Deepseek',
|
||||||
state: 'Ready',
|
state: 'ready',
|
||||||
state_message: '',
|
state_message: '',
|
||||||
provider: 'deepseek',
|
provider: 'deepseek',
|
||||||
|
proxy_config: null,
|
||||||
builtIn: false
|
builtIn: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
models: 4,
|
models: 4,
|
||||||
name: 'Anthropic',
|
name: 'Anthropic',
|
||||||
state: 'Inactive',
|
state: 'inactive',
|
||||||
state_message: 'Provider is inactive',
|
state_message: 'Provider is inactive',
|
||||||
provider: 'anthropic',
|
provider: 'anthropic',
|
||||||
|
proxy_config: {},
|
||||||
builtIn: true
|
builtIn: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export interface MaasProviderItem {
|
|||||||
state: string;
|
state: string;
|
||||||
state_message: string;
|
state_message: string;
|
||||||
provider: maasProviderType;
|
provider: maasProviderType;
|
||||||
|
proxy_config?: any;
|
||||||
builtIn: boolean;
|
builtIn: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ import { useIntl } from '@umijs/max';
|
|||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||||
import { maasProviderType } from '../config';
|
import { maasProviderType } from '../config';
|
||||||
|
import ProxyConfig from './proxy_config';
|
||||||
|
|
||||||
const AdvanceConfig: React.FC<{
|
const AdvanceConfig: React.FC<{
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
provider?: maasProviderType;
|
provider?: maasProviderType;
|
||||||
ref?: any;
|
ref?: any;
|
||||||
}> = forwardRef(({ action, provider }, ref) => {
|
}> = forwardRef(({ action, provider }, ref) => {
|
||||||
const [form] = Form.useForm();
|
const form = Form.useFormInstance();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const editorRef = React.useRef<any>(null);
|
const editorRef = React.useRef<any>(null);
|
||||||
const [fileContent, setFileContent] = React.useState<string>('');
|
const [fileContent, setFileContent] = React.useState<string>('');
|
||||||
@@ -28,7 +29,8 @@ const AdvanceConfig: React.FC<{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div data-field="customConfig"></div>
|
<div data-field="advanceConfig"></div>
|
||||||
|
<ProxyConfig></ProxyConfig>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
hidden
|
hidden
|
||||||
name="custom_config"
|
name="custom_config"
|
||||||
@@ -43,14 +45,13 @@ const AdvanceConfig: React.FC<{
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<YamlEditor
|
<YamlEditor
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
title={'Yaml'}
|
title={'Custom Config (YAML)'}
|
||||||
value={fileContent}
|
value={fileContent}
|
||||||
height={300}
|
height={300}
|
||||||
onUpload={(content) => {
|
onUpload={(content) => {
|
||||||
setFileContent(content);
|
setFileContent(content);
|
||||||
}}
|
}}
|
||||||
></YamlEditor>
|
></YamlEditor>
|
||||||
<div className="scroller-to-holder" style={{ height: 1 }}></div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
|
import { PageAction } from '@/config';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import CollapsePanel from '@/pages/_components/collapse-panel';
|
import CollapsePanel from '@/pages/_components/collapse-panel';
|
||||||
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
|
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
|
||||||
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
|
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
|
import {
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
import { maasProviderType } from '../config';
|
import { maasProviderType } from '../config';
|
||||||
|
import FormContext from '../config/form-context';
|
||||||
import { MaasProviderItem as ListItem } from '../config/types';
|
import { MaasProviderItem as ListItem } from '../config/types';
|
||||||
import AccessToken from './access-token';
|
import AccessToken from './access-token';
|
||||||
import AdvanceConfig from './advance-config';
|
import AdvanceConfig from './advance-config';
|
||||||
@@ -31,9 +39,9 @@ const TABKeysMap = {
|
|||||||
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||||
const { action, provider, currentData, onFinish } = props;
|
const { action, provider, currentData, onFinish } = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const [form] = Form.useForm();
|
||||||
const { getScrollElementScrollableHeight } = useWrapperContext();
|
const { getScrollElementScrollableHeight } = useWrapperContext();
|
||||||
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.BASIC]);
|
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.BASIC]);
|
||||||
const [form] = Form.useForm();
|
|
||||||
const scrollTabsRef = useRef<any>(null);
|
const scrollTabsRef = useRef<any>(null);
|
||||||
|
|
||||||
const segmentOptions = [
|
const segmentOptions = [
|
||||||
@@ -50,17 +58,11 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
|||||||
field: 'supportedModels'
|
field: 'supportedModels'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: TABKeysMap.CUSTOMCONFIG,
|
value: TABKeysMap.ADVANCED,
|
||||||
label: 'Custom Config',
|
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
||||||
icon: <IconFont type="icon-settings" />,
|
icon: <IconFont type="icon-settings" />,
|
||||||
field: 'customConfig'
|
field: 'advanceConfig'
|
||||||
}
|
}
|
||||||
// {
|
|
||||||
// value: TABKeysMap.ADVANCED,
|
|
||||||
// label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
|
||||||
// icon: <IconFont type="icon-settings" />,
|
|
||||||
// field: 'categories'
|
|
||||||
// }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleActiveChange = (key: string[]) => {
|
const handleActiveChange = (key: string[]) => {
|
||||||
@@ -80,6 +82,15 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (action === PageAction.EDIT && currentData) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
...currentData,
|
||||||
|
models: currentData.models || []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [form, currentData, action]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollSpyTabs
|
<ScrollSpyTabs
|
||||||
ref={scrollTabsRef}
|
ref={scrollTabsRef}
|
||||||
@@ -93,34 +104,46 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
|||||||
}}
|
}}
|
||||||
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
|
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
|
||||||
>
|
>
|
||||||
<Form form={form}>
|
<FormContext.Provider value={{ providerType: provider, action }}>
|
||||||
<Basic />
|
<Form
|
||||||
<AccessToken />
|
form={form}
|
||||||
<CollapsePanel
|
onFinish={onFinish}
|
||||||
activeKey={activeKey}
|
initialValues={{
|
||||||
accordion={false}
|
proxy_config: {
|
||||||
onChange={handleOnCollapseChange}
|
enabled: false,
|
||||||
items={[
|
url: '',
|
||||||
{
|
timeout: 30
|
||||||
key: TABKeysMap.SUPPORTEDMODELS,
|
|
||||||
label: 'Supported Models',
|
|
||||||
forceRender: true,
|
|
||||||
children: <SupportedModels></SupportedModels>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: TABKeysMap.CUSTOMCONFIG,
|
|
||||||
label: 'Custom Config',
|
|
||||||
forceRender: true,
|
|
||||||
children: (
|
|
||||||
<AdvanceConfig
|
|
||||||
action={action}
|
|
||||||
provider={provider}
|
|
||||||
></AdvanceConfig>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
]}
|
}}
|
||||||
></CollapsePanel>
|
>
|
||||||
</Form>
|
<Basic />
|
||||||
|
<AccessToken />
|
||||||
|
<CollapsePanel
|
||||||
|
activeKey={activeKey}
|
||||||
|
accordion={false}
|
||||||
|
onChange={handleOnCollapseChange}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: TABKeysMap.SUPPORTEDMODELS,
|
||||||
|
label: 'Supported Models',
|
||||||
|
forceRender: true,
|
||||||
|
children: <SupportedModels></SupportedModels>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: TABKeysMap.ADVANCED,
|
||||||
|
label: 'Advanced',
|
||||||
|
forceRender: true,
|
||||||
|
children: (
|
||||||
|
<AdvanceConfig
|
||||||
|
action={action}
|
||||||
|
provider={provider}
|
||||||
|
></AdvanceConfig>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
></CollapsePanel>
|
||||||
|
</Form>
|
||||||
|
</FormContext.Provider>
|
||||||
</ScrollSpyTabs>
|
</ScrollSpyTabs>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import CheckboxField from '@/components/seal-form/checkbox-field';
|
||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { useFormContext } from '../config/form-context';
|
||||||
|
|
||||||
|
const AdvanceConfig = () => {
|
||||||
|
const form = Form.useFormInstance();
|
||||||
|
const intl = useIntl();
|
||||||
|
const { action } = useFormContext();
|
||||||
|
const proxyConfigRef = React.useRef<any>({});
|
||||||
|
const proxyConfigEnabled = Form.useWatch(['proxy_config', 'enabled'], form);
|
||||||
|
|
||||||
|
const handleSpeculativeEnabledChange = (e: any) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
form.setFieldValue('speculative_config', {
|
||||||
|
enabled: true,
|
||||||
|
url: proxyConfigRef.current?.url || '',
|
||||||
|
timeout: proxyConfigRef.current?.timeout || 30
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
proxyConfigRef.current = form.getFieldValue('proxy_config');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
name={['proxy_config', 'enabled']}
|
||||||
|
valuePropName="checked"
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
>
|
||||||
|
<CheckboxField
|
||||||
|
label={'Enable Proxy'}
|
||||||
|
onChange={handleSpeculativeEnabledChange}
|
||||||
|
></CheckboxField>
|
||||||
|
</Form.Item>
|
||||||
|
{proxyConfigEnabled && (
|
||||||
|
<>
|
||||||
|
<Form.Item name={['proxy_config', 'url']}>
|
||||||
|
<SealInput.Input
|
||||||
|
label={'Proxy URL'}
|
||||||
|
placeholder="http://proxy.example.com:8080"
|
||||||
|
></SealInput.Input>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name={['proxy_config', 'timeout']}>
|
||||||
|
<SealInput.Number
|
||||||
|
label={'Proxy Timeout'}
|
||||||
|
placeholder="30"
|
||||||
|
></SealInput.Number>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AdvanceConfig;
|
||||||
@@ -22,6 +22,13 @@ const SupportedModels = () => {
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const form = Form.useFormInstance<FormData>();
|
const form = Form.useFormInstance<FormData>();
|
||||||
|
|
||||||
|
const handleTestModel = (data: any) => {
|
||||||
|
data.loading = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
data.loading = false;
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
const renderModelItem = (
|
const renderModelItem = (
|
||||||
data: any,
|
data: any,
|
||||||
{
|
{
|
||||||
@@ -32,6 +39,11 @@ const SupportedModels = () => {
|
|||||||
return (
|
return (
|
||||||
<SelectWrapper>
|
<SelectWrapper>
|
||||||
<SealSelect
|
<SealSelect
|
||||||
|
suffixIcon={
|
||||||
|
<CheckCircleFilled
|
||||||
|
style={{ color: 'var(--ant-color-success)', fontSize: 16 }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
alwaysFocus={true}
|
alwaysFocus={true}
|
||||||
value={data.value}
|
value={data.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
@@ -42,12 +54,12 @@ const SupportedModels = () => {
|
|||||||
{ label: 'Model C', value: 'model_c' }
|
{ label: 'Model C', value: 'model_c' }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<span className="icon-wrapper">
|
<Button
|
||||||
<CheckCircleFilled
|
type="link"
|
||||||
style={{ color: 'var(--ant-color-success)', fontSize: 16 }}
|
size="small"
|
||||||
/>
|
loading={data.loading}
|
||||||
</span>
|
onClick={() => handleTestModel(data)}
|
||||||
<Button type="link" size="small">
|
>
|
||||||
Test
|
Test
|
||||||
</Button>
|
</Button>
|
||||||
</SelectWrapper>
|
</SelectWrapper>
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ const useCreateProvider = (options: { refresh: () => void }) => {
|
|||||||
provider: null
|
provider: null
|
||||||
});
|
});
|
||||||
|
|
||||||
const openModal = (action: PageActionType, row?: ListItem) => {
|
const openModal = (action: PageActionType, title: string, row?: ListItem) => {
|
||||||
setOpenModalStatus({
|
setOpenModalStatus({
|
||||||
...openModalStatus,
|
...openModalStatus,
|
||||||
open: true,
|
open: true,
|
||||||
title: action === PageAction.CREATE ? 'Create Provider' : 'Edit Provider',
|
title: title,
|
||||||
action,
|
action,
|
||||||
currentData: row,
|
currentData: row,
|
||||||
provider: row ? row.provider : null
|
provider: row ? row.provider : null
|
||||||
@@ -31,8 +31,13 @@ const useCreateProvider = (options: { refresh: () => void }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
setOpenModalStatus({ ...openModalStatus, open: false });
|
setOpenModalStatus({
|
||||||
options.refresh();
|
open: false,
|
||||||
|
action: PageAction.CREATE,
|
||||||
|
currentData: undefined,
|
||||||
|
title: '',
|
||||||
|
provider: null
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
maasProviderLabelMap,
|
maasProviderLabelMap,
|
||||||
providerIconsMap,
|
providerIconsMap,
|
||||||
ProviderStatus,
|
ProviderStatus,
|
||||||
ProviderStatusLabelMap,
|
|
||||||
rowActionList
|
rowActionList
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { MaasProviderItem } from '../config/types';
|
import { MaasProviderItem } from '../config/types';
|
||||||
@@ -69,15 +68,21 @@ const useProviderColumns = (
|
|||||||
render: (value: number) => <span>{value}</span>
|
render: (value: number) => <span>{value}</span>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: 'common.table.status' }),
|
title: intl.formatMessage({ id: 'providers.table.proxy' }),
|
||||||
dataIndex: 'state',
|
dataIndex: 'proxy_config',
|
||||||
span: 4,
|
span: 3,
|
||||||
|
render: (value: number) => <span>{value ? 'Configured' : 'None'}</span>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Token Settings',
|
||||||
|
dataIndex: 'tokens',
|
||||||
|
span: 3,
|
||||||
render: (value: string, record: MaasProviderItem) => (
|
render: (value: string, record: MaasProviderItem) => (
|
||||||
<StatusTag
|
<StatusTag
|
||||||
statusValue={{
|
statusValue={{
|
||||||
status: ProviderStatus[value],
|
status: ProviderStatus['ready'],
|
||||||
text: ProviderStatusLabelMap[value],
|
text: 'Configured',
|
||||||
message: record.state_message || undefined
|
message: ''
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -86,7 +91,7 @@ const useProviderColumns = (
|
|||||||
title: intl.formatMessage({ id: 'common.table.createTime' }),
|
title: intl.formatMessage({ id: 'common.table.createTime' }),
|
||||||
dataIndex: 'created_at',
|
dataIndex: 'created_at',
|
||||||
sorter: tableSorter(6),
|
sorter: tableSorter(6),
|
||||||
span: 4,
|
span: 3,
|
||||||
render: (value: string) => (
|
render: (value: string) => (
|
||||||
<AutoTooltip ghost minWidth={20}>
|
<AutoTooltip ghost minWidth={20}>
|
||||||
{dayjs(value).format('YYYY-MM-DD HH:mm:ss')}
|
{dayjs(value).format('YYYY-MM-DD HH:mm:ss')}
|
||||||
|
|||||||
@@ -62,17 +62,16 @@ const MaasProvider: React.FC = () => {
|
|||||||
const { handleExpandChange, handleExpandAll, expandedRowKeys } =
|
const { handleExpandChange, handleExpandAll, expandedRowKeys } =
|
||||||
useExpandedRowKeys(expandAtom);
|
useExpandedRowKeys(expandAtom);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const {
|
const { openProviderModalStatus, openProviderModal, closeProviderModal } =
|
||||||
openProviderModalStatus,
|
useCreateProvider({
|
||||||
setOpenProviderModalStatus,
|
refresh: handleSearch
|
||||||
openProviderModal,
|
});
|
||||||
closeProviderModal
|
|
||||||
} = useCreateProvider({
|
|
||||||
refresh: handleSearch
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleClickDropdown = () => {
|
const handleClickDropdown = () => {
|
||||||
openProviderModal('create');
|
openProviderModal(
|
||||||
|
'create',
|
||||||
|
intl.formatMessage({ id: 'providers.button.add' })
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalOk = async (data: FormData) => {
|
const handleModalOk = async (data: FormData) => {
|
||||||
@@ -87,39 +86,25 @@ const MaasProvider: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
fetchData();
|
fetchData();
|
||||||
setOpenProviderModalStatus({
|
closeProviderModal();
|
||||||
open: false,
|
|
||||||
action: PageAction.CREATE,
|
|
||||||
currentData: undefined,
|
|
||||||
title: '',
|
|
||||||
provider: null
|
|
||||||
});
|
|
||||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalCancel = () => {
|
const handleModalCancel = () => {
|
||||||
console.log('handleModalCancel');
|
console.log('handleModalCancel');
|
||||||
setOpenProviderModalStatus({
|
closeProviderModal();
|
||||||
open: false,
|
|
||||||
action: PageAction.CREATE,
|
|
||||||
currentData: undefined,
|
|
||||||
title: '',
|
|
||||||
provider: null
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditProvider = (row: ListItem) => {
|
const handleEditProvider = (row: ListItem) => {
|
||||||
setOpenProviderModalStatus({
|
openProviderModal(
|
||||||
open: true,
|
PageAction.EDIT,
|
||||||
action: PageAction.EDIT,
|
intl.formatMessage(
|
||||||
currentData: row,
|
|
||||||
title: intl.formatMessage(
|
|
||||||
{ id: 'clusters.edit.cluster' },
|
{ id: 'clusters.edit.cluster' },
|
||||||
{ cluster: row.name }
|
{ cluster: row.name }
|
||||||
),
|
),
|
||||||
provider: row.provider
|
row
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelect = useMemoizedFn((val: any, row: ListItem) => {
|
const handleSelect = useMemoizedFn((val: any, row: ListItem) => {
|
||||||
@@ -128,6 +113,9 @@ const MaasProvider: React.FC = () => {
|
|||||||
} else if (val === 'delete') {
|
} else if (val === 'delete') {
|
||||||
handleDelete({ ...row, name: row.name });
|
handleDelete({ ...row, name: row.name });
|
||||||
}
|
}
|
||||||
|
if (val === 'copy') {
|
||||||
|
openProviderModal(PageAction.EDIT, 'Copy Provider', row);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleOnToggleExpandAll = () => {
|
const handleOnToggleExpandAll = () => {
|
||||||
@@ -226,11 +214,11 @@ const MaasProvider: React.FC = () => {
|
|||||||
image={<IconFont type="icon-extension-outline" />}
|
image={<IconFont type="icon-extension-outline" />}
|
||||||
filters={_.omit(queryParams, ['sort_by'])}
|
filters={_.omit(queryParams, ['sort_by'])}
|
||||||
noFoundText={intl.formatMessage({
|
noFoundText={intl.formatMessage({
|
||||||
id: 'noresult.provider.nofound'
|
id: 'noresult.providers.nofound'
|
||||||
})}
|
})}
|
||||||
title={intl.formatMessage({ id: 'noresult.provider.title' })}
|
title={intl.formatMessage({ id: 'noresult.providers.title' })}
|
||||||
subTitle={intl.formatMessage({
|
subTitle={intl.formatMessage({
|
||||||
id: 'noresult.provider.subTitle'
|
id: 'noresult.providers.subTitle'
|
||||||
})}
|
})}
|
||||||
onClick={handleClickDropdown}
|
onClick={handleClickDropdown}
|
||||||
buttonText={intl.formatMessage({ id: 'noresult.button.add' })}
|
buttonText={intl.formatMessage({ id: 'noresult.button.add' })}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
|
import { categoryOptions } from '@/pages/llmodels/config';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
|
const Basic = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const form = Form.useFormInstance<FormData>();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item name="name" data-field="name">
|
||||||
|
<SealInput.Input required label={'Model Name'} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="category">
|
||||||
|
<SealSelect options={categoryOptions} label="Category"></SealSelect>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="description">
|
||||||
|
<SealInput.TextArea
|
||||||
|
scaleSize={true}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'common.table.description'
|
||||||
|
})}
|
||||||
|
></SealInput.TextArea>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Basic;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
|
const Endpoints = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const form = Form.useFormInstance<FormData>();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item name="name" data-field="endpoints">
|
||||||
|
<SealInput.Input
|
||||||
|
required
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'common.table.name'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="provider" hidden>
|
||||||
|
<SealInput.Input
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'providers.table.providerName'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="description">
|
||||||
|
<SealInput.TextArea
|
||||||
|
scaleSize={true}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'common.table.description'
|
||||||
|
})}
|
||||||
|
></SealInput.TextArea>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Endpoints;
|
||||||
@@ -1,17 +1,68 @@
|
|||||||
|
import IconFont from '@/components/icon-font';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
|
import CollapsePanel from '@/pages/_components/collapse-panel';
|
||||||
|
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
|
||||||
|
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import { forwardRef, useImperativeHandle } from 'react';
|
import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
|
||||||
import { MaasProviderItem as ListItem } from '../config/types';
|
import { maasProviderType } from '../config';
|
||||||
|
import { AccessItem as ListItem } from '../config/types';
|
||||||
|
import Basic from './basic';
|
||||||
|
import Endpoints from './endpoints';
|
||||||
|
import MetaData from './meta-data';
|
||||||
|
|
||||||
interface ProviderFormProps {
|
interface ProviderFormProps {
|
||||||
ref?: any;
|
ref?: any;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
|
provider?: maasProviderType;
|
||||||
currentData?: ListItem; // Used when action is EDIT
|
currentData?: ListItem; // Used when action is EDIT
|
||||||
onFinish: (values: FormData) => void;
|
onFinish: (values: FormData) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TABKeysMap = {
|
||||||
|
BASIC: 'basic',
|
||||||
|
METADATA: 'metadata',
|
||||||
|
ENDPOINTS: 'endpoints',
|
||||||
|
ADVANCED: 'advanced'
|
||||||
|
};
|
||||||
|
|
||||||
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||||
|
const { action, provider, currentData, onFinish } = props;
|
||||||
|
const intl = useIntl();
|
||||||
|
const { getScrollElementScrollableHeight } = useWrapperContext();
|
||||||
|
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.BASIC]);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const scrollTabsRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const segmentOptions = [
|
||||||
|
{
|
||||||
|
value: TABKeysMap.BASIC,
|
||||||
|
label: 'Basic',
|
||||||
|
icon: <IconFont type="icon-basic" />,
|
||||||
|
field: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TABKeysMap.METADATA,
|
||||||
|
label: 'Metadata',
|
||||||
|
icon: <IconFont type="icon-speed" />,
|
||||||
|
field: 'metadata'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: TABKeysMap.ENDPOINTS,
|
||||||
|
label: 'Endpoints',
|
||||||
|
icon: <IconFont type="icon-settings" />,
|
||||||
|
field: 'endpoints'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleActiveChange = (key: string[]) => {
|
||||||
|
setActiveKey(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnCollapseChange = (keys: string | string[]) => {
|
||||||
|
setActiveKey(Array.isArray(keys) ? keys : [keys]);
|
||||||
|
};
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
submit: () => {
|
submit: () => {
|
||||||
@@ -23,9 +74,43 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form form={form} layout="vertical">
|
<ScrollSpyTabs
|
||||||
{/* Form fields go here */}
|
ref={scrollTabsRef}
|
||||||
</Form>
|
defaultTarget="basic"
|
||||||
|
segmentOptions={segmentOptions}
|
||||||
|
activeKey={activeKey}
|
||||||
|
setActiveKey={handleActiveChange}
|
||||||
|
segmentedTop={{
|
||||||
|
top: 0,
|
||||||
|
offsetTop: 96
|
||||||
|
}}
|
||||||
|
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
|
||||||
|
>
|
||||||
|
<Form form={form}>
|
||||||
|
<Basic />
|
||||||
|
<CollapsePanel
|
||||||
|
activeKey={activeKey}
|
||||||
|
accordion={false}
|
||||||
|
onChange={handleOnCollapseChange}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: TABKeysMap.METADATA,
|
||||||
|
label: 'Metadata',
|
||||||
|
forceRender: true,
|
||||||
|
children: <MetaData></MetaData>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: TABKeysMap.ENDPOINTS,
|
||||||
|
label: 'Endpoints',
|
||||||
|
forceRender: true,
|
||||||
|
children: (
|
||||||
|
<Endpoints action={action} provider={provider}></Endpoints>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
></CollapsePanel>
|
||||||
|
</Form>
|
||||||
|
</ScrollSpyTabs>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
|
const MetaData = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const form = Form.useFormInstance<FormData>();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item name="name" data-field="metadata">
|
||||||
|
<SealInput.Input
|
||||||
|
required
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'common.table.name'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="provider" hidden>
|
||||||
|
<SealInput.Input
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'providers.table.providerName'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="description">
|
||||||
|
<SealInput.TextArea
|
||||||
|
scaleSize={true}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'common.table.description'
|
||||||
|
})}
|
||||||
|
></SealInput.TextArea>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MetaData;
|
||||||
@@ -19,8 +19,14 @@ const useCreateAccess = (options: { refresh: () => void }) => {
|
|||||||
provider: null
|
provider: null
|
||||||
});
|
});
|
||||||
|
|
||||||
const openModal = () => {
|
const openModal = (action: PageActionType, currentData?: ListItem) => {
|
||||||
setOpenModalStatus({ ...openModalStatus, open: true });
|
setOpenModalStatus({
|
||||||
|
...openModalStatus,
|
||||||
|
open: true,
|
||||||
|
action,
|
||||||
|
currentData,
|
||||||
|
title: action === PageAction.CREATE ? 'Create Access' : 'Edit Access'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const Accesses: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleClickDropdown = () => {
|
const handleClickDropdown = () => {
|
||||||
openAccessModal();
|
openAccessModal('create');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalOk = async (data: FormData) => {
|
const handleModalOk = async (data: FormData) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user