fix: instance form ux
This commit is contained in:
@@ -42,6 +42,8 @@ export default {
|
|||||||
'Please make sure the <a href={link} target="_blank">prerequisites</a> for {label} are met before executing the following command.',
|
'Please make sure the <a href={link} target="_blank">prerequisites</a> for {label} are met before executing the following command.',
|
||||||
'clusters.create.addCommand.tips':
|
'clusters.create.addCommand.tips':
|
||||||
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
||||||
|
'clusters.create.addCommand.k8s.tips':
|
||||||
|
'On the Kubernetes cluster that needs to be registered, run the following command to create the Kubernetes resources and register the cluster.',
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
'On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.',
|
'On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.',
|
||||||
'cluster.create.checkEnv.tips':
|
'cluster.create.checkEnv.tips':
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export default {
|
|||||||
'Please make sure the <a href={link} target="_blank">prerequisites</a> for {label} are met before executing the following command.',
|
'Please make sure the <a href={link} target="_blank">prerequisites</a> for {label} are met before executing the following command.',
|
||||||
'clusters.create.addCommand.tips':
|
'clusters.create.addCommand.tips':
|
||||||
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
||||||
|
'clusters.create.addCommand.k8s.tips':
|
||||||
|
'登録する Kubernetes クラスターで以下のコマンドを実行し、Kubernetes リソースを作成してクラスターを登録します。',
|
||||||
'cluster.create.checkEnv.tips':
|
'cluster.create.checkEnv.tips':
|
||||||
'Use the following command to check if the environment is ready.',
|
'Use the following command to check if the environment is ready.',
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export default {
|
|||||||
'Пожалуйста, убедитесь, что выполнены <a href={link} target="_blank">предварительные условия</a> для {label} перед выполнением следующей команды.',
|
'Пожалуйста, убедитесь, что выполнены <a href={link} target="_blank">предварительные условия</a> для {label} перед выполнением следующей команды.',
|
||||||
'clusters.create.addCommand.tips':
|
'clusters.create.addCommand.tips':
|
||||||
'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.',
|
'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.',
|
||||||
|
'clusters.create.addCommand.k8s.tips':
|
||||||
|
'На Kubernetes-кластере, который необходимо зарегистрировать, выполните следующую команду, чтобы создать ресурсы Kubernetes и зарегистрировать этот кластер.',
|
||||||
'cluster.create.checkEnv.tips':
|
'cluster.create.checkEnv.tips':
|
||||||
'Используйте следующую команду для проверки готовности окружения',
|
'Используйте следующую команду для проверки готовности окружения',
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export default {
|
|||||||
'Aşağıdaki komutu çalıştırmadan önce lütfen {label} için <a href={link} target="_blank">ön koşulların</a> karşılandığından emin olun.',
|
'Aşağıdaki komutu çalıştırmadan önce lütfen {label} için <a href={link} target="_blank">ön koşulların</a> karşılandığından emin olun.',
|
||||||
'clusters.create.addCommand.tips':
|
'clusters.create.addCommand.tips':
|
||||||
'Eklenmesi gereken İşçi Düğümde, kümeye katılması için aşağıdaki komutu çalıştırın.',
|
'Eklenmesi gereken İşçi Düğümde, kümeye katılması için aşağıdaki komutu çalıştırın.',
|
||||||
|
'clusters.create.addCommand.k8s.tips':
|
||||||
|
'Kaydedilmesi gereken Kubernetes kümesinde, Kubernetes kaynaklarını oluşturmak ve kümeyi kaydetmek için aşağıdaki komutu çalıştırın.',
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
'Eklenmesi gereken Kubernetes kümesinde, düğümlerini kümeye katılması için aşağıdaki komutu çalıştırın.',
|
'Eklenmesi gereken Kubernetes kümesinde, düğümlerini kümeye katılması için aşağıdaki komutu çalıştırın.',
|
||||||
'cluster.create.checkEnv.tips':
|
'cluster.create.checkEnv.tips':
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export default {
|
|||||||
'在执行以下命令之前,请确保已满足 {label} 的<a href={link} target="_blank">先决条件</a>。',
|
'在执行以下命令之前,请确保已满足 {label} 的<a href={link} target="_blank">先决条件</a>。',
|
||||||
'clusters.create.addCommand.tips':
|
'clusters.create.addCommand.tips':
|
||||||
'在需要添加的节点上运行以下命令,将其加入到集群中。',
|
'在需要添加的节点上运行以下命令,将其加入到集群中。',
|
||||||
|
'clusters.create.addCommand.k8s.tips':
|
||||||
|
'在需要注册的 Kubernetes 集群中运行以下命令,创建 Kubernetes 资源,注册该集群。',
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
'在需要添加的 Kubernetes 集群上运行以下命令,将其中的节点加入到集群中。',
|
'在需要添加的 Kubernetes 集群上运行以下命令,将其中的节点加入到集群中。',
|
||||||
'cluster.create.checkEnv.tips': '使用以下命令检查环境是否准备妥当。',
|
'cluster.create.checkEnv.tips': '使用以下命令检查环境是否准备妥当。',
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ const NumberSelection: React.FC<NumberSelectionProps> = ({
|
|||||||
aria-disabled={disabled}
|
aria-disabled={disabled}
|
||||||
tabIndex={disabled ? -1 : 0}
|
tabIndex={disabled ? -1 : 0}
|
||||||
className={classNames(styles.numberItem, {
|
className={classNames(styles.numberItem, {
|
||||||
[styles.active]: num === value
|
[styles.active]: num === value && !!value
|
||||||
})}
|
})}
|
||||||
onClick={() => handleSelect(num)}
|
onClick={() => handleSelect(num)}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const K8sRunCommand: React.FC<AddWorkerStepProps> = ({ disabled }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
id: 'clusters.create.addCommand.tips'
|
id: 'clusters.create.addCommand.k8s.tips'
|
||||||
})}
|
})}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
<RegisterClusterInner
|
<RegisterClusterInner
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { AutoTooltip, IconFont, StatusTag, ThemeTag } from '@gpustack/core-ui';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Flex } from 'antd';
|
import { Flex } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { manufactureColorMap } from '../../templates/config';
|
||||||
import {
|
import {
|
||||||
convertKiToGi,
|
convertKiToGi,
|
||||||
InstanceTypePhaseLabelMap,
|
InstanceTypePhaseLabelMap,
|
||||||
@@ -67,7 +68,10 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({
|
|||||||
const name = item.metadata?.name;
|
const name = item.metadata?.name;
|
||||||
const acceleratable = item.spec?.acceleratable;
|
const acceleratable = item.spec?.acceleratable;
|
||||||
|
|
||||||
const manufacturer = item.spec?.manufacturer?.toUpperCase();
|
const manufacturerKey = item.spec?.manufacturer;
|
||||||
|
const manufacturer = manufacturerKey?.toUpperCase();
|
||||||
|
const manufacturerColor =
|
||||||
|
(manufacturerKey && manufactureColorMap[manufacturerKey]) ?? 'purple';
|
||||||
|
|
||||||
const renderName = () => {
|
const renderName = () => {
|
||||||
const product = item.spec?.product;
|
const product = item.spec?.product;
|
||||||
@@ -91,7 +95,7 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({
|
|||||||
fontWeight: 400
|
fontWeight: 400
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ThemeTag color="purple">{manufacturer}</ThemeTag>
|
<ThemeTag color={manufacturerColor}>{manufacturer}</ThemeTag>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface FormData {
|
|||||||
name: string;
|
name: string;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
};
|
};
|
||||||
|
enable_ssh?: boolean;
|
||||||
spec: {
|
spec: {
|
||||||
type: string;
|
type: string;
|
||||||
image: string;
|
image: string;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {
|
|||||||
CheckboxField,
|
CheckboxField,
|
||||||
CollapsePanel,
|
CollapsePanel,
|
||||||
IconFont,
|
IconFont,
|
||||||
|
MultipleSelect,
|
||||||
ScrollSpyTabs,
|
ScrollSpyTabs,
|
||||||
Select,
|
|
||||||
useFinishFailed,
|
useFinishFailed,
|
||||||
useScrollActiveChange,
|
useScrollActiveChange,
|
||||||
useWrapperContext
|
useWrapperContext
|
||||||
@@ -25,6 +25,7 @@ import TemplateBasicForm, {
|
|||||||
BasicResourceMax
|
BasicResourceMax
|
||||||
} from '../../templates/forms/basic';
|
} from '../../templates/forms/basic';
|
||||||
import { FormData, InstanceTypeItem, ListItem } from '../config/types';
|
import { FormData, InstanceTypeItem, ListItem } from '../config/types';
|
||||||
|
import instanceStyles from '../styles/instances.module.less';
|
||||||
import Basic from './basic';
|
import Basic from './basic';
|
||||||
import InstanceTypeFormItem from './instance-type';
|
import InstanceTypeFormItem from './instance-type';
|
||||||
import StorageVolume from './storage-volume';
|
import StorageVolume from './storage-volume';
|
||||||
@@ -123,6 +124,22 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
|
|||||||
}
|
}
|
||||||
}, [open, action]);
|
}, [open, action]);
|
||||||
|
|
||||||
|
const handleSSHEnableChange = (e: any) => {
|
||||||
|
if (!e?.target?.checked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentPorts = form.getFieldValue(['spec', 'ports']) || [];
|
||||||
|
const hasSSHPort = currentPorts.some(
|
||||||
|
(item: any) => item?.protocol === 'TCP' && item?.port === SSH_PORT
|
||||||
|
);
|
||||||
|
if (!hasSSHPort) {
|
||||||
|
form.setFieldValue(
|
||||||
|
['spec', 'ports'],
|
||||||
|
[...currentPorts, { protocol: 'TCP', port: SSH_PORT, name: 'SSH' }]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const segmentOptions = useMemo(
|
const segmentOptions = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -363,31 +380,39 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
|
|||||||
<CheckboxField
|
<CheckboxField
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label={'启用 SSH 访问'}
|
label={'启用 SSH 访问'}
|
||||||
|
onChange={handleSSHEnableChange}
|
||||||
></CheckboxField>
|
></CheckboxField>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{sshEnabled && (
|
{sshEnabled && (
|
||||||
<Form.Item<FormData> name={['spec', 'sshPublicKey', 'name']}>
|
<div className={instanceStyles.sshkeySelection}>
|
||||||
<Select
|
<Form.Item<FormData> name={['spec', 'sshPublicKey', 'name']}>
|
||||||
disabled={disabled}
|
<MultipleSelect
|
||||||
mode="multiple"
|
disabled={disabled}
|
||||||
maxTagCount={1}
|
mode="multiple"
|
||||||
label={'SSH 公钥'}
|
maxTagCount={1}
|
||||||
options={[
|
label={'SSH 公钥'}
|
||||||
{
|
styles={{
|
||||||
label: 'SSH-key-1',
|
wrapper: {
|
||||||
value: 'gpustack-ssh-public-key'
|
width: '100%'
|
||||||
},
|
}
|
||||||
{
|
}}
|
||||||
label: 'SSH-key-2',
|
options={[
|
||||||
value: 'gpustack-ssh-public-key-2'
|
{
|
||||||
},
|
label: 'SSH-key-1',
|
||||||
{
|
value: 'gpustack-ssh-public-key'
|
||||||
label: 'SSH-key-3',
|
},
|
||||||
value: 'gpustack-ssh-public-key-3'
|
{
|
||||||
}
|
label: 'SSH-key-2',
|
||||||
]}
|
value: 'gpustack-ssh-public-key-2'
|
||||||
></Select>
|
},
|
||||||
</Form.Item>
|
{
|
||||||
|
label: 'SSH-key-3',
|
||||||
|
value: 'gpustack-ssh-public-key-3'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
></MultipleSelect>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</ScrollSpyTabs>
|
</ScrollSpyTabs>
|
||||||
|
|||||||
@@ -87,7 +87,8 @@ const StorageVolume = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldBlock>
|
<>
|
||||||
|
<div data-field="storage"></div>
|
||||||
<Flex
|
<Flex
|
||||||
style={{
|
style={{
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
@@ -149,7 +150,6 @@ const StorageVolume = ({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<div data-field="storage"></div>
|
|
||||||
|
|
||||||
{storageMode === StorageModeValueMap.Existing && (
|
{storageMode === StorageModeValueMap.Existing && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -230,7 +230,7 @@ const StorageVolume = ({
|
|||||||
onCancel={() => setOverlayOpen(false)}
|
onCancel={() => setOverlayOpen(false)}
|
||||||
onSubmit={handleCreateStorage}
|
onSubmit={handleCreateStorage}
|
||||||
/>
|
/>
|
||||||
</FieldBlock>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,3 +32,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sshkeySelection {
|
||||||
|
:global {
|
||||||
|
.ant-select-content .ant-tag {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Tag } from 'antd';
|
import { Button, Tag } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { templateActions } from '../config';
|
import { manufactureColorMap, templateActions } from '../config';
|
||||||
import { ListItem } from '../config/types';
|
import { ListItem } from '../config/types';
|
||||||
|
|
||||||
const imageLogoMap = {
|
const imageLogoMap = {
|
||||||
@@ -40,19 +40,6 @@ const imageLogoMap = {
|
|||||||
ubuntu: ubuntuLogo
|
ubuntu: ubuntuLogo
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const manufactureColorMap: Record<string, string> = {
|
|
||||||
nvidia: 'green',
|
|
||||||
amd: 'volcano',
|
|
||||||
ascend: 'orange',
|
|
||||||
hygon: 'magenta',
|
|
||||||
moorthreads: 'cyan',
|
|
||||||
iluvatar: 'purple',
|
|
||||||
metax: 'geekblue',
|
|
||||||
cambricon: 'gold',
|
|
||||||
thead: 'red',
|
|
||||||
cpu: 'blue'
|
|
||||||
};
|
|
||||||
|
|
||||||
const manufacturerLabelMap: Record<string, string> = Object.values(
|
const manufacturerLabelMap: Record<string, string> = Object.values(
|
||||||
GPUsConfigs
|
GPUsConfigs
|
||||||
).reduce(
|
).reduce(
|
||||||
|
|||||||
@@ -35,6 +35,19 @@ export const ImagePullPolicyOptions: {
|
|||||||
|
|
||||||
export const DefaultImagePullPolicy: ImagePullPolicy = 'IfNotPresent';
|
export const DefaultImagePullPolicy: ImagePullPolicy = 'IfNotPresent';
|
||||||
|
|
||||||
|
export const manufactureColorMap: Record<string, string> = {
|
||||||
|
nvidia: 'green',
|
||||||
|
amd: 'volcano',
|
||||||
|
ascend: 'orange',
|
||||||
|
hygon: 'magenta',
|
||||||
|
moorthreads: 'cyan',
|
||||||
|
iluvatar: 'purple',
|
||||||
|
metax: 'geekblue',
|
||||||
|
cambricon: 'gold',
|
||||||
|
thead: 'red',
|
||||||
|
cpu: 'blue'
|
||||||
|
};
|
||||||
|
|
||||||
export const normalizeCommand = (value: string): string[] => {
|
export const normalizeCommand = (value: string): string[] => {
|
||||||
if (!value) return [];
|
if (!value) return [];
|
||||||
const tokens: string[] = [];
|
const tokens: string[] = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user