Form.tsx 8.9 KB

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