fix: display instance type spec

This commit is contained in:
jialin
2025-09-16 11:26:17 +08:00
parent deeee70807
commit c467280170
54 changed files with 1058 additions and 204 deletions
@@ -1,19 +1,32 @@
import {
regionInstanceTypeListAtom,
regionOSImageListAtom
} from '@/atoms/clusters';
import CollapsibleContainer, {
CollapsibleContainerProps
} from '@/components/collapse-container';
import IconFont from '@/components/icon-font';
import LabelSelector from '@/components/label-selector';
import SealInputNumber from '@/components/seal-form/input-number';
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import useAppUtils from '@/hooks/use-app-utils';
import { CardContainer } from '@/pages/llmodels/components/gpu-card';
import { DeleteOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Form } from 'antd';
import { useAtom } from 'jotai';
import _ from 'lodash';
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useState
} from 'react';
import styled from 'styled-components';
import { ProviderType } from '../config';
import { ProviderType, instanceTypeFieldMap, vendorIconMap } from '../config';
import { NodePoolFormData as FormData } from '../config/types';
import VolumesConfig from './volumes-config';
@@ -22,6 +35,12 @@ const Container = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 0 16px;
.ant-form-item:nth-child(1) {
grid-column: 1 / 3;
}
.ant-form-item:nth-child(2) {
grid-column: 1 / 3;
}
.ant-form-item:nth-child(5) {
grid-column: 1 / 3;
}
@@ -34,6 +53,65 @@ const Container = styled.div`
}
`;
const NoContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
padding-block: 12px;
`;
const OptionItem = styled.div.attrs({
className: 'option-item'
})`
display: flex;
gap: 8px;
align-items: center;
`;
const DescriptionWrapper = styled.div`
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 8px;
font-weight: 400;
`;
const NotFoundContent = () => {
return <NoContent>No instance type available</NoContent>;
};
export const RenderInstanceOption = (option: any) => {
const { data, styles } = option;
return (
<CardContainer
key={data.value}
header={
<span>
<IconFont
type={_.get(vendorIconMap, data.vendor, 'icon-gpu1')}
className="m-r-8"
></IconFont>
{data.description}
</span>
}
description={
<DescriptionWrapper style={{ ...(styles?.description || {}) }}>
{Object.entries(data?.specInfo)
.filter(([key, value]) => value)
.map(([key, value]) => (
<OptionItem key={key}>
<span className="label">
{_.get(instanceTypeFieldMap, key, key)}:
</span>
<span className="value">{value as string}</span>
</OptionItem>
))}
</DescriptionWrapper>
}
/>
);
};
type AddModalProps = {
ref: any;
name?: string;
@@ -45,6 +123,23 @@ type AddModalProps = {
showDelete?: boolean;
collapseProps?: CollapsibleContainerProps;
};
const InstanceSpecData: React.FC<{ instanceSpec: Record<string, any> }> = ({
instanceSpec
}) => {
return (
<>
{Object.entries(instanceSpec)
.filter(([key, value]) => value)
.map(([key, value]) => (
<Form.Item key={key} name={['instance_spec', key]} hidden>
<SealInput.Input />
</Form.Item>
))}
</>
);
};
const PoolForm: React.FC<AddModalProps> = forwardRef((props, ref) => {
const {
action,
@@ -55,22 +150,71 @@ const PoolForm: React.FC<AddModalProps> = forwardRef((props, ref) => {
currentData,
collapseProps
} = props;
const [instanceTypeList] = useAtom(regionInstanceTypeListAtom);
const [osImageList] = useAtom(regionOSImageListAtom);
const { collapsible, onToggle, ...restCollapseProps } = collapseProps || {};
const [form] = Form.useForm();
const intl = useIntl();
const { getRuleMessage } = useAppUtils();
const labels = Form.useWatch('labels', form);
const title = Form.useWatch('name', form);
const [instanceSpec, setInstanceSpec] = useState<Record<string, any>>({});
useEffect(() => {
if (currentData) {
console.log('currentData===========1=', currentData);
form.setFieldsValue({
...currentData
});
setInstanceSpec({
...currentData.instance_spec
});
}
}, [currentData]);
const labelRender = (data: { label: string; value: string }) => {
if (action === PageAction.EDIT) {
return currentData?.image_name || currentData?.os_image;
}
return data.label;
};
const instanceLabelRender = (data: { label: string; value: string }) => {
if (action === PageAction.EDIT) {
return currentData?.instance_spec?.label || currentData?.instance_type;
}
return data.label;
};
const handleOsImageChange = (value: string) => {
form.setFieldsValue({
image_name: osImageList.find((item) => item.value === value)?.label
});
};
const handleInstanceTypeChange = (value: string, option: any) => {
setInstanceSpec({
...option.specInfo,
label: option.label,
vendor: option.vendor,
description: option.description
});
form.setFieldsValue({
instance_spec: {
...option.specInfo,
label: option.label,
vendor: option.vendor,
description: option.description
}
});
};
const filterInstanceOption = (inputValue: string, option: any) => {
return (
option.label.toLowerCase().includes(inputValue.toLowerCase()) ||
option.description.toLowerCase().includes(inputValue.toLowerCase())
);
};
useImperativeHandle(ref, () => ({
resetFields: () => {
form.resetFields();
@@ -149,13 +293,20 @@ const PoolForm: React.FC<AddModalProps> = forwardRef((props, ref) => {
}
]}
>
<SealInput.Input
<SealSelect
showSearch
filterOption={filterInstanceOption}
labelRender={instanceLabelRender}
notFoundContent={<NotFoundContent />}
label={intl.formatMessage({
id: 'clusters.workerpool.instanceType'
})}
required
options={instanceTypeList}
disabled={action === PageAction.EDIT}
></SealInput.Input>
optionRender={RenderInstanceOption}
onChange={handleInstanceTypeChange}
></SealSelect>
</Form.Item>
<Form.Item<FormData>
name="replicas"
@@ -192,6 +343,7 @@ const PoolForm: React.FC<AddModalProps> = forwardRef((props, ref) => {
required
></SealInputNumber>
</Form.Item>
<Form.Item<FormData>
name="os_image"
rules={[
@@ -201,13 +353,18 @@ const PoolForm: React.FC<AddModalProps> = forwardRef((props, ref) => {
}
]}
>
<SealInput.Input
<SealSelect
showSearch
onChange={handleOsImageChange}
optionRender={RenderInstanceOption}
labelRender={labelRender}
options={osImageList}
disabled={action === PageAction.EDIT}
label={intl.formatMessage({
id: 'clusters.workerpool.osImage'
})}
required
></SealInput.Input>
></SealSelect>
</Form.Item>
<Form.Item<FormData>
@@ -235,12 +392,17 @@ const PoolForm: React.FC<AddModalProps> = forwardRef((props, ref) => {
]}
>
<LabelSelector
disabled={action === PageAction.EDIT}
label={intl.formatMessage({ id: 'resources.table.labels' })}
labels={labels || {}}
btnText={intl.formatMessage({ id: 'common.button.addLabel' })}
></LabelSelector>
</Form.Item>
<VolumesConfig></VolumesConfig>
<VolumesConfig disabled={action === PageAction.EDIT}></VolumesConfig>
<Form.Item<FormData> name="image_name" hidden>
<SealInput.Input></SealInput.Input>
</Form.Item>
<InstanceSpecData instanceSpec={instanceSpec} />
</Container>
</Form>
</CollapsibleContainer>