Form.tsx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import { useState } from 'react'
  2. import type { FC } from 'react'
  3. import cn from 'classnames'
  4. import { ValidatingTip } from '../../key-validator/ValidateStatus'
  5. import type {
  6. CredentialFormSchema,
  7. CredentialFormSchemaNumberInput,
  8. CredentialFormSchemaRadio,
  9. CredentialFormSchemaSecretInput,
  10. CredentialFormSchemaSelect,
  11. CredentialFormSchemaTextInput,
  12. FormValue,
  13. } from '../declarations'
  14. import { FormTypeEnum } from '../declarations'
  15. import { useLanguage } from '../hooks'
  16. import Input from './Input'
  17. import { SimpleSelect } from '@/app/components/base/select'
  18. import Tooltip from '@/app/components/base/tooltip-plus'
  19. import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
  20. import Radio from '@/app/components/base/radio'
  21. type FormProps = {
  22. className?: string
  23. itemClassName?: string
  24. fieldLabelClassName?: string
  25. value: FormValue
  26. onChange: (val: FormValue) => void
  27. formSchemas: CredentialFormSchema[]
  28. validating: boolean
  29. validatedSuccess?: boolean
  30. showOnVariableMap: Record<string, string[]>
  31. isEditMode: boolean
  32. readonly?: boolean
  33. inputClassName?: string
  34. isShowDefaultValue?: boolean
  35. fieldMoreInfo?: (payload: CredentialFormSchema) => JSX.Element | null
  36. }
  37. const Form: FC<FormProps> = ({
  38. className,
  39. itemClassName,
  40. fieldLabelClassName,
  41. value,
  42. onChange,
  43. formSchemas,
  44. validating,
  45. validatedSuccess,
  46. showOnVariableMap,
  47. isEditMode,
  48. readonly,
  49. inputClassName,
  50. isShowDefaultValue = false,
  51. fieldMoreInfo,
  52. }) => {
  53. const language = useLanguage()
  54. const [changeKey, setChangeKey] = useState('')
  55. const handleFormChange = (key: string, val: string | boolean) => {
  56. if (isEditMode && (key === '__model_type' || key === '__model_name'))
  57. return
  58. setChangeKey(key)
  59. const shouldClearVariable: Record<string, string | undefined> = {}
  60. if (showOnVariableMap[key]?.length) {
  61. showOnVariableMap[key].forEach((clearVariable) => {
  62. shouldClearVariable[clearVariable] = undefined
  63. })
  64. }
  65. onChange({ ...value, [key]: val, ...shouldClearVariable })
  66. }
  67. const renderField = (formSchema: CredentialFormSchema) => {
  68. const tooltip = formSchema.tooltip
  69. const tooltipContent = (tooltip && (
  70. <span className='ml-1 pt-1.5'>
  71. <Tooltip popupContent={
  72. // w-[100px] caused problem
  73. <div className=''>
  74. {tooltip[language] || tooltip.en_US}
  75. </div>
  76. } >
  77. <HelpCircle className='w-3 h-3 text-gray-500' />
  78. </Tooltip>
  79. </span>))
  80. if (formSchema.type === FormTypeEnum.textInput || formSchema.type === FormTypeEnum.secretInput || formSchema.type === FormTypeEnum.textNumber) {
  81. const {
  82. variable,
  83. label,
  84. placeholder,
  85. required,
  86. show_on,
  87. } = formSchema as (CredentialFormSchemaTextInput | CredentialFormSchemaSecretInput)
  88. if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value))
  89. return null
  90. const disabed = readonly || (isEditMode && (variable === '__model_type' || variable === '__model_name'))
  91. return (
  92. <div key={variable} className={cn(itemClassName, 'py-3')}>
  93. <div className={cn(fieldLabelClassName, 'py-2 text-sm text-gray-900')}>
  94. {label[language] || label.en_US}
  95. {
  96. required && (
  97. <span className='ml-1 text-red-500'>*</span>
  98. )
  99. }
  100. {tooltipContent}
  101. </div>
  102. <Input
  103. className={cn(inputClassName, `${disabed && 'cursor-not-allowed opacity-60'}`)}
  104. value={(isShowDefaultValue && ((value[variable] as string) === '' || value[variable] === undefined || value[variable] === null)) ? formSchema.default : value[variable]}
  105. onChange={val => handleFormChange(variable, val)}
  106. validated={validatedSuccess}
  107. placeholder={placeholder?.[language] || placeholder?.en_US}
  108. disabled={disabed}
  109. type={formSchema.type === FormTypeEnum.textNumber ? 'number' : 'text'}
  110. {...(formSchema.type === FormTypeEnum.textNumber ? { min: (formSchema as CredentialFormSchemaNumberInput).min, max: (formSchema as CredentialFormSchemaNumberInput).max } : {})}
  111. />
  112. {fieldMoreInfo?.(formSchema)}
  113. {validating && changeKey === variable && <ValidatingTip />}
  114. </div>
  115. )
  116. }
  117. if (formSchema.type === FormTypeEnum.radio) {
  118. const {
  119. options,
  120. variable,
  121. label,
  122. show_on,
  123. required,
  124. } = formSchema as CredentialFormSchemaRadio
  125. if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value))
  126. return null
  127. const disabed = isEditMode && (variable === '__model_type' || variable === '__model_name')
  128. return (
  129. <div key={variable} className={cn(itemClassName, 'py-3')}>
  130. <div className={cn(fieldLabelClassName, 'py-2 text-sm text-gray-900')}>
  131. {label[language] || label.en_US}
  132. {
  133. required && (
  134. <span className='ml-1 text-red-500'>*</span>
  135. )
  136. }
  137. {tooltipContent}
  138. </div>
  139. <div className={`grid grid-cols-${options?.length} gap-3`}>
  140. {
  141. options.filter((option) => {
  142. if (option.show_on.length)
  143. return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)
  144. return true
  145. }).map(option => (
  146. <div
  147. className={`
  148. flex items-center px-3 py-2 rounded-lg border border-gray-100 bg-gray-25 cursor-pointer
  149. ${value[variable] === option.value && 'bg-white border-[1.5px] border-primary-400 shadow-sm'}
  150. ${disabed && '!cursor-not-allowed opacity-60'}
  151. `}
  152. onClick={() => handleFormChange(variable, option.value)}
  153. key={`${variable}-${option.value}`}
  154. >
  155. <div className={`
  156. flex justify-center items-center mr-2 w-4 h-4 border border-gray-300 rounded-full
  157. ${value[variable] === option.value && 'border-[5px] border-primary-600'}
  158. `} />
  159. <div className='text-sm text-gray-900'>{option.label[language] || option.label.en_US}</div>
  160. </div>
  161. ))
  162. }
  163. </div>
  164. {fieldMoreInfo?.(formSchema)}
  165. {validating && changeKey === variable && <ValidatingTip />}
  166. </div>
  167. )
  168. }
  169. if (formSchema.type === 'select') {
  170. const {
  171. options,
  172. variable,
  173. label,
  174. show_on,
  175. required,
  176. placeholder,
  177. } = formSchema as CredentialFormSchemaSelect
  178. if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value))
  179. return null
  180. return (
  181. <div key={variable} className={cn(itemClassName, 'py-3')}>
  182. <div className={cn(fieldLabelClassName, 'py-2 text-sm text-gray-900')}>
  183. {label[language] || label.en_US}
  184. {
  185. required && (
  186. <span className='ml-1 text-red-500'>*</span>
  187. )
  188. }
  189. {tooltipContent}
  190. </div>
  191. <SimpleSelect
  192. className={cn(inputClassName)}
  193. disabled={readonly}
  194. defaultValue={(isShowDefaultValue && ((value[variable] as string) === '' || value[variable] === undefined || value[variable] === null)) ? formSchema.default : value[variable]}
  195. items={options.filter((option) => {
  196. if (option.show_on.length)
  197. return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)
  198. return true
  199. }).map(option => ({ value: option.value, name: option.label[language] || option.label.en_US }))}
  200. onSelect={item => handleFormChange(variable, item.value as string)}
  201. placeholder={placeholder?.[language] || placeholder?.en_US}
  202. />
  203. {fieldMoreInfo?.(formSchema)}
  204. {validating && changeKey === variable && <ValidatingTip />}
  205. </div>
  206. )
  207. }
  208. if (formSchema.type === 'boolean') {
  209. const {
  210. variable,
  211. label,
  212. show_on,
  213. } = formSchema as CredentialFormSchemaRadio
  214. if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value))
  215. return null
  216. return (
  217. <div key={variable} className={cn(itemClassName, 'py-3')}>
  218. <div className='flex items-center justify-between py-2 text-sm text-gray-900'>
  219. <div className='flex items-center space-x-2'>
  220. <span>{label[language] || label.en_US}</span>
  221. {tooltipContent}
  222. </div>
  223. <Radio.Group
  224. className='flex items-center'
  225. value={value[variable] ? 1 : 0}
  226. onChange={val => handleFormChange(variable, val === 1)}
  227. >
  228. <Radio value={1} className='!mr-1'>True</Radio>
  229. <Radio value={0}>False</Radio>
  230. </Radio.Group>
  231. </div>
  232. {fieldMoreInfo?.(formSchema)}
  233. </div>
  234. )
  235. }
  236. }
  237. return (
  238. <div className={className}>
  239. {
  240. formSchemas.map(formSchema => renderField(formSchema))
  241. }
  242. </div>
  243. )
  244. }
  245. export default Form