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.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { useCallback, useEffect, useMemo } from 'react'
  2. import WorkspaceSelector from '@/app/components/base/notion-page-selector/workspace-selector'
  3. import SearchInput from '@/app/components/base/notion-page-selector/search-input'
  4. import PageSelector from './page-selector'
  5. import type { DataSourceNotionPageMap, DataSourceNotionWorkspace } from '@/models/common'
  6. import Header from '@/app/components/datasets/create/website/base/header'
  7. import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
  8. import { DatasourceType } from '@/models/pipeline'
  9. import { ssePost } from '@/service/base'
  10. import Toast from '@/app/components/base/toast'
  11. import type { DataSourceNodeCompletedResponse, DataSourceNodeErrorResponse } from '@/types/pipeline'
  12. import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types'
  13. import { useDataSourceStore, useDataSourceStoreWithSelector } from '../store'
  14. import { useShallow } from 'zustand/react/shallow'
  15. type OnlineDocumentsProps = {
  16. isInPipeline?: boolean
  17. nodeId: string
  18. nodeData: DataSourceNodeType
  19. }
  20. const OnlineDocuments = ({
  21. isInPipeline = false,
  22. nodeId,
  23. nodeData,
  24. }: OnlineDocumentsProps) => {
  25. const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id)
  26. const {
  27. documentsData,
  28. searchValue,
  29. selectedPagesId,
  30. currentWorkspaceId,
  31. } = useDataSourceStoreWithSelector(useShallow(state => ({
  32. documentsData: state.documentsData,
  33. searchValue: state.searchValue,
  34. selectedPagesId: state.selectedPagesId,
  35. currentWorkspaceId: state.currentWorkspaceId,
  36. })))
  37. const dataSourceStore = useDataSourceStore()
  38. const PagesMapAndSelectedPagesId: DataSourceNotionPageMap = useMemo(() => {
  39. const pagesMap = (documentsData || []).reduce((prev: DataSourceNotionPageMap, next: DataSourceNotionWorkspace) => {
  40. next.pages.forEach((page) => {
  41. prev[page.page_id] = {
  42. ...page,
  43. workspace_id: next.workspace_id,
  44. }
  45. })
  46. return prev
  47. }, {})
  48. return pagesMap
  49. }, [documentsData])
  50. const datasourceNodeRunURL = !isInPipeline
  51. ? `/rag/pipelines/${pipelineId}/workflows/published/datasource/nodes/${nodeId}/run`
  52. : `/rag/pipelines/${pipelineId}/workflows/draft/datasource/nodes/${nodeId}/run`
  53. const getOnlineDocuments = useCallback(async () => {
  54. ssePost(
  55. datasourceNodeRunURL,
  56. {
  57. body: {
  58. inputs: {},
  59. datasource_type: DatasourceType.onlineDocument,
  60. },
  61. },
  62. {
  63. onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => {
  64. const { setDocumentsData, setCurrentWorkspaceId } = dataSourceStore.getState()
  65. setDocumentsData(documentsData.data as DataSourceNotionWorkspace[])
  66. setCurrentWorkspaceId(documentsData.data[0].workspace_id)
  67. },
  68. onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => {
  69. Toast.notify({
  70. type: 'error',
  71. message: error.error,
  72. })
  73. },
  74. },
  75. )
  76. }, [dataSourceStore, datasourceNodeRunURL])
  77. useEffect(() => {
  78. const {
  79. setDocumentsData,
  80. setCurrentWorkspaceId,
  81. setSearchValue,
  82. setSelectedPagesId,
  83. setOnlineDocuments,
  84. setCurrentDocument,
  85. currentNodeIdRef,
  86. } = dataSourceStore.getState()
  87. if (nodeId !== currentNodeIdRef.current) {
  88. setDocumentsData([])
  89. setCurrentWorkspaceId('')
  90. setSearchValue('')
  91. setSelectedPagesId(new Set())
  92. setOnlineDocuments([])
  93. setCurrentDocument(undefined)
  94. currentNodeIdRef.current = nodeId
  95. getOnlineDocuments()
  96. }
  97. else {
  98. // Avoid fetching documents when come back from next step
  99. if (!documentsData.length)
  100. getOnlineDocuments()
  101. }
  102. // eslint-disable-next-line react-hooks/exhaustive-deps
  103. }, [nodeId])
  104. const currentWorkspace = documentsData.find(workspace => workspace.workspace_id === currentWorkspaceId)
  105. const handleSearchValueChange = useCallback((value: string) => {
  106. const { setSearchValue } = dataSourceStore.getState()
  107. setSearchValue(value)
  108. }, [dataSourceStore])
  109. const handleSelectWorkspace = useCallback((workspaceId: string) => {
  110. const { setCurrentWorkspaceId } = dataSourceStore.getState()
  111. setCurrentWorkspaceId(workspaceId)
  112. }, [dataSourceStore])
  113. const handleSelectPages = useCallback((newSelectedPagesId: Set<string>) => {
  114. const { setSelectedPagesId, setOnlineDocuments } = dataSourceStore.getState()
  115. const selectedPages = Array.from(newSelectedPagesId).map(pageId => PagesMapAndSelectedPagesId[pageId])
  116. setSelectedPagesId(new Set(Array.from(newSelectedPagesId)))
  117. setOnlineDocuments(selectedPages)
  118. }, [dataSourceStore, PagesMapAndSelectedPagesId])
  119. const handlePreviewPage = useCallback((previewPageId: string) => {
  120. const { setCurrentDocument } = dataSourceStore.getState()
  121. setCurrentDocument(PagesMapAndSelectedPagesId[previewPageId])
  122. }, [PagesMapAndSelectedPagesId, dataSourceStore])
  123. const headerInfo = useMemo(() => {
  124. return {
  125. title: nodeData.title,
  126. docTitle: 'How to use?',
  127. docLink: 'https://docs.dify.ai',
  128. }
  129. }, [nodeData])
  130. if (!documentsData?.length)
  131. return null
  132. return (
  133. <div className='flex flex-col gap-y-2'>
  134. <Header
  135. isInPipeline={isInPipeline}
  136. {...headerInfo}
  137. />
  138. <div className='rounded-xl border border-components-panel-border bg-background-default-subtle'>
  139. <div className='flex h-12 items-center gap-x-2 rounded-t-xl border-b border-b-divider-regular bg-components-panel-bg p-2'>
  140. <div className='flex grow items-center gap-x-1'>
  141. <WorkspaceSelector
  142. value={currentWorkspaceId}
  143. items={documentsData}
  144. onSelect={handleSelectWorkspace}
  145. />
  146. </div>
  147. <SearchInput
  148. value={searchValue}
  149. onChange={handleSearchValueChange}
  150. />
  151. </div>
  152. <div className='overflow-hidden rounded-b-xl'>
  153. <PageSelector
  154. checkedIds={selectedPagesId}
  155. disabledValue={new Set()}
  156. searchValue={searchValue}
  157. list={currentWorkspace?.pages || []}
  158. pagesMap={PagesMapAndSelectedPagesId}
  159. onSelect={handleSelectPages}
  160. canPreview={!isInPipeline}
  161. onPreview={handlePreviewPage}
  162. isMultipleChoice={!isInPipeline}
  163. currentWorkspaceId={currentWorkspaceId}
  164. />
  165. </div>
  166. </div>
  167. </div>
  168. )
  169. }
  170. export default OnlineDocuments