Form.tsx 7.8 KB

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