Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(dashboard): optimize execute WfRun components #1159

Merged
merged 7 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,24 @@ export const WfRunForm = forwardRef<HTMLFormElement, Prop>(({ 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
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice


return (
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmitForm)} ref={ref}>
<form onSubmit={handleSubmit(onSubmitForm)} ref={ref} className="space-y-4">
<div>
<Label htmlFor="custom-id">Custom WfRun Id</Label>
<Input
type="text"
className="mb-4 mt-1"
id="custom-id"
{...register('custom-id-wfRun-flow')}
placeholder="Enter string value"
/>
<Label htmlFor="customWfRunId" className="mb-2 flex items-center gap-2">
Custom WfRun Id
<span className="rounded bg-gray-300 p-1 text-xs">Optional</span>
</Label>
<Input type="text" id="customWfRunId" {...register('customWfRunId')} placeholder="Enter string value" />
</div>
{!!wfSpecVariables?.length &&
wfSpecVariables.map((variable: ThreadVarDef) => (
{!!sortedVariables.length &&
sortedVariables.map((variable: ThreadVarDef) => (
<FormFields key={variable.varDef?.name} variables={variable} register={register} formState={formState} />
))}
</form>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<BaseFormFieldProps> = ({
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 (
<div>
<div className="mb-2 flex justify-between">
<Label htmlFor={name} className="flex items-center gap-2">
{name}
<span className="rounded bg-green-300 p-1 text-xs">{accessLevels[accessLevel]}</span>
{required ? (
<span className="rounded bg-red-300 p-1 text-xs">Required</span>
) : (
<span className="rounded bg-gray-300 p-1 text-xs">Optional</span>
)}
</Label>
{!required && (
<Button
type="button"
variant="outline"
size="sm"
onClick={isDisabled ? handleEnable : handleSetNull}
className="h-8"
>
{isDisabled ? 'Enter Value' : 'Set Null'}
</Button>
)}
</div>
{children}
{errors[name] && (
<p className="mt-2 flex items-center gap-1 text-sm text-destructive">
<CircleAlert size={16} />
{errors[name]?.message}
</p>
)}
</div>
)
}
Original file line number Diff line number Diff line change
@@ -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<FormFieldProp> = 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 (
<div>
<div className="mb-2 flex justify-between">
<Label htmlFor={name} className="flex items-center gap-2">
{name}
<span className="rounded bg-green-300 p-1 text-xs">{accessLevels[accessLevel]}</span>
{required ? (
<span className="rounded bg-red-300 p-1 text-xs">Required</span>
) : (
<span className="rounded bg-gray-300 p-1 text-xs">Optional</span>
)}
</Label>
</div>
<BaseFormField {...props} isDisabled={isDisabled} setIsDisabled={setIsDisabled}>
<Input
type={variableToType(type)}
className={errors[name] ? 'mb-1 mt-2 border-red-700' : 'mb-4 mt-1'}
type={type === VariableType.INT || type === VariableType.DOUBLE ? 'number' : 'text'}
className={cn(errors[name] && 'border-destructive')}
id={name}
step={type === VariableType.DOUBLE ? '0.01' : undefined}
disabled={isDisabled}
placeholder={`Enter ${VARIABLE_TYPES[type]?.toLowerCase()} value`}
{...register(name, {
required: required ? `${name} is required` : false,
setValueAs: setValue,
setValueAs: (value: string) => {
return type === VariableType.INT || type === VariableType.DOUBLE ? parseFloat(value) || undefined : value
},
})}
/>
{errors[name] && (
<p className="mb-3 flex items-center gap-1 text-sm text-red-700">
<CircleAlert size={16} />
{errors[name]?.message}
</p>
)}
</div>
</BaseFormField>
)
}
Original file line number Diff line number Diff line change
@@ -1,76 +1,53 @@
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<FormFieldProp> = 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

const {
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 (
<div>
<div className="mb-2 flex justify-between">
<Label htmlFor={name} className="center flex items-center gap-2">
{name}
<span className="rounded bg-green-300 p-1 text-xs">{accessLevels[accessLevel]}</span>
{required ? (
<span className="rounded bg-red-300 p-1 text-xs">Required</span>
) : (
<span className="rounded bg-gray-300 p-1 text-xs">Optional</span>
)}
</Label>
</div>
<BaseFormField {...props} isDisabled={isDisabled} setIsDisabled={setIsDisabled}>
<Select
value={value?.toString() || ''}
onValueChange={handleChange}
disabled={isDisabled}
{...register(name, { required: required ? `${name} is required` : false })}
>
<SelectTrigger id={name} className={errors[name] ? 'mb-1 mt-1 border-red-700' : 'mb-4 mt-1'}>
<SelectTrigger id={name} className={cn(props.formState.errors[name] && 'border-destructive')}>
<SelectValue placeholder="Select True or False" />
</SelectTrigger>
<SelectContent>
<SelectItem value="true">True</SelectItem>
<SelectItem value="false">False</SelectItem>
<SelectItem value="none">None</SelectItem>
</SelectContent>
</Select>
{errors[name]?.message && (
<p className="mb-3 flex items-center gap-1 text-sm text-red-700">
<CircleAlert size={16} />
{String(errors[name]?.message)}
</p>
)}
</div>
</BaseFormField>
)
}
Original file line number Diff line number Diff line change
@@ -1,63 +1,49 @@
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<FormFieldProp> = 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,
formState: { errors },
} = props

return (
<div>
<div className="flex justify-between">
<Label htmlFor={name} className="flex items-center gap-2">
{name}
<span className="rounded bg-green-300 p-1 text-xs">{accessLevels[accessLevel]}</span>
{required ? (
<span className="rounded bg-red-300 p-1 text-xs">Required</span>
) : (
<span className="rounded bg-gray-300 p-1 text-xs">Optional</span>
)}
</Label>
</div>
<BaseFormField {...props} isDisabled={isDisabled} setIsDisabled={setIsDisabled}>
<Textarea
className={errors[name] ? 'mb-1 mt-1 min-h-[120px] border-red-700' : 'mb-4 mt-1 rounded-xl'}
className={cn(errors[name] && 'border-destructive')}
id={name}
disabled={isDisabled}
placeholder={`Enter ${VARIABLE_TYPES[type]?.toLowerCase()} value`}
{...register(name, {
required: required ? `${name} is required` : false,
validate: getValidation(type),
onChange: e => {
setValue(name, e.target.value || undefined)
setValue(name, e.target.value)
trigger(name)
},
})}
defaultValue=""
/>
{errors[name] && (
<p className="mb-3 flex items-center gap-1 text-sm text-red-700">
<CircleAlert size={16} />
{errors[name]?.message}
</p>
)}
</div>
</BaseFormField>
)
}
Loading
Loading