diff --git a/ui/src/app/jobs/new/page.tsx b/ui/src/app/jobs/new/page.tsx index e5247da1..0633f31e 100644 --- a/ui/src/app/jobs/new/page.tsx +++ b/ui/src/app/jobs/new/page.tsx @@ -28,6 +28,7 @@ export default function TrainingForm() { const { datasets, status: datasetFetchStatus } = useDatasetList(); const [datasetOptions, setDatasetOptions] = useState<{ value: string; label: string }[]>([]); + const [jobConfig, setJobConfig] = useNestedState(objectCopy(defaultJobConfig)); const [status, setStatus] = useState<'idle' | 'saving' | 'success' | 'error'>('idle'); @@ -54,6 +55,7 @@ export default function TrainingForm() { .then(data => { setGpuIDs(data.gpu_ids); setJobConfig(JSON.parse(data.job_config)); + // setJobConfig(data.name, 'config.name'); }) .catch(error => console.error('Error fetching training:', error)); } @@ -111,6 +113,8 @@ export default function TrainingForm() { saveJob(); }; + console.log('jobConfig.config.process[0].network.linear', jobConfig?.config?.process[0].network?.linear); + return ( <> @@ -142,6 +146,7 @@ export default function TrainingForm() { value={jobConfig.config.name} onChange={value => setJobConfig(value, 'config.name')} placeholder="Enter training name" + disabled={runId !== null} required /> - {jobConfig.config.process[0].network?.linear && ( + {jobConfig.config.process[0].network?.type && ( { + console.log('onChange', value); setJobConfig(value, 'config.process[0].network.linear'); setJobConfig(value, 'config.process[0].network.linear_alpha'); }} placeholder="eg. 16" - min={1} + min={0} max={1024} required /> diff --git a/ui/src/components/formInputs.tsx b/ui/src/components/formInputs.tsx index 09afd0d0..64e1cf92 100644 --- a/ui/src/components/formInputs.tsx +++ b/ui/src/components/formInputs.tsx @@ -16,20 +16,25 @@ export interface TextInputProps extends InputProps { value: string; onChange: (value: string) => void; type?: 'text' | 'password'; + disabled?: boolean; } export const TextInput = (props: TextInputProps) => { - const { label, value, onChange, placeholder, required } = props; + const { label, value, onChange, placeholder, required, disabled } = props; return (
{label && } onChange(e.target.value)} - className={inputClasses} + onChange={e => { + if (disabled) return; + onChange(e.target.value); + }} + className={`${inputClasses} ${disabled && 'opacity-30 cursor-not-allowed'}`} placeholder={placeholder} required={required} + disabled={disabled} />
); @@ -50,10 +55,10 @@ export const NumberInput = (props: NumberInputProps) => { { + onChange={e => { // Use parseFloat instead of Number to properly handle decimal values const rawValue = e.target.value; - + // Special handling for empty or partial inputs if (rawValue === '' || rawValue === '-' || rawValue === '.') { // For empty or partial inputs (like just a minus sign or decimal point), @@ -62,18 +67,18 @@ export const NumberInput = (props: NumberInputProps) => { onChange(0); return; } - - let value = parseFloat(rawValue); - + + let value = Number(rawValue); + // Handle NaN cases if (isNaN(value)) { value = 0; } - + // Apply min/max constraints only for valid numbers if (min !== undefined && value < min) value = min; if (max !== undefined && value > max) value = max; - + onChange(value); }} className={inputClasses} diff --git a/ui/src/utils/hooks.tsx b/ui/src/utils/hooks.tsx index d78dcac8..a3a66b12 100644 --- a/ui/src/utils/hooks.tsx +++ b/ui/src/utils/hooks.tsx @@ -77,6 +77,10 @@ export function useNestedState(initialState: T): [T, (value: any, path?: stri const [state, setState] = React.useState(initialState); const setValue = React.useCallback((value: any, path?: string) => { + if (path === undefined) { + setState(value); + return + } setState(prevState => setNestedValue(prevState, value, path)); }, []);