fix: prevent duplicate submit and unlock on validation failure in deploy/update modals
This commit is contained in:
@@ -59,7 +59,7 @@ type AddModalProps = {
|
|||||||
source: SourceType;
|
source: SourceType;
|
||||||
width?: string | number;
|
width?: string | number;
|
||||||
current?: any;
|
current?: any;
|
||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void | Promise<void>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -104,8 +104,14 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
const specListRef = useRef<any[]>([]);
|
const specListRef = useRef<any[]>([]);
|
||||||
const noCompatibleGPUsRef = useRef<boolean>(false);
|
const noCompatibleGPUsRef = useRef<boolean>(false);
|
||||||
const backendOptionsCache = useRef<any>([]);
|
const backendOptionsCache = useRef<any>([]);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const submitloadingRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const handleSumit = () => {
|
const handleSumit = () => {
|
||||||
|
if (submitloadingRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitloadingRef.current = true;
|
||||||
form.current?.submit?.();
|
form.current?.submit?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -115,7 +121,11 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
submitAnyway.current = true;
|
submitAnyway.current = true;
|
||||||
form.current?.submit?.();
|
handleSumit();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFinishFailed = () => {
|
||||||
|
submitloadingRef.current = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateSubmitData = (formData: FormData) => {
|
const generateSubmitData = (formData: FormData) => {
|
||||||
@@ -348,7 +358,13 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
..._.omit(selectSpecRef.current, ['name']),
|
..._.omit(selectSpecRef.current, ['name']),
|
||||||
...values
|
...values
|
||||||
};
|
};
|
||||||
onOk(data);
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
await onOk(data);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
submitloadingRef.current = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
@@ -447,6 +463,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
<ModalFooter
|
<ModalFooter
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
onOk={handleSumit}
|
onOk={handleSumit}
|
||||||
|
loading={loading}
|
||||||
showOkBtn={!showExtraButton}
|
showOkBtn={!showExtraButton}
|
||||||
extra={
|
extra={
|
||||||
showExtraButton && (
|
showExtraButton && (
|
||||||
@@ -472,6 +489,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
source={source}
|
source={source}
|
||||||
action={action}
|
action={action}
|
||||||
onOk={handleOk}
|
onOk={handleOk}
|
||||||
|
onFinishFailed={onFinishFailed}
|
||||||
ref={form}
|
ref={form}
|
||||||
isGGUF={isGGUF}
|
isGGUF={isGGUF}
|
||||||
formKey={DeployFormKeyMap.CATALOG}
|
formKey={DeployFormKeyMap.CATALOG}
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
const requestModelIdRef = useRef<number>(0);
|
const requestModelIdRef = useRef<number>(0);
|
||||||
const currentSelectedModel = useRef<any>({});
|
const currentSelectedModel = useRef<any>({});
|
||||||
const flatBackendOptionsRef = useRef<any[]>([]);
|
const flatBackendOptionsRef = useRef<any[]>([]);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const submitloadingRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const { run: fetchModelFiles } = useDeferredRequest(
|
const { run: fetchModelFiles } = useDeferredRequest(
|
||||||
() => modelFileRef.current?.fetchModelFiles?.(),
|
() => modelFileRef.current?.fetchModelFiles?.(),
|
||||||
@@ -422,22 +424,33 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOnOk = async (allValues: FormData) => {
|
const handleOnOk = async (allValues: FormData) => {
|
||||||
onOk(allValues);
|
setLoading(true);
|
||||||
|
await onOk(allValues);
|
||||||
|
setLoading(false);
|
||||||
|
submitloadingRef.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSumit = () => {
|
||||||
|
if (submitloadingRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitloadingRef.current = true;
|
||||||
|
form.current?.submit?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmitAnyway = async () => {
|
const handleSubmitAnyway = async () => {
|
||||||
submitAnyway.current = true;
|
submitAnyway.current = true;
|
||||||
form.current?.submit?.();
|
handleSumit();
|
||||||
};
|
|
||||||
|
|
||||||
const handleSumit = () => {
|
|
||||||
form.current?.submit?.();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetIsGGUF = async (flag: boolean) => {
|
const handleSetIsGGUF = async (flag: boolean) => {
|
||||||
setIsGGUF(flag);
|
setIsGGUF(flag);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onFinishFailed = () => {
|
||||||
|
submitloadingRef.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
const handleBackendChange = async (backend: string) => {
|
const handleBackendChange = async (backend: string) => {
|
||||||
const data = form.current.form.getFieldsValue?.();
|
const data = form.current.form.getFieldsValue?.();
|
||||||
const res = handleBackendChangeBefore(data);
|
const res = handleBackendChangeBefore(data);
|
||||||
@@ -660,6 +673,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
<ModalFooter
|
<ModalFooter
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
onOk={handleSumit}
|
onOk={handleSumit}
|
||||||
|
loading={loading}
|
||||||
showOkBtn={!showExtraButton}
|
showOkBtn={!showExtraButton}
|
||||||
extra={
|
extra={
|
||||||
showExtraButton && (
|
showExtraButton && (
|
||||||
@@ -691,6 +705,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
onOk={handleOnOk}
|
onOk={handleOnOk}
|
||||||
ref={form}
|
ref={form}
|
||||||
isGGUF={isGGUF}
|
isGGUF={isGGUF}
|
||||||
|
onFinishFailed={onFinishFailed}
|
||||||
onBackendChange={handleBackendChange}
|
onBackendChange={handleBackendChange}
|
||||||
onValuesChange={onValuesChange}
|
onValuesChange={onValuesChange}
|
||||||
clearCacheFormValues={clearCacheFormValues}
|
clearCacheFormValues={clearCacheFormValues}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { PageActionType } from '@/config/types';
|
|||||||
import { ColumnWrapper, GSDrawer, ModalFooter } from '@gpustack/core-ui';
|
import { ColumnWrapper, GSDrawer, ModalFooter } from '@gpustack/core-ui';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
DeployFormKeyMap,
|
DeployFormKeyMap,
|
||||||
DO_NOT_NOTIFY_RECREATE,
|
DO_NOT_NOTIFY_RECREATE,
|
||||||
@@ -29,7 +29,7 @@ type AddModalProps = {
|
|||||||
number,
|
number,
|
||||||
{ provider: string; state: string | number }
|
{ provider: string; state: string | number }
|
||||||
>[];
|
>[];
|
||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void | Promise<void>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,6 +60,8 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
const formRef = useRef<any>(null);
|
const formRef = useRef<any>(null);
|
||||||
const submitAnyway = useRef<boolean>(false);
|
const submitAnyway = useRef<boolean>(false);
|
||||||
const originFormData = useRef<any>(null);
|
const originFormData = useRef<any>(null);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const submitloadingRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const setOriginalFormData = () => {
|
const setOriginalFormData = () => {
|
||||||
if (!originFormData.current) {
|
if (!originFormData.current) {
|
||||||
@@ -157,12 +159,20 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSumit = () => {
|
const handleSumit = () => {
|
||||||
|
if (submitloadingRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitloadingRef.current = true;
|
||||||
formRef.current?.submit();
|
formRef.current?.submit();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmitAnyway = async () => {
|
const handleSubmitAnyway = async () => {
|
||||||
submitAnyway.current = true;
|
submitAnyway.current = true;
|
||||||
formRef.current?.submit?.();
|
handleSumit();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFinishFailed = () => {
|
||||||
|
submitloadingRef.current = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOk = async (formdata: FormData) => {
|
const handleOk = async (formdata: FormData) => {
|
||||||
@@ -182,7 +192,13 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
};
|
};
|
||||||
onOk(submitData);
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
await onOk(submitData);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
submitloadingRef.current = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleManulOnValuesChange = (changedValues: any, allValues: any) => {
|
const handleManulOnValuesChange = (changedValues: any, allValues: any) => {
|
||||||
@@ -266,6 +282,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
style={ModalFooterStyle}
|
style={ModalFooterStyle}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onOk={handleSumit}
|
onOk={handleSumit}
|
||||||
|
loading={loading}
|
||||||
></ModalFooter>
|
></ModalFooter>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@@ -278,6 +295,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
realAction={realAction}
|
realAction={realAction}
|
||||||
clusterList={clusterList}
|
clusterList={clusterList}
|
||||||
onOk={handleOk}
|
onOk={handleOk}
|
||||||
|
onFinishFailed={onFinishFailed}
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
isGGUF={isGGUF}
|
isGGUF={isGGUF}
|
||||||
onBackendChange={handleAsyncBackendChange}
|
onBackendChange={handleAsyncBackendChange}
|
||||||
|
|||||||
@@ -76,15 +76,15 @@ const SearchResult: React.FC<SearchResultProps> = (props) => {
|
|||||||
></IconFont>
|
></IconFont>
|
||||||
}
|
}
|
||||||
description={
|
description={
|
||||||
source === modelSourceMap.huggingface_value ? (
|
<div className="flex-column gap-5">
|
||||||
<div className="flex-column gap-5">
|
<span>
|
||||||
|
{intl.formatMessage({ id: 'models.search.networkerror' })}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
<span>
|
<span>
|
||||||
{intl.formatMessage({ id: 'models.search.networkerror' })}
|
{intl.formatMessage({ id: 'models.search.hfvisit' })}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
{source === modelSourceMap.huggingface_value ? (
|
||||||
<span>
|
|
||||||
{intl.formatMessage({ id: 'models.search.hfvisit' })}
|
|
||||||
</span>
|
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -93,9 +93,18 @@ const SearchResult: React.FC<SearchResultProps> = (props) => {
|
|||||||
>
|
>
|
||||||
Hugging Face
|
Hugging Face
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
) : (
|
||||||
</div>
|
<Button
|
||||||
) : null
|
type="link"
|
||||||
|
size="small"
|
||||||
|
href="https://modelscope.cn/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
ModelScope
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ interface DataFormProps {
|
|||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void;
|
||||||
onBackendChange?: (value: string) => void;
|
onBackendChange?: (value: string) => void;
|
||||||
onClusterChange?: (value: number) => void;
|
onClusterChange?: (value: number) => void;
|
||||||
|
onFinishFailed?: (errorInfo: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TABKeysMap = {
|
const TABKeysMap = {
|
||||||
@@ -91,6 +92,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
onSourceChange,
|
onSourceChange,
|
||||||
onValuesChange,
|
onValuesChange,
|
||||||
onClusterChange,
|
onClusterChange,
|
||||||
|
onFinishFailed,
|
||||||
onOk
|
onOk
|
||||||
} = props;
|
} = props;
|
||||||
const { getScrollElementScrollableHeight } = useWrapperContext();
|
const { getScrollElementScrollableHeight } = useWrapperContext();
|
||||||
@@ -285,6 +287,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
const handleOnFinishFailed = (errorInfo: any) => {
|
const handleOnFinishFailed = (errorInfo: any) => {
|
||||||
setSubmitAttempted(true);
|
setSubmitAttempted(true);
|
||||||
|
onFinishFailed?.(errorInfo);
|
||||||
console.log('Failed:', errorInfo);
|
console.log('Failed:', errorInfo);
|
||||||
const { errorFields } = errorInfo;
|
const { errorFields } = errorInfo;
|
||||||
if (errorFields && errorFields.length > 0) {
|
if (errorFields && errorFields.length > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user