style: gpu instance service
This commit is contained in:
@@ -4,10 +4,15 @@ import {
|
||||
PlusOutlined,
|
||||
QuestionCircleOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { Input as CInput, LabelSelector, useAppUtils } from '@gpustack/core-ui';
|
||||
import {
|
||||
Input as CInput,
|
||||
LabelSelector,
|
||||
SwitchCard,
|
||||
useAppUtils
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Form, Switch, Tooltip } from 'antd';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Button, Form, Tooltip } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { GpuInstanceOptions } from '../config/types';
|
||||
import K8SVolumeMount from './k8s-volume-mount';
|
||||
@@ -180,6 +185,7 @@ const NamespaceForm: React.FC = () => {
|
||||
<Form.Item
|
||||
name={['k8s_options', 'namespace']}
|
||||
style={{ marginBottom: 0 }}
|
||||
normalize={(value) => value || null}
|
||||
>
|
||||
<CInput.Input
|
||||
label={intl.formatMessage({ id: 'clusters.namespace.title' })}
|
||||
@@ -201,6 +207,7 @@ const OperatorImageForm: React.FC = () => {
|
||||
<Form.Item
|
||||
name={['k8s_options', 'operatorImage']}
|
||||
style={{ marginBottom: 0 }}
|
||||
normalize={(value) => value || null}
|
||||
>
|
||||
<CInput.Input
|
||||
label={intl.formatMessage({ id: 'clusters.operatorImage.title' })}
|
||||
@@ -211,92 +218,40 @@ const OperatorImageForm: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// GPU-instance support. The backend treats the mere presence of
|
||||
// `gpuInstanceOptions` as the enable flag, so the switch toggles the whole
|
||||
// object in/out of the form rather than setting a boolean field; the static
|
||||
// address (optional even when enabled) is nested underneath.
|
||||
//
|
||||
// We drive the toggle from local state (not Form.useWatch) because the
|
||||
// gpuInstanceOptions path has no registered Form.Item of its own — useWatch
|
||||
// doesn't reliably re-render on setFieldValue for such paths, which left the
|
||||
// switch unresponsive. Local state owns the visible state and we mirror it
|
||||
// into the form via setFieldValue so submit still collects it.
|
||||
const GpuInstanceOptionsForm: React.FC<{
|
||||
initialValue?: GpuInstanceOptions;
|
||||
}> = ({ initialValue }) => {
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance();
|
||||
const [enabled, setEnabled] = useState<boolean>(!!initialValue);
|
||||
const [address, setAddress] = useState<string>(
|
||||
initialValue?.gpuInstancesAccessStaticAddress || ''
|
||||
);
|
||||
const initializedRef = useRef<boolean>(!!initialValue);
|
||||
|
||||
const writeForm = (en: boolean, addr: string) => {
|
||||
form.setFieldValue(
|
||||
['k8s_options', 'gpuInstanceOptions'],
|
||||
en ? { gpuInstancesAccessStaticAddress: addr } : undefined
|
||||
);
|
||||
};
|
||||
|
||||
// Mirror a seeded initial value into the form on mount so submit collects it.
|
||||
useEffect(() => {
|
||||
if (initialValue) {
|
||||
writeForm(true, initialValue.gpuInstancesAccessStaticAddress || '');
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Adopt currentData arriving after mount (async edit load), once. After the
|
||||
// user has interacted (`initializedRef`), local state owns the section.
|
||||
useEffect(() => {
|
||||
if (initializedRef.current) return;
|
||||
if (initialValue) {
|
||||
setEnabled(true);
|
||||
setAddress(initialValue.gpuInstancesAccessStaticAddress || '');
|
||||
writeForm(true, initialValue.gpuInstancesAccessStaticAddress || '');
|
||||
initializedRef.current = true;
|
||||
}
|
||||
}, [initialValue]);
|
||||
|
||||
const handleToggle = (checked: boolean) => {
|
||||
initializedRef.current = true;
|
||||
setEnabled(checked);
|
||||
if (!checked) {
|
||||
setAddress('');
|
||||
}
|
||||
writeForm(checked, checked ? address : '');
|
||||
};
|
||||
|
||||
const handleAddressChange = (e: any) => {
|
||||
const next = typeof e === 'string' ? e : (e?.target?.value ?? '');
|
||||
setAddress(next);
|
||||
writeForm(true, next);
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionWrap>
|
||||
<Title>
|
||||
<div className="flex-center gap-8">
|
||||
<span className="flex-center gap-4">
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'clusters.gpuInstances.title' })}
|
||||
</span>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({ id: 'clusters.gpuInstances.tip' })}
|
||||
>
|
||||
<QuestionCircleOutlined
|
||||
style={{ color: 'var(--ant-color-text-secondary)' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</span>
|
||||
<Switch checked={enabled} onChange={handleToggle} />
|
||||
</div>
|
||||
</Title>
|
||||
{enabled && (
|
||||
<SwitchCard
|
||||
styles={{
|
||||
wrapper: {
|
||||
borderRadius: 'var(--ant-border-radius-lg)',
|
||||
paddingInline: 14,
|
||||
marginBottom: 24
|
||||
}
|
||||
}}
|
||||
value={enabled}
|
||||
onChange={handleToggle}
|
||||
label={intl.formatMessage({ id: 'clusters.gpuInstances.title' })}
|
||||
description={intl.formatMessage({ id: 'clusters.gpuInstances.tip' })}
|
||||
>
|
||||
<Form.Item
|
||||
name={[
|
||||
'k8s_options',
|
||||
'gpuInstanceOptions',
|
||||
'gpuInstancesAccessStaticAddress'
|
||||
]}
|
||||
normalize={(value) => value || null}
|
||||
noStyle
|
||||
>
|
||||
<CInput.Input
|
||||
isInFormItems={false}
|
||||
value={address}
|
||||
onChange={handleAddressChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'clusters.gpuInstances.staticAddress'
|
||||
})}
|
||||
@@ -304,8 +259,8 @@ const GpuInstanceOptionsForm: React.FC<{
|
||||
id: 'clusters.gpuInstances.staticAddress.tip'
|
||||
})}
|
||||
></CInput.Input>
|
||||
)}
|
||||
</SectionWrap>
|
||||
</Form.Item>
|
||||
</SwitchCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -315,12 +270,12 @@ const K8sPodSpec: React.FC<{
|
||||
}> = ({ action, initialGpuInstanceOptions }) => {
|
||||
return (
|
||||
<>
|
||||
<GpuInstanceOptionsForm initialValue={initialGpuInstanceOptions} />
|
||||
<NamespaceForm />
|
||||
<K8SVolumeMount action={action}></K8SVolumeMount>
|
||||
<ImageCredentialsForm />
|
||||
<NodeSelectorForm />
|
||||
<OperatorImageForm />
|
||||
<GpuInstanceOptionsForm initialValue={initialGpuInstanceOptions} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user