refactor: create cluster by steps
This commit is contained in:
@@ -1,19 +1,12 @@
|
||||
import ModalFooter from '@/components/modal-footer';
|
||||
import ScrollerModal from '@/components/scroller-modal/index';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import ContainerInstall from '@/pages/resources/components/container-install';
|
||||
import { CheckCircleFilled } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { ProviderValueMap } from '../config';
|
||||
import React, { useRef } from 'react';
|
||||
import {
|
||||
ClusterFormData as FormData,
|
||||
ClusterListItem as ListItem
|
||||
} from '../config/types';
|
||||
import CloudProvider from './cloud-provider-form';
|
||||
import RegisterClusterInner from './register-cluster-inner';
|
||||
import ClusterForm from './cluster-form';
|
||||
|
||||
type AddModalProps = {
|
||||
title: string;
|
||||
@@ -35,15 +28,10 @@ const AddCluster: React.FC<AddModalProps> = ({
|
||||
onOk,
|
||||
onCancel
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
const [submissionStatus, setSubmissionStatus] = React.useState<{
|
||||
success: boolean;
|
||||
data: ListItem;
|
||||
}>({ success: false, data: {} as ListItem });
|
||||
const form = useRef<any>(null);
|
||||
|
||||
const handleSubmit = () => {
|
||||
form.submit();
|
||||
form.current?.submit();
|
||||
};
|
||||
|
||||
const handleOk = async (data: FormData) => {
|
||||
@@ -54,63 +42,13 @@ const AddCluster: React.FC<AddModalProps> = ({
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
form.resetFields();
|
||||
form.current?.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
const renderAddWorkerContent = () => {
|
||||
if (provider === ProviderValueMap.Custom) {
|
||||
return <ContainerInstall />;
|
||||
}
|
||||
if (provider === ProviderValueMap.Kubernetes) {
|
||||
return <RegisterClusterInner></RegisterClusterInner>;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const modalTitle = useMemo(() => {
|
||||
if (submissionStatus.success) {
|
||||
return (
|
||||
<div className="flex-center">
|
||||
<CheckCircleFilled
|
||||
className="text-success font-size-20"
|
||||
style={{ marginRight: '8px' }}
|
||||
/>
|
||||
<span>Cluster added! Now you can register a worker.</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return title;
|
||||
}, [submissionStatus.success, title]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentData) {
|
||||
form.setFieldsValue(currentData);
|
||||
}
|
||||
}, [currentData]);
|
||||
|
||||
const renderFooter = () => {
|
||||
if (submissionStatus.success) {
|
||||
return (
|
||||
<ModalFooter
|
||||
onOk={onCancel}
|
||||
onCancel={onCancel}
|
||||
showCancelBtn={false}
|
||||
okText="Skip for Now"
|
||||
okBtnProps={{
|
||||
style: {
|
||||
width: 'auto'
|
||||
}
|
||||
}}
|
||||
></ModalFooter>
|
||||
);
|
||||
}
|
||||
return <ModalFooter onOk={handleSubmit} onCancel={onCancel}></ModalFooter>;
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollerModal
|
||||
title={modalTitle}
|
||||
title={title}
|
||||
open={open}
|
||||
onCancel={handleCancel}
|
||||
destroyOnHidden={true}
|
||||
@@ -118,49 +56,18 @@ const AddCluster: React.FC<AddModalProps> = ({
|
||||
maskClosable={false}
|
||||
keyboard={false}
|
||||
width={680}
|
||||
footer={renderFooter()}
|
||||
footer={
|
||||
<ModalFooter onOk={handleSubmit} onCancel={onCancel}></ModalFooter>
|
||||
}
|
||||
>
|
||||
{submissionStatus.success ? (
|
||||
renderAddWorkerContent()
|
||||
) : (
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleOk}
|
||||
preserve={false}
|
||||
initialValues={currentData}
|
||||
>
|
||||
<Form.Item<FormData>
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{ id: 'common.form.rule.input' },
|
||||
{
|
||||
name: intl.formatMessage({ id: 'common.table.name' })
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label={intl.formatMessage({ id: 'common.table.name' })}
|
||||
required
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
{provider === ProviderValueMap.DigitalOcean && (
|
||||
<CloudProvider
|
||||
provider={provider}
|
||||
credentialList={credentialList}
|
||||
></CloudProvider>
|
||||
)}
|
||||
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
||||
<SealInput.TextArea
|
||||
label={intl.formatMessage({ id: 'common.table.description' })}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)}
|
||||
<ClusterForm
|
||||
ref={form}
|
||||
provider={provider}
|
||||
credentialList={credentialList}
|
||||
action={action}
|
||||
currentData={currentData}
|
||||
onFinish={handleOk}
|
||||
/>
|
||||
</ScrollerModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import RegisterClusterInner from './register-cluster-inner';
|
||||
import SupportedHardware from './support-hardware';
|
||||
|
||||
const Title = styled.div`
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 24px;
|
||||
`;
|
||||
|
||||
type AddModalProps = {
|
||||
registrationInfo: {
|
||||
token: string;
|
||||
image: string;
|
||||
server_url: string;
|
||||
cluster_id: number;
|
||||
};
|
||||
};
|
||||
const AddWorkerStep: React.FC<AddModalProps> = ({ registrationInfo }) => {
|
||||
return (
|
||||
<div>
|
||||
<Title>Supported Hardware Platforms</Title>
|
||||
<SupportedHardware />
|
||||
<Title style={{ marginTop: 32 }}>Execute Command</Title>
|
||||
<RegisterClusterInner registrationInfo={registrationInfo} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddWorkerStep;
|
||||
@@ -2,6 +2,7 @@ import DropDownActions from '@/components/drop-down-actions';
|
||||
import ListMap from '@/components/dynamic-form/components/list-map';
|
||||
import { FieldSchema } from '@/components/dynamic-form/config/types';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Button, Form } from 'antd';
|
||||
import _ from 'lodash';
|
||||
@@ -36,7 +37,7 @@ const ButtonWrapper = styled.span`
|
||||
const CloudOptions: React.FC<{
|
||||
ref?: any;
|
||||
}> = forwardRef((props, ref) => {
|
||||
// form instance
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance();
|
||||
const [selectedOptions, setSelectedOptions] = React.useState<Set<string>>(
|
||||
new Set()
|
||||
@@ -102,7 +103,9 @@ const CloudOptions: React.FC<{
|
||||
<DropDownActions menu={menu}>
|
||||
<Button variant="filled" color="default">
|
||||
<PlusOutlined />
|
||||
<span>Add Cloud Options</span>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'clusters.workerpool.cloudOptions' })}
|
||||
</span>
|
||||
</Button>
|
||||
</DropDownActions>
|
||||
</Title>
|
||||
@@ -113,7 +116,7 @@ const CloudOptions: React.FC<{
|
||||
name={['cloud_options', field.name as string]}
|
||||
>
|
||||
<ListMap
|
||||
btnText={'Add Item'}
|
||||
btnText={intl.formatMessage({ id: 'common.button.addItem' })}
|
||||
label={field.title}
|
||||
dataList={form.getFieldValue(['cloud_options', field.name]) || []}
|
||||
properties={field.properties || {}}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
|
||||
import { ProviderType, ProviderValueMap } from '../config';
|
||||
import {
|
||||
ClusterFormData as FormData,
|
||||
ClusterListItem as ListItem
|
||||
} from '../config/types';
|
||||
import CloudProvider from './cloud-provider-form';
|
||||
|
||||
type AddModalProps = {
|
||||
action: PageActionType;
|
||||
currentData?: ListItem; // Used when action is EDIT
|
||||
provider: ProviderType;
|
||||
credentialList: Global.BaseOption<number>[];
|
||||
onFinish: (values: FormData) => void;
|
||||
ref?: any;
|
||||
};
|
||||
const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
||||
({ action, provider, currentData, credentialList, onFinish }, ref) => {
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
if (currentData) {
|
||||
form.setFieldsValue(currentData);
|
||||
}
|
||||
}, [currentData]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
resetFields: () => {
|
||||
form.resetFields();
|
||||
},
|
||||
submit: () => {
|
||||
form.submit();
|
||||
},
|
||||
setFieldsValue: (values: any) => {
|
||||
form.setFieldsValue(values);
|
||||
},
|
||||
getFieldsValue: () => {
|
||||
return form.getFieldsValue();
|
||||
},
|
||||
validateFields: async () => {
|
||||
return await form.validateFields();
|
||||
}
|
||||
}));
|
||||
|
||||
return (
|
||||
<Form
|
||||
name="clusterForm"
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
preserve={false}
|
||||
scrollToFirstError={true}
|
||||
initialValues={currentData}
|
||||
>
|
||||
<Form.Item<FormData>
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{ id: 'common.form.rule.input' },
|
||||
{
|
||||
name: intl.formatMessage({ id: 'common.table.name' })
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label={intl.formatMessage({ id: 'common.table.name' })}
|
||||
required
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
{provider === ProviderValueMap.DigitalOcean && (
|
||||
<CloudProvider
|
||||
provider={provider}
|
||||
credentialList={credentialList}
|
||||
></CloudProvider>
|
||||
)}
|
||||
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
||||
<SealInput.TextArea
|
||||
label={intl.formatMessage({ id: 'common.table.description' })}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default ClusterForm;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Steps } from 'antd';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const { Step } = Steps;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
padding-block: 20px;
|
||||
background-color: var(--ant-color-bg-container);
|
||||
.ant-steps-item-description {
|
||||
max-width: 300px !important;
|
||||
color: var(--ant-color-text-description) !important;
|
||||
}
|
||||
.ant-steps-item-content > .ant-steps-item-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
`;
|
||||
|
||||
const ClusterSteps: React.FC<{
|
||||
currentStep: number;
|
||||
onChange: (step: number) => void;
|
||||
steps: any[];
|
||||
}> = (props) => {
|
||||
const { steps, currentStep, onChange } = props;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Steps current={currentStep} size="small" onChange={onChange}>
|
||||
{steps.map((step) => (
|
||||
<Step
|
||||
disabled={step.disabled}
|
||||
key={step.title}
|
||||
title={step.title}
|
||||
description={step.content}
|
||||
/>
|
||||
))}
|
||||
</Steps>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClusterSteps;
|
||||
@@ -0,0 +1,63 @@
|
||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Title = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
.text {
|
||||
font-size: 20px;
|
||||
}
|
||||
`;
|
||||
interface FooterButtonsProps {
|
||||
onPrevious: () => void;
|
||||
onNext: () => void;
|
||||
handleCancel: () => void;
|
||||
handleSubmit: () => void;
|
||||
showButtons: Record<string, boolean>;
|
||||
}
|
||||
|
||||
const FooterButtons: React.FC<FooterButtonsProps> = (props) => {
|
||||
const { onPrevious, onNext, handleCancel, handleSubmit, showButtons } = props;
|
||||
const intl = useIntl();
|
||||
const handleOnNext = () => {
|
||||
onNext();
|
||||
};
|
||||
return (
|
||||
<Title style={{ height: 64 }}>
|
||||
<div className="flex-center gap-16">
|
||||
{showButtons.previous && (
|
||||
<Button type="link" icon={<LeftOutlined />} onClick={onPrevious}>
|
||||
Previous
|
||||
</Button>
|
||||
)}
|
||||
{showButtons.next && (
|
||||
<Button type="link" onClick={handleOnNext}>
|
||||
Next
|
||||
<RightOutlined />
|
||||
</Button>
|
||||
)}
|
||||
{showButtons.skip && (
|
||||
<Button type="primary" onClick={handleCancel}>
|
||||
Skip for now
|
||||
</Button>
|
||||
)}
|
||||
{showButtons.save && (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSubmit}
|
||||
style={{ minWidth: 88 }}
|
||||
>
|
||||
{intl.formatMessage({ id: 'common.button.save' })}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Title>
|
||||
);
|
||||
};
|
||||
|
||||
export default FooterButtons;
|
||||
@@ -13,21 +13,20 @@ import React, {
|
||||
useImperativeHandle,
|
||||
useRef
|
||||
} from 'react';
|
||||
import {
|
||||
NodePoolFormData as FormData,
|
||||
NodePoolListItem as ListItem
|
||||
} from '../config/types';
|
||||
import CloudOptions from './cloud-options';
|
||||
import { ProviderType } from '../config';
|
||||
import { NodePoolFormData as FormData } from '../config/types';
|
||||
import VolumesConfig from './volumes-config';
|
||||
|
||||
type AddModalProps = {
|
||||
ref: any;
|
||||
name?: string;
|
||||
action: PageActionType;
|
||||
provider: string; // 'kubernetes' | 'custom' | 'digitalocean';
|
||||
currentData?: ListItem | null;
|
||||
provider: ProviderType; // 'kubernetes' | 'custom' | 'digitalocean';
|
||||
currentData?: FormData | null;
|
||||
onFinish: (values: FormData) => void;
|
||||
};
|
||||
const PoolForm: React.FC<AddModalProps> = forwardRef(
|
||||
({ action, onFinish, currentData }, ref) => {
|
||||
({ action, name = 'workerPoolForm', onFinish, currentData }, ref) => {
|
||||
const cloudOptionsRef = useRef<any>(null);
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
@@ -35,21 +34,26 @@ const PoolForm: React.FC<AddModalProps> = forwardRef(
|
||||
|
||||
useEffect(() => {
|
||||
if (currentData) {
|
||||
console.log('currentData===========1=', currentData);
|
||||
form.setFieldsValue({
|
||||
...currentData,
|
||||
volumes: currentData.cloud_options?.volumes?.[0] || {}
|
||||
...currentData
|
||||
});
|
||||
cloudOptionsRef.current?.initFieldList();
|
||||
}
|
||||
}, [currentData]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
reset: () => {
|
||||
resetFields: () => {
|
||||
form.resetFields();
|
||||
},
|
||||
submit: () => {
|
||||
form.submit();
|
||||
},
|
||||
setFieldsValue: (values: any) => {
|
||||
form.setFieldsValue(values);
|
||||
},
|
||||
getFieldsValue: () => {
|
||||
return form.getFieldsValue();
|
||||
},
|
||||
validateFields: async () => {
|
||||
return await form.validateFields();
|
||||
}
|
||||
@@ -57,9 +61,11 @@ const PoolForm: React.FC<AddModalProps> = forwardRef(
|
||||
|
||||
return (
|
||||
<Form
|
||||
name={name}
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
preserve={false}
|
||||
scrollToFirstError={true}
|
||||
initialValues={{
|
||||
replicas: 1,
|
||||
batch_size: 1
|
||||
@@ -158,7 +164,7 @@ const PoolForm: React.FC<AddModalProps> = forwardRef(
|
||||
btnText={intl.formatMessage({ id: 'common.button.addLabel' })}
|
||||
></LabelSelector>
|
||||
</Form.Item>
|
||||
<CloudOptions ref={cloudOptionsRef}></CloudOptions>
|
||||
<VolumesConfig ref={cloudOptionsRef}></VolumesConfig>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import Card from '@/components/templates/card';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { ProviderType } from '../config';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 22px;
|
||||
`;
|
||||
interface ProviderCatalogProps {
|
||||
onSelect?: (provider: ProviderType) => void;
|
||||
currentProvider?: ProviderType;
|
||||
clickable?: boolean;
|
||||
dataList: {
|
||||
label: string;
|
||||
key: string;
|
||||
locale?: boolean;
|
||||
disabled?: boolean;
|
||||
icon: React.ReactNode;
|
||||
description?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
const ProviderCatalog: React.FC<ProviderCatalogProps> = ({
|
||||
onSelect,
|
||||
dataList,
|
||||
clickable,
|
||||
currentProvider
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<Wrapper>
|
||||
{dataList?.map((action) => (
|
||||
<Card
|
||||
height="auto"
|
||||
key={action.key}
|
||||
onClick={() => onSelect?.(action.key as ProviderType)}
|
||||
active={currentProvider === action.key}
|
||||
disabled={action.disabled}
|
||||
clickable={clickable}
|
||||
header={
|
||||
action.locale
|
||||
? intl.formatMessage({ id: action.label })
|
||||
: action.label
|
||||
}
|
||||
icon={action.icon}
|
||||
>
|
||||
{action.description || 'This is a description'}
|
||||
</Card>
|
||||
))}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProviderCatalog;
|
||||
@@ -0,0 +1,94 @@
|
||||
import ascendLogo from '@/assets/logo/ascend.png';
|
||||
import CambriconPNG from '@/assets/logo/cambricon.png';
|
||||
import hyponPNG from '@/assets/logo/hygon.png';
|
||||
import iluvatarWEBP from '@/assets/logo/Iluvatar.png';
|
||||
import metaxLogo from '@/assets/logo/metax.png';
|
||||
import moorePNG from '@/assets/logo/moore _threads.png';
|
||||
import nvidiaLogo from '@/assets/logo/nvidia.png';
|
||||
import IconFont from '@/components/icon-font';
|
||||
import ProviderCatalog from './provider-catalog';
|
||||
|
||||
const ProviderImage = ({ src, showBg }: { src: string; showBg?: boolean }) => {
|
||||
return (
|
||||
<img
|
||||
src={src}
|
||||
style={{
|
||||
width: 46,
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
width={46}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const supportedHardPlatforms = [
|
||||
{
|
||||
label: 'NVIDIA CUDA',
|
||||
value: 'cuda',
|
||||
key: 'nvidia',
|
||||
locale: false,
|
||||
icon: <ProviderImage src={nvidiaLogo} showBg />
|
||||
},
|
||||
{
|
||||
label: 'AMD ROCm',
|
||||
value: 'rocm',
|
||||
key: 'amd',
|
||||
locale: false,
|
||||
icon: (
|
||||
<IconFont
|
||||
type="icon-amd"
|
||||
style={{ fontSize: 46, color: 'var(--ant-color-text)' }}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
label: 'Ascend CANN',
|
||||
value: 'npu',
|
||||
key: 'ascend',
|
||||
locale: false,
|
||||
icon: <ProviderImage src={ascendLogo} showBg />
|
||||
},
|
||||
{
|
||||
label: 'Hygon DTK',
|
||||
value: 'dcu',
|
||||
key: 'hygon',
|
||||
locale: false,
|
||||
icon: <ProviderImage src={hyponPNG} />
|
||||
},
|
||||
{
|
||||
label: 'Moore Threads MUSA',
|
||||
value: 'musa',
|
||||
key: 'musa',
|
||||
locale: false,
|
||||
icon: <ProviderImage src={moorePNG} />
|
||||
},
|
||||
{
|
||||
label: 'Iluvatar Corex',
|
||||
value: 'corex',
|
||||
key: 'corex',
|
||||
locale: false,
|
||||
icon: <ProviderImage src={iluvatarWEBP} showBg />
|
||||
},
|
||||
{
|
||||
label: 'Cambricon',
|
||||
value: 'cambricon',
|
||||
key: 'cambricon',
|
||||
locale: false,
|
||||
icon: <ProviderImage src={CambriconPNG} />
|
||||
},
|
||||
{
|
||||
label: 'Metax',
|
||||
value: 'metax',
|
||||
key: 'metax',
|
||||
locale: false,
|
||||
icon: <ProviderImage src={metaxLogo} showBg />
|
||||
}
|
||||
];
|
||||
|
||||
const SupportedHardware = () => {
|
||||
return (
|
||||
<ProviderCatalog dataList={supportedHardPlatforms} clickable={false} />
|
||||
);
|
||||
};
|
||||
|
||||
export default SupportedHardware;
|
||||
@@ -0,0 +1,34 @@
|
||||
import ListMap from '@/components/dynamic-form/components/list-map';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { fieldConfig } from '../config/cloud-options-config';
|
||||
|
||||
const volumeOptions = fieldConfig.volumes;
|
||||
|
||||
const CloudOptions: React.FC<{
|
||||
ref?: any;
|
||||
}> = forwardRef((props, ref) => {
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance();
|
||||
const volumes = Form.useWatch(['cloud_options', volumeOptions.name], form);
|
||||
|
||||
const handleOnChange = (name: string, value: any) => {
|
||||
console.log('handleOnChange========', name, value);
|
||||
form.setFieldValue(['cloud_options', name], value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form.Item name={['cloud_options', volumeOptions.name as string]}>
|
||||
<ListMap
|
||||
btnText={intl.formatMessage({ id: 'clusters.workerpool.volumes.add' })}
|
||||
label={intl.formatMessage({ id: 'clusters.workerpool.volumes' })}
|
||||
dataList={volumes || []}
|
||||
properties={volumeOptions.properties || {}}
|
||||
onChange={(value) => handleOnChange(volumeOptions.name, value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export default CloudOptions;
|
||||
Reference in New Issue
Block a user