347 lines
9.3 KiB
TypeScript
347 lines
9.3 KiB
TypeScript
import { PageAction } from '@/config';
|
|
import { PageActionType } from '@/config/types';
|
|
import { PageContainer } from '@ant-design/pro-components';
|
|
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
|
import _ from 'lodash';
|
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
import styled from 'styled-components';
|
|
import { createCluster, queryClusterToken, queryCredentialList } from './apis';
|
|
import ClusterSteps from './components/cluster-steps';
|
|
import FooterButtons from './components/footer-buttons';
|
|
import ProviderCatalog from './components/provider-catalog';
|
|
import { ProviderType, ProviderValueMap } from './config';
|
|
import providerList from './config/providers';
|
|
import { ClusterFormData } from './config/types';
|
|
import { moduleMap, moduleRegistry } from './step-forms/module-registry';
|
|
import useStepList from './step-forms/use-step-list';
|
|
|
|
const Container = styled.div`
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
`;
|
|
|
|
const Nav = styled.div`
|
|
display: flex;
|
|
align-items: center;
|
|
height: 72px;
|
|
font-weight: 400;
|
|
font-size: 20px;
|
|
color: var(--ant-color-text-tertiary);
|
|
.level-2 {
|
|
color: var(--ant-color-text);
|
|
font-weight: 600;
|
|
}
|
|
`;
|
|
|
|
const Content = styled.div`
|
|
width: 600px;
|
|
`;
|
|
|
|
const HeaderContainer = styled.div`
|
|
position: relative;
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
align-items: center;
|
|
padding-inline: var(--layout-content-header-inlinepadding);
|
|
.text {
|
|
margin-right: 16px;
|
|
padding-right: 16px;
|
|
border-right: 1px solid var(--ant-color-split);
|
|
font-weight: 600;
|
|
font-size: 20px;
|
|
margin-right: 32px;
|
|
}
|
|
`;
|
|
|
|
const ClusterCreate = () => {
|
|
const intl = useIntl();
|
|
const startStep = 0;
|
|
const stepList = useStepList();
|
|
const [searchParams] = useSearchParams();
|
|
const action =
|
|
(searchParams.get('action') as PageActionType) || PageAction.CREATE;
|
|
const navigate = useNavigate();
|
|
const [credentialList, setCredentialList] = useState<
|
|
Global.BaseOption<number, { provider: ProviderType }>[]
|
|
>([]);
|
|
const [currentStep, setCurrentStep] = useState<number>(startStep);
|
|
const [registrationInfo, setRegistrationInfo] = useState<{
|
|
token: string;
|
|
image: string;
|
|
server_url: string;
|
|
cluster_id: number;
|
|
}>({
|
|
token: '',
|
|
image: '',
|
|
server_url: '',
|
|
cluster_id: 0
|
|
});
|
|
const [extraData, setExtraData] = useState<ClusterFormData>({
|
|
provider: ProviderValueMap.Docker
|
|
} as ClusterFormData);
|
|
const [formValues, setFormValues] = useState<Record<string, any>>({});
|
|
|
|
const formRefs: Record<string, any> = {
|
|
[moduleMap.BasicForm]: useRef<any>(null),
|
|
[moduleMap.WorkerPoolForm]: useRef<any>(null)
|
|
};
|
|
|
|
const availableCredentials = useMemo(
|
|
() => credentialList.filter((item) => item.provider === extraData.provider),
|
|
[credentialList, extraData.provider]
|
|
);
|
|
|
|
const steps = useMemo(() => {
|
|
if (!extraData.provider) {
|
|
return stepList.filter((step) => step.defaultShow);
|
|
}
|
|
|
|
return stepList
|
|
.filter((step) =>
|
|
step.providers?.length
|
|
? step.providers.includes(extraData.provider as never)
|
|
: true
|
|
)
|
|
.map((step, index) => ({
|
|
...step,
|
|
disabled: action === PageAction.CREATE
|
|
}));
|
|
}, [action, extraData.provider, stepList]);
|
|
|
|
const handleStepChange = (newStep: number) => {
|
|
setCurrentStep(newStep);
|
|
console.log('step========', newStep);
|
|
};
|
|
|
|
const getFormFieldsValue = () => {
|
|
setFormValues((prev) => {
|
|
const newFormValues = _.cloneDeep(prev);
|
|
for (const [formKey, formRef] of Object.entries(formRefs)) {
|
|
const formValues = formRef?.current?.getFieldsValue?.();
|
|
if (formValues) {
|
|
newFormValues[formKey] = formValues;
|
|
}
|
|
}
|
|
return newFormValues;
|
|
});
|
|
};
|
|
|
|
const onPrevious = () => {
|
|
getFormFieldsValue();
|
|
setCurrentStep((prev) => Math.max(prev - 1, 0));
|
|
};
|
|
|
|
const validateForms = async () => {
|
|
const step = steps[currentStep];
|
|
const formKeys = step?.showForms || [];
|
|
if (formKeys?.length > 0) {
|
|
const results = await Promise.allSettled(
|
|
formKeys.map((key) => formRefs[key].current?.validateFields())
|
|
);
|
|
|
|
console.log('results========', results);
|
|
|
|
const resultsMap = new Map<string, any>();
|
|
results.forEach((result, index) => {
|
|
if (result.status === 'rejected') {
|
|
const values = result.reason?.values || {};
|
|
resultsMap.set(formKeys[index], values);
|
|
} else if (result.status === 'fulfilled') {
|
|
resultsMap.set(formKeys[index], result.value);
|
|
}
|
|
});
|
|
const newValues = _.merge(formValues, Object.fromEntries(resultsMap));
|
|
|
|
setFormValues(newValues);
|
|
|
|
const isValid = results.every((result) => result.status === 'fulfilled');
|
|
if (!isValid) {
|
|
return false;
|
|
}
|
|
return Object.assign({}, ...Object.values(newValues));
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const onNext = async (callback?: (values: ClusterFormData) => void) => {
|
|
try {
|
|
const result = await validateForms();
|
|
console.log('results========2', result);
|
|
if (result) {
|
|
await callback?.(result as ClusterFormData);
|
|
const step = steps[currentStep];
|
|
step.beforeNext?.();
|
|
setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1));
|
|
}
|
|
} catch (error) {
|
|
console.log('next error:', error);
|
|
}
|
|
};
|
|
|
|
const resetForm = () => {
|
|
setFormValues({});
|
|
for (const [formKey, formRef] of Object.entries(formRefs)) {
|
|
formRefs[formKey] = React.createRef();
|
|
}
|
|
};
|
|
|
|
const handleSelectProvider = (value: string) => {
|
|
if (value === extraData.provider) {
|
|
onNext();
|
|
return;
|
|
}
|
|
setExtraData({
|
|
provider: value
|
|
} as ClusterFormData);
|
|
resetForm();
|
|
onNext();
|
|
};
|
|
|
|
useEffect(() => {
|
|
const fetchData = async () => {
|
|
const res = await queryCredentialList({ page: 1, perPage: 100 });
|
|
const list = res.items?.map((item) => ({
|
|
label: item.name,
|
|
value: item.id,
|
|
provider: item.provider
|
|
}));
|
|
setCredentialList(list);
|
|
};
|
|
fetchData();
|
|
}, []);
|
|
|
|
const renderForms = () => {
|
|
const step = steps[currentStep];
|
|
const formKeys = step?.showForms || [];
|
|
if (formKeys.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return formKeys.map((key) => {
|
|
console.log('renderForms key========', key, formValues);
|
|
const FormComponent = moduleRegistry[key];
|
|
return FormComponent ? (
|
|
<FormComponent
|
|
key={key}
|
|
ref={formRefs[key]}
|
|
action={action}
|
|
provider={extraData.provider}
|
|
credentialList={availableCredentials}
|
|
currentData={formValues[key]}
|
|
/>
|
|
) : null;
|
|
});
|
|
};
|
|
|
|
const renderModules = () => {
|
|
const step = steps[currentStep];
|
|
const moduleKeys = step?.showModules || [];
|
|
if (moduleKeys.length === 0) {
|
|
return null;
|
|
}
|
|
return moduleKeys.map((key) => {
|
|
const ModuleComponent = moduleRegistry[key];
|
|
return ModuleComponent ? (
|
|
<ModuleComponent
|
|
key={key}
|
|
registrationInfo={registrationInfo}
|
|
provider={extraData.provider}
|
|
/>
|
|
) : null;
|
|
});
|
|
};
|
|
|
|
const showButtons = useMemo(() => {
|
|
const step = steps[currentStep];
|
|
return step?.showButtons ? step?.showButtons(extraData.provider) : {};
|
|
}, [currentStep, steps, extraData.provider]);
|
|
|
|
const submit = async (values: ClusterFormData) => {
|
|
const data = {
|
|
...extraData,
|
|
...(typeof values === 'object' ? values : {})
|
|
};
|
|
const res = await createCluster({ data });
|
|
const info = await queryClusterToken({ id: res.id });
|
|
setRegistrationInfo({
|
|
...info,
|
|
cluster_id: res.id
|
|
});
|
|
return true;
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
onNext(submit);
|
|
};
|
|
|
|
const handleCancel = () => {
|
|
navigate(-1);
|
|
};
|
|
|
|
return (
|
|
<PageContainer
|
|
ghost
|
|
footer={[
|
|
<FooterButtons
|
|
key="buttons"
|
|
onPrevious={onPrevious}
|
|
onNext={onNext}
|
|
handleCancel={handleCancel}
|
|
handleSubmit={handleSubmit}
|
|
showButtons={showButtons}
|
|
/>
|
|
]}
|
|
header={{
|
|
title: false,
|
|
style: {
|
|
paddingInline: 'var(--layout-content-header-inlinepadding)'
|
|
},
|
|
breadcrumb: {}
|
|
}}
|
|
pageHeaderRender={() => (
|
|
<HeaderContainer>
|
|
<Nav>
|
|
<span className="level-1">
|
|
{intl.formatMessage({ id: 'clusters.title' })}
|
|
</span>
|
|
<span
|
|
style={{
|
|
marginInline: 20,
|
|
color: 'var(--ant-color-split)'
|
|
}}
|
|
>
|
|
/
|
|
</span>
|
|
<span className="level-2">
|
|
{intl.formatMessage({ id: 'common.button.create' })}
|
|
</span>
|
|
</Nav>
|
|
<ClusterSteps
|
|
steps={steps}
|
|
currentStep={currentStep}
|
|
onChange={handleStepChange}
|
|
></ClusterSteps>
|
|
</HeaderContainer>
|
|
)}
|
|
>
|
|
{currentStep === startStep && (
|
|
<ProviderCatalog
|
|
dataList={providerList}
|
|
height={70}
|
|
onSelect={handleSelectProvider}
|
|
clickable={true}
|
|
current={extraData.provider}
|
|
/>
|
|
)}
|
|
{renderModules()}
|
|
<Container>
|
|
<Content>{renderForms()}</Content>
|
|
</Container>
|
|
</PageContainer>
|
|
);
|
|
};
|
|
|
|
export default ClusterCreate;
|