index.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. 'use client'
  2. import React, { useMemo, useState } from 'react'
  3. import useSWR from 'swr'
  4. import { useTranslation } from 'react-i18next'
  5. import cn from 'classnames'
  6. import FilePreview from '../file-preview'
  7. import FileUploader from '../file-uploader'
  8. import NotionPagePreview from '../notion-page-preview'
  9. import EmptyDatasetCreationModal from '../empty-dataset-creation-modal'
  10. import s from './index.module.css'
  11. import type { FileItem } from '@/models/datasets'
  12. import type { NotionPage } from '@/models/common'
  13. import { DataSourceType } from '@/models/datasets'
  14. import Button from '@/app/components/base/button'
  15. import { NotionPageSelector } from '@/app/components/base/notion-page-selector'
  16. import { useDatasetDetailContext } from '@/context/dataset-detail'
  17. import { fetchDocumentsLimit } from '@/service/common'
  18. import { useProviderContext } from '@/context/provider-context'
  19. import VectorSpaceFull from '@/app/components/billing/vector-space-full'
  20. type IStepOneProps = {
  21. datasetId?: string
  22. dataSourceType?: DataSourceType
  23. dataSourceTypeDisable: Boolean
  24. hasConnection: boolean
  25. onSetting: () => void
  26. files: FileItem[]
  27. updateFileList: (files: FileItem[]) => void
  28. updateFile: (fileItem: FileItem, progress: number, list: FileItem[]) => void
  29. notionPages?: NotionPage[]
  30. updateNotionPages: (value: NotionPage[]) => void
  31. onStepChange: () => void
  32. changeType: (type: DataSourceType) => void
  33. }
  34. type NotionConnectorProps = {
  35. onSetting: () => void
  36. }
  37. export const NotionConnector = ({ onSetting }: NotionConnectorProps) => {
  38. const { t } = useTranslation()
  39. return (
  40. <div className={s.notionConnectionTip}>
  41. <span className={s.notionIcon}/>
  42. <div className={s.title}>{t('datasetCreation.stepOne.notionSyncTitle')}</div>
  43. <div className={s.tip}>{t('datasetCreation.stepOne.notionSyncTip')}</div>
  44. <Button className='h-8' type='primary' onClick={onSetting}>{t('datasetCreation.stepOne.connect')}</Button>
  45. </div>
  46. )
  47. }
  48. const StepOne = ({
  49. datasetId,
  50. dataSourceType,
  51. dataSourceTypeDisable,
  52. changeType,
  53. hasConnection,
  54. onSetting,
  55. onStepChange,
  56. files,
  57. updateFileList,
  58. updateFile,
  59. notionPages = [],
  60. updateNotionPages,
  61. }: IStepOneProps) => {
  62. const { data: limitsData } = useSWR('/datasets/limit', fetchDocumentsLimit)
  63. const { dataset } = useDatasetDetailContext()
  64. const [showModal, setShowModal] = useState(false)
  65. const [currentFile, setCurrentFile] = useState<File | undefined>()
  66. const [currentNotionPage, setCurrentNotionPage] = useState<NotionPage | undefined>()
  67. const { t } = useTranslation()
  68. const modalShowHandle = () => setShowModal(true)
  69. const modalCloseHandle = () => setShowModal(false)
  70. const updateCurrentFile = (file: File) => {
  71. setCurrentFile(file)
  72. }
  73. const hideFilePreview = () => {
  74. setCurrentFile(undefined)
  75. }
  76. const updateCurrentPage = (page: NotionPage) => {
  77. setCurrentNotionPage(page)
  78. }
  79. const hideNotionPagePreview = () => {
  80. setCurrentNotionPage(undefined)
  81. }
  82. const shouldShowDataSourceTypeList = !datasetId || (datasetId && !dataset?.data_source_type)
  83. const { plan, enableBilling } = useProviderContext()
  84. const allFileLoaded = (files.length > 0 && files.every(file => file.file.id))
  85. const hasNotin = notionPages.length > 0
  86. const isVectorSpaceFull = plan.usage.vectorSpace >= plan.total.vectorSpace
  87. const isShowVectorSpaceFull = (allFileLoaded || hasNotin) && isVectorSpaceFull && enableBilling
  88. const nextDisabled = useMemo(() => {
  89. if (!files.length)
  90. return true
  91. if (files.some(file => !file.file.id))
  92. return true
  93. if (isShowVectorSpaceFull)
  94. return true
  95. return false
  96. }, [files])
  97. return (
  98. <div className='flex w-full h-full'>
  99. <div className='grow overflow-y-auto relative'>
  100. {
  101. shouldShowDataSourceTypeList && (
  102. <div className={s.stepHeader}>{t('datasetCreation.steps.one')}</div>
  103. )
  104. }
  105. <div className={s.form}>
  106. {
  107. shouldShowDataSourceTypeList && (
  108. <div className='flex items-center mb-8 flex-wrap gap-y-4'>
  109. <div
  110. className={cn(
  111. s.dataSourceItem,
  112. dataSourceType === DataSourceType.FILE && s.active,
  113. dataSourceTypeDisable && dataSourceType !== DataSourceType.FILE && s.disabled,
  114. )}
  115. onClick={() => {
  116. if (dataSourceTypeDisable)
  117. return
  118. changeType(DataSourceType.FILE)
  119. hideFilePreview()
  120. hideNotionPagePreview()
  121. }}
  122. >
  123. <span className={cn(s.datasetIcon)} />
  124. {t('datasetCreation.stepOne.dataSourceType.file')}
  125. </div>
  126. <div
  127. className={cn(
  128. s.dataSourceItem,
  129. dataSourceType === DataSourceType.NOTION && s.active,
  130. dataSourceTypeDisable && dataSourceType !== DataSourceType.NOTION && s.disabled,
  131. )}
  132. onClick={() => {
  133. if (dataSourceTypeDisable)
  134. return
  135. changeType(DataSourceType.NOTION)
  136. hideFilePreview()
  137. hideNotionPagePreview()
  138. }}
  139. >
  140. <span className={cn(s.datasetIcon, s.notion)} />
  141. {t('datasetCreation.stepOne.dataSourceType.notion')}
  142. </div>
  143. <div
  144. className={cn(s.dataSourceItem, s.disabled, dataSourceType === DataSourceType.WEB && s.active)}
  145. // onClick={() => changeType(DataSourceType.WEB)}
  146. >
  147. <span className={s.comingTag}>Coming soon</span>
  148. <span className={cn(s.datasetIcon, s.web)} />
  149. {t('datasetCreation.stepOne.dataSourceType.web')}
  150. </div>
  151. </div>
  152. )
  153. }
  154. {dataSourceType === DataSourceType.FILE && limitsData && (
  155. <>
  156. <FileUploader
  157. fileList={files}
  158. titleClassName={!shouldShowDataSourceTypeList ? 'mt-[30px] !mb-[44px] !text-lg !font-semibold !text-gray-900' : undefined}
  159. prepareFileList={updateFileList}
  160. onFileListUpdate={updateFileList}
  161. onFileUpdate={updateFile}
  162. onPreview={updateCurrentFile}
  163. countLimit={limitsData.documents_limit}
  164. countUsed={limitsData.documents_count}
  165. />
  166. {isShowVectorSpaceFull && (
  167. <div className='max-w-[640px] mb-4'>
  168. <VectorSpaceFull />
  169. </div>
  170. )}
  171. <Button disabled={nextDisabled} className={s.submitButton} type='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button>
  172. </>
  173. )}
  174. {dataSourceType === DataSourceType.NOTION && (
  175. <>
  176. {!hasConnection && <NotionConnector onSetting={onSetting} />}
  177. {hasConnection && limitsData && (
  178. <>
  179. <div className='mb-8 w-[640px]'>
  180. <NotionPageSelector
  181. value={notionPages.map(page => page.page_id)}
  182. onSelect={updateNotionPages}
  183. onPreview={updateCurrentPage}
  184. countLimit={limitsData.documents_limit}
  185. countUsed={limitsData.documents_count}
  186. />
  187. </div>
  188. {isShowVectorSpaceFull && (
  189. <div className='max-w-[640px] mb-4'>
  190. <VectorSpaceFull />
  191. </div>
  192. )}
  193. <Button disabled={isShowVectorSpaceFull || !notionPages.length} className={s.submitButton} type='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button>
  194. </>
  195. )}
  196. </>
  197. )}
  198. {!datasetId && (
  199. <>
  200. <div className={s.dividerLine} />
  201. <div onClick={modalShowHandle} className={s.OtherCreationOption}>{t('datasetCreation.stepOne.emptyDatasetCreation')}</div>
  202. </>
  203. )}
  204. </div>
  205. <EmptyDatasetCreationModal show={showModal} onHide={modalCloseHandle} />
  206. </div>
  207. {currentFile && <FilePreview file={currentFile} hidePreview={hideFilePreview} />}
  208. {currentNotionPage && <NotionPagePreview currentPage={currentNotionPage} hidePreview={hideNotionPagePreview} />}
  209. </div>
  210. )
  211. }
  212. export default StepOne