diff --git a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/WfRunForm.tsx b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/WfRunForm.tsx index b4663c731..9a76c8bb2 100644 --- a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/WfRunForm.tsx +++ b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/WfRunForm.tsx @@ -23,21 +23,24 @@ export const WfRunForm = forwardRef(({ wfSpecVariables, o onSubmit(data) } + // Sort variables so required fields come first + const sortedVariables = wfSpecVariables.sort((a, b) => { + if (a.required === b.required) return 0 + return a.required ? -1 : 1 + }) + return ( -
+
- - + +
- {!!wfSpecVariables?.length && - wfSpecVariables.map((variable: ThreadVarDef) => ( + {!!sortedVariables.length && + sortedVariables.map((variable: ThreadVarDef) => ( ))} diff --git a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/BaseFormField.tsx b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/BaseFormField.tsx new file mode 100644 index 000000000..9d4a96f28 --- /dev/null +++ b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/BaseFormField.tsx @@ -0,0 +1,75 @@ +import { Label } from '@/components/ui/label' +import { FormFieldProp } from '@/types' +import { CircleAlert } from 'lucide-react' +import { FC, ReactNode } from 'react' +import { useFormContext } from 'react-hook-form' +import { accessLevels } from '../../../wfSpec/[...props]/components/Variables' +import { Button } from '@/components/ui/button' + +type BaseFormFieldProps = FormFieldProp & { + children: ReactNode + isDisabled: boolean + setIsDisabled: (disabled: boolean) => void +} + +export const BaseFormField: FC = ({ + variables, + formState: { errors }, + children, + isDisabled, + setIsDisabled, +}) => { + const { setValue, trigger } = useFormContext() + + if (!variables?.varDef?.name) return null + + const { + varDef: { name }, + required, + accessLevel, + } = variables + + const handleSetNull = () => { + setValue(name, null) + trigger(name) + setIsDisabled(true) + } + + const handleEnable = () => { + setIsDisabled(false) + } + + return ( +
+
+ + {!required && ( + + )} +
+ {children} + {errors[name] && ( +

+ + {errors[name]?.message} +

+ )} +
+ ) +} diff --git a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormInput.tsx b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormInput.tsx index e2ba9a178..804552b10 100644 --- a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormInput.tsx +++ b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormInput.tsx @@ -1,77 +1,49 @@ import { VARIABLE_TYPES } from '@/app/constants' import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' +import { cn } from '@/components/utils' import { FormFieldProp } from '@/types' import { VariableType } from 'littlehorse-client/proto' -import { CircleAlert } from 'lucide-react' -import { FC, useState } from 'react' -import { accessLevels } from '../../../wfSpec/[...props]/components/Variables' +import { FC, useEffect, useState } from 'react' +import { useFormContext } from 'react-hook-form' +import { BaseFormField } from './BaseFormField' export const FormInput: FC = props => { - const [isDisabled, setIsDisabled] = useState(false) + const [isDisabled, setIsDisabled] = useState(!props.variables?.required) + const { setValue } = useFormContext() + + useEffect(() => { + if (!props.variables?.required && props.variables?.varDef?.name) { + setValue(props.variables.varDef.name, null) + } + }, [props.variables, setValue]) + if (!props.variables?.varDef?.name) return null const { variables: { varDef: { type, name }, required, - accessLevel, }, register, formState: { errors }, } = props - const variableToType = (variable: VariableType) => { - switch (variable) { - case VariableType.INT: - return 'number' - case VariableType.DOUBLE: - return 'number' - case VariableType.BYTES: - return 'number' - case VariableType.STR: - return 'text' - default: - return 'text' - } - } - - const setValue = (value: number | string) => { - if (value === null) return value - return variableToType(type) === 'number' ? parseFloat(value?.toString()) || undefined : value || undefined - } - return ( -
-
- -
+ { + return type === VariableType.INT || type === VariableType.DOUBLE ? parseFloat(value) || undefined : value + }, })} /> - {errors[name] && ( -

- - {errors[name]?.message} -

- )} -
+ ) } diff --git a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormSelect.tsx b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormSelect.tsx index c3ba897fa..daf2d3505 100644 --- a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormSelect.tsx +++ b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormSelect.tsx @@ -1,20 +1,19 @@ -import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { FormFieldProp } from '@/types' -import { CircleAlert } from 'lucide-react' -import { FC, useState } from 'react' +import { FC, useEffect, useState } from 'react' import { useFormContext } from 'react-hook-form' -import { accessLevels } from '../../../wfSpec/[...props]/components/Variables' +import { BaseFormField } from './BaseFormField' +import { cn } from '@/components/utils' export const FormSelect: FC = props => { - const { - register, - setValue, - formState: { errors }, - getValues, - trigger, - } = useFormContext() - const [isDisabled, setIsDisabled] = useState(false) + const [isDisabled, setIsDisabled] = useState(!props.variables?.required) + const { register, setValue, getValues, trigger } = useFormContext() + + useEffect(() => { + if (!props.variables?.required && props.variables?.varDef?.name) { + setValue(props.variables.varDef.name, null) + } + }, [props.variables, setValue]) if (!props.variables?.varDef?.name) return null @@ -22,55 +21,33 @@ export const FormSelect: FC = props => { variables: { varDef: { name }, required, - accessLevel, }, } = props const handleChange = (value: string) => { - if (value === 'none') setValue(name, undefined) - else { - const booleanValue = value === 'true' - setValue(name, booleanValue) - } + const booleanValue = value === 'true' + setValue(name, booleanValue) trigger(name) } const value = getValues(name) return ( -
-
- -
+ - {errors[name]?.message && ( -

- - {String(errors[name]?.message)} -

- )} -
+ ) } diff --git a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormTextarea.tsx b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormTextarea.tsx index adebb0dce..336def7bb 100644 --- a/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormTextarea.tsx +++ b/dashboard/src/app/(authenticated)/(diagram)/components/Forms/components/FormTextarea.tsx @@ -1,23 +1,26 @@ -import { Label } from '@/components/ui/label' +import { VARIABLE_TYPES } from '@/app/constants' import { Textarea } from '@/components/ui/textarea' +import { cn } from '@/components/utils' import { FormFieldProp } from '@/types' -import { FC, useState } from 'react' - -import { VARIABLE_TYPES } from '@/app/constants' -import { CircleAlert } from 'lucide-react' +import { FC, useEffect, useState } from 'react' import { useFormContext } from 'react-hook-form' -import { accessLevels } from '../../../wfSpec/[...props]/components/Variables' +import { BaseFormField } from './BaseFormField' import { getValidation } from './validation' export const FormTextarea: FC = props => { - const [isDisabled, setIsDisabled] = useState(false) + const [isDisabled, setIsDisabled] = useState(!props.variables?.required) const { setValue, trigger } = useFormContext() - if (!props.variables?.varDef?.name) return + useEffect(() => { + if (!props.variables?.required && props.variables?.varDef?.name) { + setValue(props.variables.varDef.name, null) + } + }, [props.variables, setValue]) + + if (!props.variables?.varDef?.name) return null const { variables: { varDef: { type, name }, - accessLevel, required, }, register, @@ -25,20 +28,9 @@ export const FormTextarea: FC = props => { } = props return ( -
-
- -
+