fix: name validation
This commit is contained in:
+1
-1
@@ -84,4 +84,4 @@ export const modelNameReg =
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const validateLabelNameRegxFor63 =
|
export const validateLabelNameRegxFor63 =
|
||||||
/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
/^(?![0-9])(?!.*--)[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ export default {
|
|||||||
'gpuservice.storage.persistentVolume': 'Persistent Volume',
|
'gpuservice.storage.persistentVolume': 'Persistent Volume',
|
||||||
'gpuservice.storage.persistentVolume.required':
|
'gpuservice.storage.persistentVolume.required':
|
||||||
'Please select a persistent volume',
|
'Please select a persistent volume',
|
||||||
'gpuservice.storage.tempCapacity': 'Storage Capacity (GB)',
|
'gpuservice.storage.tempCapacity': 'Capacity (GB)',
|
||||||
'gpuservice.storage.tempCapacity.required':
|
'gpuservice.storage.tempCapacity.required':
|
||||||
'Please enter the local temporary storage capacity'
|
'Please enter the temporary storage capacity',
|
||||||
|
'gpuservice.form.rule.name':
|
||||||
|
"Lowercase letters, numbers, and '-'. Start and end with a letter or number, no consecutive '-', max 63 characters."
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ export default {
|
|||||||
'gpuservice.storage.persistentVolume': '永続ボリューム',
|
'gpuservice.storage.persistentVolume': '永続ボリューム',
|
||||||
'gpuservice.storage.persistentVolume.required':
|
'gpuservice.storage.persistentVolume.required':
|
||||||
'永続ボリュームを選択してください',
|
'永続ボリュームを選択してください',
|
||||||
'gpuservice.storage.tempCapacity': 'ストレージ容量 (GB)',
|
'gpuservice.storage.tempCapacity': '容量 (GB)',
|
||||||
'gpuservice.storage.tempCapacity.required':
|
'gpuservice.storage.tempCapacity.required':
|
||||||
'ローカルの一時ストレージ容量を入力してください'
|
'一時ストレージ容量を入力してください',
|
||||||
|
'gpuservice.form.rule.name':
|
||||||
|
"小文字、数字、'-' のみ使用可能。文字または数字で始まり、文字または数字で終わる必要があり、連続する '-' は不可、最大 63 文字。"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ export default {
|
|||||||
'gpuservice.storage.temporary': 'Временное',
|
'gpuservice.storage.temporary': 'Временное',
|
||||||
'gpuservice.storage.persistentVolume': 'Постоянный том',
|
'gpuservice.storage.persistentVolume': 'Постоянный том',
|
||||||
'gpuservice.storage.persistentVolume.required': 'Выберите постоянный том',
|
'gpuservice.storage.persistentVolume.required': 'Выберите постоянный том',
|
||||||
'gpuservice.storage.tempCapacity': 'Объём хранилища (ГБ)',
|
'gpuservice.storage.tempCapacity': 'Объём (ГБ)',
|
||||||
'gpuservice.storage.tempCapacity.required':
|
'gpuservice.storage.tempCapacity.required':
|
||||||
'Введите объём локального временного хранилища'
|
'Введите объём временного хранилища',
|
||||||
|
'gpuservice.form.rule.name':
|
||||||
|
"Строчные буквы, цифры и '-'. Должно начинаться и заканчиваться буквой или цифрой, без подряд идущих '-', максимум 63 символа."
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ export default {
|
|||||||
'gpuservice.storage.persistentVolume': 'Kalıcı Hacim',
|
'gpuservice.storage.persistentVolume': 'Kalıcı Hacim',
|
||||||
'gpuservice.storage.persistentVolume.required':
|
'gpuservice.storage.persistentVolume.required':
|
||||||
'Lütfen bir kalıcı hacim seçin',
|
'Lütfen bir kalıcı hacim seçin',
|
||||||
'gpuservice.storage.tempCapacity': 'Depolama Kapasitesi (GB)',
|
'gpuservice.storage.tempCapacity': 'Kapasite (GB)',
|
||||||
'gpuservice.storage.tempCapacity.required':
|
'gpuservice.storage.tempCapacity.required':
|
||||||
'Lütfen yerel geçici depolama kapasitesini girin'
|
'Lütfen geçici depolama kapasitesini girin',
|
||||||
|
'gpuservice.form.rule.name':
|
||||||
|
"Küçük harfler, rakamlar ve '-'. Harf veya rakamla başlamalı ve bitmeli, ardışık '-' içermemeli, en fazla 63 karakter."
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ export default {
|
|||||||
'gpuservice.storage.temporary': '临时',
|
'gpuservice.storage.temporary': '临时',
|
||||||
'gpuservice.storage.persistentVolume': '持久卷',
|
'gpuservice.storage.persistentVolume': '持久卷',
|
||||||
'gpuservice.storage.persistentVolume.required': '请选择持久卷',
|
'gpuservice.storage.persistentVolume.required': '请选择持久卷',
|
||||||
'gpuservice.storage.tempCapacity': '存储容量 (GB)',
|
'gpuservice.storage.tempCapacity': '容量(GB)',
|
||||||
'gpuservice.storage.tempCapacity.required': '请输入本地临时存储容量'
|
'gpuservice.storage.tempCapacity.required': '请输入临时存储容量',
|
||||||
|
'gpuservice.form.rule.name':
|
||||||
|
'由小写字母、数字和 "-" 组成,以字母或数字开头和结尾,不能包含连续的 "-",最多 63 个字符。'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,43 +6,15 @@ import { ColumnWrapper, GSDrawer, ModalFooter } from '@gpustack/core-ui';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Empty, Input, Typography } from 'antd';
|
import { Empty, Input, Typography } from 'antd';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
|
||||||
import { ListItem as TemplateItem } from '../../templates/config/types';
|
import { ListItem as TemplateItem } from '../../templates/config/types';
|
||||||
import useQueryTemplates from '../../templates/services/use-query-templates';
|
import useQueryTemplates from '../../templates/services/use-query-templates';
|
||||||
import { FormData, InstanceTypeItem, ListItem } from '../config/types';
|
import { FormData, InstanceTypeItem, ListItem } from '../config/types';
|
||||||
import GPUServiceInstanceForm from '../forms';
|
import GPUServiceInstanceForm from '../forms';
|
||||||
import TemplateSelector from '../forms/template-selector';
|
import TemplateSelector from '../forms/template-selector';
|
||||||
import useQueryInstanceTypes from '../services/use-query-instance-types';
|
import useQueryInstanceTypes from '../services/use-query-instance-types';
|
||||||
|
import styles from '../styles/instances.module.less';
|
||||||
import InstanceTypeList from './instance-type-list';
|
import InstanceTypeList from './instance-type-list';
|
||||||
|
|
||||||
const Container = styled.div`
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ColWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
max-width: 33%;
|
|
||||||
min-height: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const PanelBody = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const FormWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
max-width: 34%;
|
|
||||||
min-height: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type AddModalProps = {
|
type AddModalProps = {
|
||||||
title: string;
|
title: string;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
@@ -280,10 +252,10 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
}}
|
}}
|
||||||
footer={false}
|
footer={false}
|
||||||
>
|
>
|
||||||
<Container>
|
<div className={styles.container}>
|
||||||
<ColWrapper>
|
<div className={styles.colWrapper}>
|
||||||
<ColumnWrapper styles={{ container: { paddingBlock: 0 } }}>
|
<ColumnWrapper styles={{ container: { paddingBlock: 0 } }}>
|
||||||
<PanelBody>
|
<div className={styles.panelBody}>
|
||||||
<ColTitle style={{ paddingBottom: 0 }}>
|
<ColTitle style={{ paddingBottom: 0 }}>
|
||||||
{intl.formatMessage({ id: 'gpuservice.instance.types' })}
|
{intl.formatMessage({ id: 'gpuservice.instance.types' })}
|
||||||
</ColTitle>
|
</ColTitle>
|
||||||
@@ -311,13 +283,13 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
loading={instanceTypesLoading}
|
loading={instanceTypesLoading}
|
||||||
onChange={handleInstanceTypeChange}
|
onChange={handleInstanceTypeChange}
|
||||||
/>
|
/>
|
||||||
</PanelBody>
|
</div>
|
||||||
</ColumnWrapper>
|
</ColumnWrapper>
|
||||||
<Separator></Separator>
|
<Separator></Separator>
|
||||||
</ColWrapper>
|
</div>
|
||||||
<ColWrapper>
|
<div className={styles.colWrapper}>
|
||||||
<ColumnWrapper styles={{ container: { paddingBlock: 0 } }}>
|
<ColumnWrapper styles={{ container: { paddingBlock: 0 } }}>
|
||||||
<PanelBody>
|
<div className={styles.panelBody}>
|
||||||
<ColTitle style={{ paddingBottom: 0 }}>
|
<ColTitle style={{ paddingBottom: 0 }}>
|
||||||
{intl.formatMessage({ id: 'gpuservice.instance.templates' })}
|
{intl.formatMessage({ id: 'gpuservice.instance.templates' })}
|
||||||
</ColTitle>
|
</ColTitle>
|
||||||
@@ -348,11 +320,11 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
) : (
|
) : (
|
||||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||||
)}
|
)}
|
||||||
</PanelBody>
|
</div>
|
||||||
</ColumnWrapper>
|
</ColumnWrapper>
|
||||||
<Separator></Separator>
|
<Separator></Separator>
|
||||||
</ColWrapper>
|
</div>
|
||||||
<FormWrapper>
|
<div className={styles.formWrapper}>
|
||||||
<ColumnWrapper
|
<ColumnWrapper
|
||||||
styles={{ container: { paddingBlock: 0 } }}
|
styles={{ container: { paddingBlock: 0 } }}
|
||||||
footer={
|
footer={
|
||||||
@@ -381,8 +353,8 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
</ColumnWrapper>
|
</ColumnWrapper>
|
||||||
</FormWrapper>
|
</div>
|
||||||
</Container>
|
</div>
|
||||||
</GSDrawer>
|
</GSDrawer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({
|
|||||||
<Title>
|
<Title>
|
||||||
<Flex gap={8} align="center">
|
<Flex gap={8} align="center">
|
||||||
<AutoTooltip ghost minWidth={20} maxWidth={180}>
|
<AutoTooltip ghost minWidth={20} maxWidth={180}>
|
||||||
{displayName}
|
{item.spec ? displayName : '-'}
|
||||||
</AutoTooltip>
|
</AutoTooltip>
|
||||||
{acceleratable && manufacturer && (
|
{acceleratable && manufacturer && (
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { validateLabelNameRegxFor63 } from '@/config';
|
||||||
import { Input as CInput } from '@gpustack/core-ui';
|
import { Input as CInput } from '@gpustack/core-ui';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
@@ -16,6 +17,10 @@ const Basic = () => {
|
|||||||
message: intl.formatMessage({
|
message: intl.formatMessage({
|
||||||
id: 'gpuservice.instance.name.required'
|
id: 'gpuservice.instance.name.required'
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: validateLabelNameRegxFor63,
|
||||||
|
message: intl.formatMessage({ id: 'gpuservice.form.rule.name' })
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -32,11 +32,12 @@ const InstanceTypePicker: React.FC<InstanceTypePickerProps> = ({
|
|||||||
dataList
|
dataList
|
||||||
}) => {
|
}) => {
|
||||||
const selected = useMemo(() => {
|
const selected = useMemo(() => {
|
||||||
return dataList.find((item) => (item.metadata?.name || '') === value);
|
return (
|
||||||
|
dataList.find((item) => (item.metadata?.name || '') === value) ||
|
||||||
|
({} as InstanceTypeItemModel)
|
||||||
|
);
|
||||||
}, [value, dataList]);
|
}, [value, dataList]);
|
||||||
|
|
||||||
if (!selected) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectedCard>
|
<SelectedCard>
|
||||||
<InstanceTypeItem item={selected} showStatus={false} />
|
<InstanceTypeItem item={selected} showStatus={false} />
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { currentClusterAtom } from '@/atoms/gpuservice';
|
import { currentClusterAtom } from '@/atoms/gpuservice';
|
||||||
import { getCurrentOrganizationId } from '@/atoms/user';
|
import { getCurrentOrgNamespace } from '@/atoms/user';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { GPU_SERVICE_INSTANCES_API } from '../apis';
|
import { GPU_SERVICE_INSTANCES_API } from '../apis';
|
||||||
import { ListItem } from '../config/types';
|
import { ListItem } from '../config/types';
|
||||||
|
|
||||||
const useViewLogs = () => {
|
const useViewLogs = () => {
|
||||||
const namespace = getCurrentOrganizationId();
|
|
||||||
const currentCluster = useAtomValue(currentClusterAtom);
|
const currentCluster = useAtomValue(currentClusterAtom);
|
||||||
const clusterID = currentCluster?.id;
|
const clusterID = currentCluster?.id;
|
||||||
|
const namespace = getCurrentOrgNamespace(currentCluster?.owner_principal_id);
|
||||||
|
|
||||||
const [openModalStatus, setOpenModalStatus] = useState<{
|
const [openModalStatus, setOpenModalStatus] = useState<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colWrapper {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 33%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelBody {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formWrapper {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 34%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { validateLabelNameRegxFor63 } from '@/config';
|
||||||
import {
|
import {
|
||||||
Input as CInput,
|
Input as CInput,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
@@ -29,6 +30,10 @@ const Basic = ({ open }: { open: boolean }) => {
|
|||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: getRuleMessage('input', 'common.table.name')
|
message: getRuleMessage('input', 'common.table.name')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: validateLabelNameRegxFor63,
|
||||||
|
message: intl.formatMessage({ id: 'gpuservice.form.rule.name' })
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user