file-upload-setting.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback } from 'react'
  4. import produce from 'immer'
  5. import { useTranslation } from 'react-i18next'
  6. import type { UploadFileSetting } from '../../../types'
  7. import { SupportUploadFileTypes } from '../../../types'
  8. import OptionCard from './option-card'
  9. import FileTypeItem from './file-type-item'
  10. import InputNumberWithSlider from './input-number-with-slider'
  11. import Field from '@/app/components/app/configuration/config-var/config-modal/field'
  12. import { TransferMethod } from '@/types/app'
  13. import { FILE_SIZE_LIMIT } from '@/app/components/base/file-uploader/constants'
  14. import { formatFileSize } from '@/utils/format'
  15. type Props = {
  16. payload: UploadFileSetting
  17. isMultiple: boolean
  18. inFeaturePanel?: boolean
  19. hideSupportFileType?: boolean
  20. onChange: (payload: UploadFileSetting) => void
  21. }
  22. const FileUploadSetting: FC<Props> = ({
  23. payload,
  24. isMultiple,
  25. inFeaturePanel = false,
  26. hideSupportFileType = false,
  27. onChange,
  28. }) => {
  29. const { t } = useTranslation()
  30. const {
  31. allowed_file_upload_methods,
  32. max_length,
  33. allowed_file_types,
  34. allowed_file_extensions,
  35. } = payload
  36. const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => {
  37. const newPayload = produce(payload, (draft) => {
  38. if (type === SupportUploadFileTypes.custom) {
  39. if (!draft.allowed_file_types.includes(SupportUploadFileTypes.custom))
  40. draft.allowed_file_types = [SupportUploadFileTypes.custom]
  41. else
  42. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== type)
  43. }
  44. else {
  45. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== SupportUploadFileTypes.custom)
  46. if (draft.allowed_file_types.includes(type))
  47. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== type)
  48. else
  49. draft.allowed_file_types.push(type)
  50. }
  51. })
  52. onChange(newPayload)
  53. }, [onChange, payload])
  54. const handleUploadMethodChange = useCallback((method: TransferMethod) => {
  55. return () => {
  56. const newPayload = produce(payload, (draft) => {
  57. if (method === TransferMethod.all)
  58. draft.allowed_file_upload_methods = [TransferMethod.local_file, TransferMethod.remote_url]
  59. else
  60. draft.allowed_file_upload_methods = [method]
  61. })
  62. onChange(newPayload)
  63. }
  64. }, [onChange, payload])
  65. const handleCustomFileTypesChange = useCallback((customFileTypes: string[]) => {
  66. const newPayload = produce(payload, (draft) => {
  67. draft.allowed_file_extensions = customFileTypes.map((v) => {
  68. if (v.startsWith('.')) // Not start with dot
  69. return v.slice(1)
  70. return v
  71. })
  72. })
  73. onChange(newPayload)
  74. }, [onChange, payload])
  75. const handleMaxUploadNumLimitChange = useCallback((value: number) => {
  76. const newPayload = produce(payload, (draft) => {
  77. draft.max_length = value
  78. })
  79. onChange(newPayload)
  80. }, [onChange, payload])
  81. return (
  82. <div>
  83. {!inFeaturePanel && (
  84. <Field
  85. title={t('appDebug.variableConfig.file.supportFileTypes')}
  86. >
  87. <div className='space-y-1'>
  88. {
  89. [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
  90. <FileTypeItem
  91. key={type}
  92. type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
  93. selected={allowed_file_types.includes(type)}
  94. onToggle={handleSupportFileTypeChange}
  95. />
  96. ))
  97. }
  98. <FileTypeItem
  99. type={SupportUploadFileTypes.custom}
  100. selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
  101. onToggle={handleSupportFileTypeChange}
  102. customFileTypes={allowed_file_extensions?.map(item => `.${item}`)}
  103. onCustomFileTypesChange={handleCustomFileTypesChange}
  104. />
  105. </div>
  106. </Field>
  107. )}
  108. <Field
  109. title={t('appDebug.variableConfig.uploadFileTypes')}
  110. className='mt-4'
  111. >
  112. <div className='grid grid-cols-3 gap-2'>
  113. <OptionCard
  114. title={t('appDebug.variableConfig.localUpload')}
  115. selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.local_file)}
  116. onSelect={handleUploadMethodChange(TransferMethod.local_file)}
  117. />
  118. <OptionCard
  119. title="URL"
  120. selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
  121. onSelect={handleUploadMethodChange(TransferMethod.remote_url)}
  122. />
  123. <OptionCard
  124. title={t('appDebug.variableConfig.both')}
  125. selected={allowed_file_upload_methods.includes(TransferMethod.local_file) && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
  126. onSelect={handleUploadMethodChange(TransferMethod.all)}
  127. />
  128. </div>
  129. </Field>
  130. {isMultiple && (
  131. <Field
  132. className='mt-4'
  133. title={t('appDebug.variableConfig.maxNumberOfUploads')!}
  134. >
  135. <div>
  136. <div className='mb-1.5 text-text-tertiary body-xs-regular'>{t('appDebug.variableConfig.maxNumberTip', { size: formatFileSize(FILE_SIZE_LIMIT) })}</div>
  137. <InputNumberWithSlider
  138. value={max_length}
  139. min={1}
  140. max={10}
  141. onChange={handleMaxUploadNumLimitChange}
  142. />
  143. </div>
  144. </Field>
  145. )}
  146. {inFeaturePanel && !hideSupportFileType && (
  147. <Field
  148. title={t('appDebug.variableConfig.file.supportFileTypes')}
  149. className='mt-4'
  150. >
  151. <div className='space-y-1'>
  152. {
  153. [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
  154. <FileTypeItem
  155. key={type}
  156. type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
  157. selected={allowed_file_types.includes(type)}
  158. onToggle={handleSupportFileTypeChange}
  159. />
  160. ))
  161. }
  162. <FileTypeItem
  163. type={SupportUploadFileTypes.custom}
  164. selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
  165. onToggle={handleSupportFileTypeChange}
  166. customFileTypes={allowed_file_extensions}
  167. onCustomFileTypesChange={handleCustomFileTypesChange}
  168. />
  169. </div>
  170. </Field>
  171. )}
  172. </div>
  173. )
  174. }
  175. export default React.memo(FileUploadSetting)