dify/web/app/components/datasets/external-api/external-api-modal/Form.tsx
Stephen Zhou 0564b75187
Merge remote-tracking branch 'origin/main' into deploy/konwledge
# Conflicts:
#	api/tests/unit_tests/commands/test_generate_swagger_specs.py
#	api/tests/unit_tests/controllers/console/app/test_agent_manage_guard.py
#	api/tests/unit_tests/extensions/test_ext_login.py
#	packages/dify-ui/src/alert-dialog/index.tsx
#	web/app/components/datasets/external-api/external-api-modal/index.tsx
#	web/app/components/datasets/list/dataset-card/components/operations-dropdown.tsx
#	web/features/new-rag/document-list.tsx
#	web/features/new-rag/sources-page.tsx
2026-08-24 11:03:38 +08:00

126 lines
3.9 KiB
TypeScript

import type { FC, FormEventHandler } from 'react'
import type { CreateExternalAPIReq, FormSchema } from '../declarations'
import { cn } from '@langgenius/dify-ui/cn'
import { Input } from '@langgenius/dify-ui/input'
import { RiBookOpenLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useDocLink } from '@/context/i18n'
type FormProps = {
id: string
onSubmit: FormEventHandler<HTMLFormElement>
className?: string
itemClassName?: string
fieldLabelClassName?: string
value: CreateExternalAPIReq
onChange: (val: CreateExternalAPIReq) => void
formSchemas: FormSchema[]
inputClassName?: string
errors?: Partial<Record<'api_key' | 'endpoint' | 'name', string>>
}
const Form: FC<FormProps> = React.memo(
({
id,
onSubmit,
className,
itemClassName,
fieldLabelClassName,
value,
onChange,
formSchemas,
inputClassName,
errors,
}) => {
const { t, i18n } = useTranslation()
const docLink = useDocLink()
const handleFormChange = (key: string, val: string) => {
if (key === 'name') {
onChange({ ...value, [key]: val })
} else {
onChange({
...value,
settings: {
...value.settings,
[key]: val,
},
})
}
}
const renderField = (formSchema: FormSchema) => {
const { variable, type, label, required } = formSchema
const fieldValue =
variable === 'name'
? value[variable]
: value.settings[variable as keyof typeof value.settings] || ''
const fieldError = errors?.[variable as keyof typeof errors]
const errorId = `${variable}-error`
return (
<div
key={variable}
className={cn(itemClassName, 'flex flex-col items-start gap-1 self-stretch')}
>
<div className="flex w-full items-center justify-between">
<label
className={cn(fieldLabelClassName, 'system-sm-semibold text-text-secondary')}
htmlFor={variable}
>
{label[i18n.language] || label.en_US}
{required && <span className="ml-1 text-red-500">*</span>}
</label>
{variable === 'endpoint' && (
<a
href={docLink('/use-dify/knowledge/external-knowledge-api') || '/'}
target="_blank"
rel="noopener noreferrer"
className="flex items-center body-xs-regular text-text-accent"
>
<RiBookOpenLine className="mr-1 size-3 text-text-accent" />
{t(($) => $.externalAPIPanelDocumentation, { ns: 'dataset' })}
</a>
)}
</div>
<Input
type={type === 'secret' ? 'password' : 'text'}
autoComplete="off"
inputMode={variable === 'endpoint' ? 'url' : undefined}
placeholder={t(($) => $['placeholder.input'], { ns: 'common' }) || ''}
spellCheck={variable === 'endpoint' || type === 'secret' ? false : undefined}
id={variable}
name={variable}
aria-describedby={fieldError ? errorId : undefined}
aria-invalid={Boolean(fieldError)}
value={fieldValue}
onChange={(val) => handleFormChange(variable, val.target.value)}
required={required}
className={cn(inputClassName)}
/>
{fieldError && (
<p id={errorId} className="system-xs-regular text-text-destructive" role="alert">
{fieldError}
</p>
)}
</div>
)
}
return (
<form
id={id}
onSubmit={onSubmit}
className={cn('flex flex-col items-start justify-center gap-4 self-stretch', className)}
>
{formSchemas.map((formSchema) => renderField(formSchema))}
</form>
)
},
)
Form.displayName = 'Form'
export default Form