You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import AppIcon from '@/app/components/base/app-icon'
  2. import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
  3. import AppIconPicker from '@/app/components/base/app-icon-picker'
  4. import Input from '@/app/components/base/input'
  5. import Textarea from '@/app/components/base/textarea'
  6. import type { Member } from '@/models/common'
  7. import { DatasetPermission } from '@/models/datasets'
  8. import { useMembers } from '@/service/use-common'
  9. import type { AppIconType } from '@/types/app'
  10. import React, { useCallback, useEffect, useRef, useState } from 'react'
  11. import { useTranslation } from 'react-i18next'
  12. import PermissionSelector from '@/app/components/datasets/settings/permission-selector'
  13. import Button from '@/app/components/base/button'
  14. import { RiCloseLine } from '@remixicon/react'
  15. import Toast from '@/app/components/base/toast'
  16. import type { CreateFormData } from '@/models/pipeline'
  17. const DEFAULT_APP_ICON: AppIconSelection = {
  18. type: 'emoji',
  19. icon: '📙',
  20. background: '#FFF4ED',
  21. }
  22. type CreateFormProps = {
  23. onCreate: (payload: CreateFormData) => void
  24. onClose: () => void
  25. }
  26. const CreateForm = ({
  27. onCreate,
  28. onClose,
  29. }: CreateFormProps) => {
  30. const { t } = useTranslation()
  31. const [name, setName] = useState('')
  32. const [appIcon, setAppIcon] = useState<AppIconSelection>(DEFAULT_APP_ICON)
  33. const [description, setDescription] = useState('')
  34. const [permission, setPermission] = useState(DatasetPermission.onlyMe)
  35. const [showAppIconPicker, setShowAppIconPicker] = useState(false)
  36. const [selectedMemberIDs, setSelectedMemberIDs] = useState<string[]>([])
  37. const previousAppIcon = useRef<AppIconSelection>(DEFAULT_APP_ICON)
  38. const [memberList, setMemberList] = useState<Member[]>([])
  39. const { data: members } = useMembers()
  40. useEffect(() => {
  41. if (members?.accounts)
  42. setMemberList(members.accounts)
  43. }, [members])
  44. const handleAppNameChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
  45. const value = event.target.value
  46. setName(value)
  47. }, [])
  48. const handleOpenAppIconPicker = useCallback(() => {
  49. setShowAppIconPicker(true)
  50. previousAppIcon.current = appIcon
  51. }, [appIcon])
  52. const handleSelectAppIcon = useCallback((icon: AppIconSelection) => {
  53. setAppIcon(icon)
  54. setShowAppIconPicker(false)
  55. }, [])
  56. const handleCloseAppIconPicker = useCallback(() => {
  57. setAppIcon(previousAppIcon.current)
  58. setShowAppIconPicker(false)
  59. }, [])
  60. const handleDescriptionChange = useCallback((event: React.ChangeEvent<HTMLTextAreaElement>) => {
  61. const value = event.target.value
  62. setDescription(value)
  63. }, [])
  64. const handlePermissionChange = useCallback((value?: DatasetPermission) => {
  65. setPermission(value!)
  66. }, [])
  67. const handleCreate = useCallback(() => {
  68. if (!name) {
  69. Toast.notify({
  70. type: 'error',
  71. message: 'Please enter a name for the Knowledge Base.',
  72. })
  73. return
  74. }
  75. onCreate({
  76. name,
  77. appIcon,
  78. description,
  79. permission,
  80. selectedMemberIDs,
  81. })
  82. }, [name, appIcon, description, permission, selectedMemberIDs, onCreate])
  83. return (
  84. <div className='relative flex flex-col'>
  85. {/* Header */}
  86. <div className='pb-3 pl-6 pr-14 pt-6'>
  87. <span className='title-2xl-semi-bold text-text-primary'>
  88. {t('datasetPipeline.creation.createKnowledge')}
  89. </span>
  90. </div>
  91. <button
  92. className='absolute right-5 top-5 flex size-8 items-center justify-center'
  93. onClick={onClose}
  94. >
  95. <RiCloseLine className='size-5 text-text-tertiary' />
  96. </button>
  97. {/* Form */}
  98. <div className='flex flex-col gap-y-5 px-6 py-3'>
  99. <div className='flex items-end gap-x-3 self-stretch'>
  100. <div className='flex grow flex-col gap-y-1 pb-1'>
  101. <label className='system-sm-medium flex h-6 items-center text-text-secondary'>
  102. {t('datasetPipeline.knowledgeNameAndIcon')}
  103. </label>
  104. <Input
  105. onChange={handleAppNameChange}
  106. value={name}
  107. placeholder={t('datasetPipeline.knowledgeNameAndIconPlaceholder')}
  108. />
  109. </div>
  110. <AppIcon
  111. size='xxl'
  112. onClick={handleOpenAppIconPicker}
  113. className='cursor-pointer'
  114. iconType={appIcon.type as AppIconType}
  115. icon={appIcon.type === 'image' ? appIcon.fileId : appIcon.icon}
  116. background={appIcon.type === 'image' ? undefined : appIcon.background}
  117. imageUrl={appIcon.type === 'image' ? appIcon.url : undefined}
  118. showEditIcon
  119. />
  120. </div>
  121. <div className='flex flex-col gap-y-1'>
  122. <label className='system-sm-medium flex h-6 items-center text-text-secondary'>
  123. {t('datasetPipeline.knowledgeDescription')}
  124. </label>
  125. <Textarea
  126. onChange={handleDescriptionChange}
  127. value={description}
  128. placeholder={t('datasetPipeline.knowledgeDescriptionPlaceholder')}
  129. />
  130. </div>
  131. <div className='flex flex-col gap-y-1'>
  132. <label className='system-sm-medium flex h-6 items-center text-text-secondary'>
  133. {t('datasetPipeline.knowledgePermissions')}
  134. </label>
  135. <PermissionSelector
  136. permission={permission}
  137. value={selectedMemberIDs}
  138. onChange={handlePermissionChange}
  139. onMemberSelect={setSelectedMemberIDs}
  140. memberList={memberList}
  141. />
  142. </div>
  143. </div>
  144. {/* Actions */}
  145. <div className='flex items-center justify-end gap-x-2 p-6 pt-5'>
  146. <Button
  147. variant='secondary'
  148. onClick={onClose}
  149. >
  150. {t('common.operation.cancel')}
  151. </Button>
  152. <Button
  153. variant='primary'
  154. onClick={handleCreate}
  155. >
  156. {t('common.operation.create')}
  157. </Button>
  158. </div>
  159. {showAppIconPicker && (
  160. <AppIconPicker
  161. onSelect={handleSelectAppIcon}
  162. onClose={handleCloseAppIconPicker}
  163. />
  164. )}
  165. </div>
  166. )
  167. }
  168. export default React.memo(CreateForm)