feat: implement input field form with file upload settings and validation
parent
a2dc38f90a
commit
51165408ed
@ -0,0 +1,72 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { InputType } from '../types'
|
||||||
|
import { InputVarType } from '@/app/components/workflow/types'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
|
const i18nFileTypeMap: Record<string, string> = {
|
||||||
|
'file': 'single-file',
|
||||||
|
'file-list': 'multi-files',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useInputTypes = (supportFile: boolean) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const options = supportFile ? InputType.options : InputType.exclude(['file', 'file-list']).options
|
||||||
|
|
||||||
|
return options.map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: t(`appDebug.variableConfig.${i18nFileTypeMap[value] || value}`),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useHiddenFieldNames = (type: InputVarType) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const hiddenFieldNames = useMemo(() => {
|
||||||
|
let fieldNames = []
|
||||||
|
switch (type) {
|
||||||
|
case InputVarType.textInput:
|
||||||
|
case InputVarType.paragraph:
|
||||||
|
fieldNames = [
|
||||||
|
t('appDebug.variableConfig.defaultValue'),
|
||||||
|
t('appDebug.variableConfig.placeholder'),
|
||||||
|
t('appDebug.variableConfig.tooltips'),
|
||||||
|
]
|
||||||
|
break
|
||||||
|
case InputVarType.number:
|
||||||
|
fieldNames = [
|
||||||
|
t('appDebug.variableConfig.defaultValue'),
|
||||||
|
t('appDebug.variableConfig.unit'),
|
||||||
|
t('appDebug.variableConfig.placeholder'),
|
||||||
|
t('appDebug.variableConfig.tooltips'),
|
||||||
|
]
|
||||||
|
break
|
||||||
|
case InputVarType.select:
|
||||||
|
fieldNames = [
|
||||||
|
t('appDebug.variableConfig.defaultValue'),
|
||||||
|
t('appDebug.variableConfig.tooltips'),
|
||||||
|
]
|
||||||
|
break
|
||||||
|
case InputVarType.singleFile:
|
||||||
|
fieldNames = [
|
||||||
|
t('appDebug.variableConfig.uploadMethod'),
|
||||||
|
t('appDebug.variableConfig.tooltips'),
|
||||||
|
]
|
||||||
|
break
|
||||||
|
case InputVarType.multiFiles:
|
||||||
|
fieldNames = [
|
||||||
|
t('appDebug.variableConfig.uploadMethod'),
|
||||||
|
t('appDebug.variableConfig.maxNumberOfUploads'),
|
||||||
|
t('appDebug.variableConfig.tooltips'),
|
||||||
|
]
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
fieldNames = [
|
||||||
|
t('appDebug.variableConfig.tooltips'),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return fieldNames.map(name => name.toLowerCase()).join(', ')
|
||||||
|
}, [type, t])
|
||||||
|
|
||||||
|
return hiddenFieldNames
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { withForm } from '../../..'
|
||||||
|
import { type InputVar, SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||||
|
import { getNewVarInWorkflow } from '@/utils/var'
|
||||||
|
import { useField } from '@tanstack/react-form'
|
||||||
|
import Label from '../../../components/label'
|
||||||
|
import FileTypeItem from '@/app/components/workflow/nodes/_base/components/file-type-item'
|
||||||
|
import { useCallback, useMemo } from 'react'
|
||||||
|
|
||||||
|
type FileTypesFieldsProps = {
|
||||||
|
initialData?: InputVar
|
||||||
|
}
|
||||||
|
|
||||||
|
const UseFileTypesFields = ({
|
||||||
|
initialData,
|
||||||
|
}: FileTypesFieldsProps) => {
|
||||||
|
const FileTypesFields = useMemo(() => {
|
||||||
|
return withForm({
|
||||||
|
defaultValues: initialData || getNewVarInWorkflow(''),
|
||||||
|
render: function Render({
|
||||||
|
form,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const allowFileTypesField = useField({ form, name: 'allowed_file_types' })
|
||||||
|
const allowFileExtensionsField = useField({ form, name: 'allowed_file_extensions' })
|
||||||
|
const { value: allowed_file_types = [] } = allowFileTypesField.state
|
||||||
|
const { value: allowed_file_extensions = [] } = allowFileExtensionsField.state
|
||||||
|
|
||||||
|
const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => {
|
||||||
|
let newAllowFileTypes = [...allowed_file_types]
|
||||||
|
if (type === SupportUploadFileTypes.custom) {
|
||||||
|
if (!newAllowFileTypes.includes(SupportUploadFileTypes.custom))
|
||||||
|
newAllowFileTypes = [SupportUploadFileTypes.custom]
|
||||||
|
else
|
||||||
|
newAllowFileTypes = newAllowFileTypes.filter(v => v !== type)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newAllowFileTypes = newAllowFileTypes.filter(v => v !== SupportUploadFileTypes.custom)
|
||||||
|
if (newAllowFileTypes.includes(type))
|
||||||
|
newAllowFileTypes = newAllowFileTypes.filter(v => v !== type)
|
||||||
|
else
|
||||||
|
newAllowFileTypes.push(type)
|
||||||
|
}
|
||||||
|
allowFileTypesField.handleChange(newAllowFileTypes)
|
||||||
|
}, [allowFileTypesField, allowed_file_types])
|
||||||
|
|
||||||
|
const handleCustomFileTypesChange = useCallback((customFileTypes: string[]) => {
|
||||||
|
allowFileExtensionsField.handleChange(customFileTypes)
|
||||||
|
}, [allowFileExtensionsField])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-y-0.5'>
|
||||||
|
<Label
|
||||||
|
htmlFor='allowed_file_types'
|
||||||
|
label={t('appDebug.variableConfig.file.supportFileTypes')}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
[SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
|
||||||
|
<FileTypeItem
|
||||||
|
key={type}
|
||||||
|
type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
|
||||||
|
selected={allowed_file_types.includes(type)}
|
||||||
|
onToggle={handleSupportFileTypeChange}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
<FileTypeItem
|
||||||
|
type={SupportUploadFileTypes.custom}
|
||||||
|
selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
|
||||||
|
onToggle={handleSupportFileTypeChange}
|
||||||
|
customFileTypes={allowed_file_extensions}
|
||||||
|
onCustomFileTypesChange={handleCustomFileTypesChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}, [initialData])
|
||||||
|
|
||||||
|
return FileTypesFields
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UseFileTypesFields
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { withForm } from '../../..'
|
||||||
|
import type { InputVar } from '@/app/components/workflow/types'
|
||||||
|
import { getNewVarInWorkflow } from '@/utils/var'
|
||||||
|
import { useField } from '@tanstack/react-form'
|
||||||
|
import Label from '../../../components/label'
|
||||||
|
import { useCallback, useMemo } from 'react'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
import { fetchFileUploadConfig } from '@/service/common'
|
||||||
|
import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks'
|
||||||
|
import { formatFileSize } from '@/utils/format'
|
||||||
|
import InputNumberWithSlider from '@/app/components/workflow/nodes/_base/components/input-number-with-slider'
|
||||||
|
|
||||||
|
type MaxNumberOfUploadsFieldProps = {
|
||||||
|
initialData?: InputVar
|
||||||
|
}
|
||||||
|
|
||||||
|
const UseMaxNumberOfUploadsField = ({
|
||||||
|
initialData,
|
||||||
|
}: MaxNumberOfUploadsFieldProps) => {
|
||||||
|
const MaxNumberOfUploadsField = useMemo(() => {
|
||||||
|
return withForm({
|
||||||
|
defaultValues: initialData || getNewVarInWorkflow(''),
|
||||||
|
render: function Render({
|
||||||
|
form,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const maxNumberOfUploadsField = useField({ form, name: 'max_length' })
|
||||||
|
const { value: max_length = 0 } = maxNumberOfUploadsField.state
|
||||||
|
|
||||||
|
const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
|
||||||
|
const {
|
||||||
|
imgSizeLimit,
|
||||||
|
docSizeLimit,
|
||||||
|
audioSizeLimit,
|
||||||
|
videoSizeLimit,
|
||||||
|
maxFileUploadLimit,
|
||||||
|
} = useFileSizeLimit(fileUploadConfigResponse)
|
||||||
|
|
||||||
|
const handleMaxUploadNumLimitChange = useCallback((value: number) => {
|
||||||
|
maxNumberOfUploadsField.handleChange(value)
|
||||||
|
}, [maxNumberOfUploadsField])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-y-0.5'>
|
||||||
|
<Label
|
||||||
|
htmlFor='allowed_file_types'
|
||||||
|
label={t('appDebug.variableConfig.maxNumberOfUploads')}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<div className='body-xs-regular mb-1.5 text-text-tertiary'>
|
||||||
|
{t('appDebug.variableConfig.maxNumberTip', {
|
||||||
|
imgLimit: formatFileSize(imgSizeLimit),
|
||||||
|
docLimit: formatFileSize(docSizeLimit),
|
||||||
|
audioLimit: formatFileSize(audioSizeLimit),
|
||||||
|
videoLimit: formatFileSize(videoSizeLimit),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<InputNumberWithSlider
|
||||||
|
value={max_length}
|
||||||
|
min={1}
|
||||||
|
max={maxFileUploadLimit}
|
||||||
|
onChange={handleMaxUploadNumLimitChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}, [initialData])
|
||||||
|
|
||||||
|
return MaxNumberOfUploadsField
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UseMaxNumberOfUploadsField
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { withForm } from '../../..'
|
||||||
|
import type { InputVar } from '@/app/components/workflow/types'
|
||||||
|
import { getNewVarInWorkflow } from '@/utils/var'
|
||||||
|
import { useField } from '@tanstack/react-form'
|
||||||
|
import Label from '../../../components/label'
|
||||||
|
import { useCallback, useMemo } from 'react'
|
||||||
|
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
|
||||||
|
import { TransferMethod } from '@/types/app'
|
||||||
|
|
||||||
|
type UploadMethodFieldProps = {
|
||||||
|
initialData?: InputVar
|
||||||
|
}
|
||||||
|
|
||||||
|
const UseUploadMethodField = ({
|
||||||
|
initialData,
|
||||||
|
}: UploadMethodFieldProps) => {
|
||||||
|
const UploadMethodField = useMemo(() => {
|
||||||
|
return withForm({
|
||||||
|
defaultValues: initialData || getNewVarInWorkflow(''),
|
||||||
|
render: function Render({
|
||||||
|
form,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const allowFileUploadMethodField = useField({ form, name: 'allowed_file_upload_methods' })
|
||||||
|
const { value: allowed_file_upload_methods = [] } = allowFileUploadMethodField.state
|
||||||
|
|
||||||
|
const handleUploadMethodChange = useCallback((method: TransferMethod) => {
|
||||||
|
allowFileUploadMethodField.handleChange(method === TransferMethod.all ? [TransferMethod.local_file, TransferMethod.remote_url] : [method])
|
||||||
|
}, [allowFileUploadMethodField])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-y-0.5'>
|
||||||
|
<Label
|
||||||
|
htmlFor='allowed_file_types'
|
||||||
|
label={t('appDebug.variableConfig.uploadFileTypes')}
|
||||||
|
/>
|
||||||
|
<div className='grid grid-cols-3 gap-2'>
|
||||||
|
<OptionCard
|
||||||
|
title={t('appDebug.variableConfig.localUpload')}
|
||||||
|
selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.local_file)}
|
||||||
|
onSelect={handleUploadMethodChange.bind(null, TransferMethod.local_file)}
|
||||||
|
/>
|
||||||
|
<OptionCard
|
||||||
|
title="URL"
|
||||||
|
selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
|
||||||
|
onSelect={handleUploadMethodChange.bind(null, TransferMethod.remote_url)}
|
||||||
|
/>
|
||||||
|
<OptionCard
|
||||||
|
title={t('appDebug.variableConfig.both')}
|
||||||
|
selected={allowed_file_upload_methods.includes(TransferMethod.local_file) && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
|
||||||
|
onSelect={handleUploadMethodChange.bind(null, TransferMethod.all)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}, [initialData])
|
||||||
|
|
||||||
|
return UploadMethodField
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UseUploadMethodField
|
||||||
@ -0,0 +1,268 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useAppForm } from '../..'
|
||||||
|
import type { InputFieldFormProps } from './types'
|
||||||
|
import { getNewVarInWorkflow } from '@/utils/var'
|
||||||
|
import { useHiddenFieldNames, useInputTypes } from './hooks'
|
||||||
|
import Divider from '../../../divider'
|
||||||
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
|
import { useStore } from '@tanstack/react-form'
|
||||||
|
import { InputVarType } from '@/app/components/workflow/types'
|
||||||
|
import ShowAllSettings from './show-all-settings'
|
||||||
|
import Button from '../../../button'
|
||||||
|
import UseFileTypesFields from './hooks/use-file-types-fields'
|
||||||
|
import UseUploadMethodField from './hooks/use-upload-method-field'
|
||||||
|
import UseMaxNumberOfUploadsField from './hooks/use-max-number-of-uploads-filed'
|
||||||
|
import { DEFAULT_FILE_UPLOAD_SETTING } from '@/app/components/workflow/constants'
|
||||||
|
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
|
||||||
|
|
||||||
|
const InputFieldForm = ({
|
||||||
|
initialData,
|
||||||
|
supportFile = false,
|
||||||
|
onCancel,
|
||||||
|
onSubmit,
|
||||||
|
}: InputFieldFormProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const form = useAppForm({
|
||||||
|
defaultValues: initialData || getNewVarInWorkflow(''),
|
||||||
|
validators: {
|
||||||
|
onSubmit: ({ value }) => {
|
||||||
|
// TODO: Add validation logic here
|
||||||
|
console.log('Validator form on submit:', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onSubmit: ({ value }) => {
|
||||||
|
// TODO: Add submit logic here
|
||||||
|
onSubmit(value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [showAllSettings, setShowAllSettings] = useState(false)
|
||||||
|
const type = useStore(form.store, state => state.values.type)
|
||||||
|
const options = useStore(form.store, state => state.values.options)
|
||||||
|
const hiddenFieldNames = useHiddenFieldNames(type)
|
||||||
|
const inputTypes = useInputTypes(supportFile)
|
||||||
|
|
||||||
|
const FileTypesFields = UseFileTypesFields({ initialData })
|
||||||
|
const UploadMethodField = UseUploadMethodField({ initialData })
|
||||||
|
const MaxNumberOfUploads = UseMaxNumberOfUploadsField({ initialData })
|
||||||
|
|
||||||
|
const isTextInput = [InputVarType.textInput, InputVarType.paragraph].includes(type)
|
||||||
|
const isNumberInput = type === InputVarType.number
|
||||||
|
const isSelectInput = type === InputVarType.select
|
||||||
|
const isSingleFile = type === InputVarType.singleFile
|
||||||
|
const isMultipleFile = type === InputVarType.multiFiles
|
||||||
|
|
||||||
|
const defaultSelectOptions = useMemo(() => {
|
||||||
|
if (isSelectInput && options) {
|
||||||
|
const defaultOptions = [
|
||||||
|
{
|
||||||
|
value: '',
|
||||||
|
label: t('appDebug.variableConfig.noDefaultSelected'),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const otherOptions = options.map((option: string) => ({
|
||||||
|
value: option,
|
||||||
|
label: option,
|
||||||
|
}))
|
||||||
|
return [...defaultOptions, ...otherOptions]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}, [isSelectInput, options, t])
|
||||||
|
|
||||||
|
const handleTypeChange = useCallback((type: string) => {
|
||||||
|
if ([InputVarType.singleFile, InputVarType.multiFiles].includes(type as InputVarType)) {
|
||||||
|
(Object.keys(DEFAULT_FILE_UPLOAD_SETTING)).forEach((key) => {
|
||||||
|
if (key !== 'max_length')
|
||||||
|
form.setFieldValue(key as keyof typeof form.options.defaultValues, (DEFAULT_FILE_UPLOAD_SETTING as any)[key])
|
||||||
|
})
|
||||||
|
if (type === InputVarType.multiFiles)
|
||||||
|
form.setFieldValue('max_length', DEFAULT_FILE_UPLOAD_SETTING.max_length)
|
||||||
|
}
|
||||||
|
if (type === InputVarType.paragraph)
|
||||||
|
form.setFieldValue('max_length', DEFAULT_VALUE_MAX_LEN)
|
||||||
|
}, [form])
|
||||||
|
|
||||||
|
const handleShowAllSettings = useCallback(() => {
|
||||||
|
setShowAllSettings(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
className='w-full'
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
form.handleSubmit()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col gap-4 px-4 py-2'>
|
||||||
|
<form.AppField
|
||||||
|
name='type'
|
||||||
|
children={field => (
|
||||||
|
<field.SelectField
|
||||||
|
label={t('appDebug.variableConfig.fieldType')}
|
||||||
|
options={inputTypes}
|
||||||
|
onChange={handleTypeChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<form.AppField
|
||||||
|
name='variable'
|
||||||
|
children={field => (
|
||||||
|
<field.TextField
|
||||||
|
label={t('appDebug.variableConfig.varName')}
|
||||||
|
placeholder={t('appDebug.variableConfig.inputPlaceholder')!}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<form.AppField
|
||||||
|
name='label'
|
||||||
|
children={field => (
|
||||||
|
<field.TextField
|
||||||
|
label={t('appDebug.variableConfig.labelName')}
|
||||||
|
placeholder={t('appDebug.variableConfig.inputPlaceholder')!}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{isTextInput && (
|
||||||
|
<form.AppField
|
||||||
|
name='max_length'
|
||||||
|
children={field => (
|
||||||
|
<field.NumberInputField
|
||||||
|
label={t('appDebug.variableConfig.maxLength')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isSelectInput && (
|
||||||
|
<form.AppField
|
||||||
|
name='options'
|
||||||
|
listeners={{
|
||||||
|
onChange: () => {
|
||||||
|
form.setFieldValue('default', '')
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
children={field => (
|
||||||
|
<field.OptionsField
|
||||||
|
label={t('appDebug.variableConfig.options')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(isSingleFile || isMultipleFile) && (
|
||||||
|
<FileTypesFields form={form} />
|
||||||
|
)}
|
||||||
|
<form.AppField
|
||||||
|
name='required'
|
||||||
|
children={field => (
|
||||||
|
<field.CheckboxField
|
||||||
|
label={t('appDebug.variableConfig.required')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Divider type='horizontal' />
|
||||||
|
{!showAllSettings && (
|
||||||
|
<ShowAllSettings
|
||||||
|
handleShowAllSettings={handleShowAllSettings}
|
||||||
|
description={hiddenFieldNames}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showAllSettings && (
|
||||||
|
<>
|
||||||
|
{isTextInput && (
|
||||||
|
<form.AppField
|
||||||
|
name='default'
|
||||||
|
children={field => (
|
||||||
|
<field.TextField
|
||||||
|
label={t('appDebug.variableConfig.defaultValue')}
|
||||||
|
placeholder={t('appDebug.variableConfig.defaultValuePlaceholder')!}
|
||||||
|
showOptional
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isNumberInput && (
|
||||||
|
<form.AppField
|
||||||
|
name='default'
|
||||||
|
children={field => (
|
||||||
|
<field.NumberInputField
|
||||||
|
label={t('appDebug.variableConfig.defaultValue')}
|
||||||
|
placeholder={t('appDebug.variableConfig.defaultValuePlaceholder')!}
|
||||||
|
showOptional
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isSelectInput && (
|
||||||
|
<form.AppField
|
||||||
|
name='default'
|
||||||
|
children={field => (
|
||||||
|
<field.SelectField
|
||||||
|
label={t('appDebug.variableConfig.startSelectedOption')}
|
||||||
|
options={defaultSelectOptions}
|
||||||
|
showOptional
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(isTextInput || isNumberInput) && (
|
||||||
|
<form.AppField
|
||||||
|
name='placeholder'
|
||||||
|
children={field => (
|
||||||
|
<field.TextField
|
||||||
|
label={t('appDebug.variableConfig.placeholder')}
|
||||||
|
placeholder={t('appDebug.variableConfig.placeholderPlaceholder')!}
|
||||||
|
showOptional
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isNumberInput && (
|
||||||
|
<form.AppField
|
||||||
|
name='unit'
|
||||||
|
children={field => (
|
||||||
|
<field.TextField
|
||||||
|
label={t('appDebug.variableConfig.unit')}
|
||||||
|
placeholder={t('appDebug.variableConfig.unitPlaceholder')!}
|
||||||
|
showOptional
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(isSingleFile || isMultipleFile) && (
|
||||||
|
<UploadMethodField form={form} />
|
||||||
|
)}
|
||||||
|
{isMultipleFile && (
|
||||||
|
<MaxNumberOfUploads form={form} />
|
||||||
|
)}
|
||||||
|
<form.AppField
|
||||||
|
name='hint'
|
||||||
|
children={(field) => {
|
||||||
|
return (
|
||||||
|
<field.TextField
|
||||||
|
label={t('appDebug.variableConfig.tooltips')}
|
||||||
|
placeholder={t('appDebug.variableConfig.tooltipsPlaceholder')!}
|
||||||
|
showOptional
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center justify-end gap-x-2 p-4 pt-2'>
|
||||||
|
<Button variant='secondary' onClick={onCancel}>
|
||||||
|
{t('common.operation.cancel')}
|
||||||
|
</Button>
|
||||||
|
<form.AppForm>
|
||||||
|
<form.SubmitButton variant='primary'>
|
||||||
|
{t('common.operation.save')}
|
||||||
|
</form.SubmitButton>
|
||||||
|
</form.AppForm>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InputFieldForm
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { RiArrowRightSLine } from '@remixicon/react'
|
||||||
|
|
||||||
|
type ShowAllSettingsProps = {
|
||||||
|
description: string
|
||||||
|
handleShowAllSettings: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShowAllSettings = ({
|
||||||
|
description,
|
||||||
|
handleShowAllSettings,
|
||||||
|
}: ShowAllSettingsProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex cursor-pointer items-center gap-x-4' onClick={handleShowAllSettings}>
|
||||||
|
<div className='flex grow flex-col'>
|
||||||
|
<span className='system-sm-medium flex min-h-6 items-center text-text-secondary'>
|
||||||
|
{t('appDebug.variableConfig.showAllSettings')}
|
||||||
|
</span>
|
||||||
|
<span className='body-xs-regular pb-0.5 text-text-tertiary first-letter:capitalize'>
|
||||||
|
{description}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<RiArrowRightSLine className='h-4 w-4 shrink-0 text-text-secondary' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ShowAllSettings
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
import type { InputVar } from '@/app/components/workflow/types'
|
||||||
|
import type { TFunction } from 'i18next'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export const InputType = z.enum([
|
||||||
|
'text-input',
|
||||||
|
'paragraph',
|
||||||
|
'number',
|
||||||
|
'select',
|
||||||
|
'checkbox',
|
||||||
|
'file',
|
||||||
|
'file-list',
|
||||||
|
])
|
||||||
|
|
||||||
|
const TransferMethod = z.enum([
|
||||||
|
'all',
|
||||||
|
'local_file',
|
||||||
|
'remote_url',
|
||||||
|
])
|
||||||
|
|
||||||
|
const SupportedFileTypes = z.enum([
|
||||||
|
'image',
|
||||||
|
'document',
|
||||||
|
'video',
|
||||||
|
'audio',
|
||||||
|
'custom',
|
||||||
|
])
|
||||||
|
|
||||||
|
// TODO: Add validation rules
|
||||||
|
export const createInputFieldSchema = (t: TFunction) => z.object({
|
||||||
|
type: InputType,
|
||||||
|
label: z.string(),
|
||||||
|
variable: z.string(),
|
||||||
|
max_length: z.number().optional(),
|
||||||
|
default: z.string().optional(),
|
||||||
|
required: z.boolean(),
|
||||||
|
hint: z.string().optional(),
|
||||||
|
options: z.array(z.string()).optional(),
|
||||||
|
allowed_file_upload_methods: z.array(TransferMethod),
|
||||||
|
allowed_file_types: z.array(SupportedFileTypes),
|
||||||
|
allowed_file_extensions: z.string().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type InputFieldFormProps = {
|
||||||
|
initialData?: InputVar
|
||||||
|
supportFile?: boolean
|
||||||
|
onCancel: () => void
|
||||||
|
onSubmit: (value: InputVar) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TextFieldsProps = {
|
||||||
|
initialData?: InputVar
|
||||||
|
}
|
||||||
@ -1,11 +1,20 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import DemoForm from '../components/base/form/form-scenarios/demo'
|
import InputFieldForm from '../components/base/form/form-scenarios/input-field'
|
||||||
|
// import DemoForm from '../components/base/form/form-scenarios/demo'
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<div className='flex h-screen w-full items-center justify-center p-20'>
|
<div className='flex h-screen w-full items-center justify-center p-20'>
|
||||||
<DemoForm />
|
<div className='w-[400px] rounded-lg border border-gray-800 bg-components-panel-bg'>
|
||||||
|
<InputFieldForm
|
||||||
|
initialData={undefined}
|
||||||
|
supportFile
|
||||||
|
onCancel={() => { console.log('cancel') }}
|
||||||
|
onSubmit={value => console.log('submit', value)}
|
||||||
|
/>
|
||||||
|
{/* <DemoForm /> */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue