Skip to content

Commit

Permalink
refactor(dashboard): optimize components
Browse files Browse the repository at this point in the history
  • Loading branch information
HazimAr committed Nov 25, 2024
1 parent 25e86b1 commit ab25772
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 132 deletions.
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
})

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

0 comments on commit ab25772

Please sign in to comment.