Form.tsx 7.6 KB

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