import { clusterSessionAtom } from '@/atoms/clusters'; import { PageAction } from '@/config'; import { PageActionType } from '@/config/types'; import PageBreadcrumb from '@/pages/_components/page-breadcrumb'; import { useIntl, useNavigate, useSearchParams } from '@umijs/max'; import { useAtom } from 'jotai'; import _ from 'lodash'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; import { PageContainerInner } from '../_components/page-box'; import { createCluster, queryClusterToken, queryCredentialList } from './apis'; import { DockerStepsFromCluster, K8sStepsFromCluter } from './components/add-worker/config'; 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 { StepsContext } from './config/steps-context'; 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 Content = styled.div` width: 600px; `; 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 [clusterSession, setClusterSession] = useAtom(clusterSessionAtom); const [credentialList, setCredentialList] = useState< Global.BaseOption[] >([]); const [currentStep, setCurrentStep] = useState(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({ provider: ProviderValueMap.Docker } as ClusterFormData); const [formValues, setFormValues] = useState>({}); const formRefs: Record = { [moduleMap.BasicForm]: useRef(null), [moduleMap.WorkerPoolForm]: useRef(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]); // before moving to the next step, get all form values 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()) ); const resultsMap = new Map(); 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(); 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 }); 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) => { const FormComponent = moduleRegistry[key]; return FormComponent ? ( ) : null; }); }; /** * this function is used to render the modules in the current step, they are not forms * @returns */ 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 ? ( ) : 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 = () => { if (clusterSession?.firstAddCluster) { setClusterSession({ firstAddCluster: false, firstAddWorker: false }); navigate(`/cluster-management/clusters/list`); return; } navigate(-1); }; const breadcrumbItems = [ { title: {intl.formatMessage({ id: 'clusters.title' })}, onClick: () => navigate(-1) }, { title: intl.formatMessage({ id: 'common.button.create' }) } ]; return ( ]} header={{ title: }} >
{currentStep === startStep && ( )} {renderModules()} {renderForms()}
); }; export default ClusterCreate;