base.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { useCallback, useEffect, useMemo, useState } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import { useContext } from 'use-context-selector'
  4. import useSWR from 'swr'
  5. import cn from 'classnames'
  6. import s from './base.module.css'
  7. import WorkspaceSelector from './workspace-selector'
  8. import SearchInput from './search-input'
  9. import PageSelector from './page-selector'
  10. import { preImportNotionPages } from '@/service/datasets'
  11. import AccountSetting from '@/app/components/header/account-setting'
  12. import { NotionConnector } from '@/app/components/datasets/create/step-one'
  13. import type { DataSourceNotionPage, DataSourceNotionPageMap, DataSourceNotionWorkspace } from '@/models/common'
  14. import { ToastContext } from '@/app/components/base/toast'
  15. export type NotionPageSelectorValue = DataSourceNotionPage & { workspace_id: string }
  16. type NotionPageSelectorProps = {
  17. value?: string[]
  18. onSelect: (selectedPages: NotionPageSelectorValue[]) => void
  19. canPreview?: boolean
  20. previewPageId?: string
  21. onPreview?: (selectedPage: NotionPageSelectorValue) => void
  22. datasetId?: string
  23. countLimit: number
  24. countUsed: number
  25. }
  26. const NotionPageSelector = ({
  27. value,
  28. onSelect,
  29. canPreview,
  30. previewPageId,
  31. onPreview,
  32. datasetId = '',
  33. countLimit,
  34. countUsed,
  35. }: NotionPageSelectorProps) => {
  36. const { t } = useTranslation()
  37. const { notify } = useContext(ToastContext)
  38. const { data, mutate } = useSWR({ url: '/notion/pre-import/pages', datasetId }, preImportNotionPages)
  39. const [prevData, setPrevData] = useState(data)
  40. const [searchValue, setSearchValue] = useState('')
  41. const [showDataSourceSetting, setShowDataSourceSetting] = useState(false)
  42. const [currentWorkspaceId, setCurrentWorkspaceId] = useState('')
  43. const notionWorkspaces = useMemo(() => {
  44. return data?.notion_info || []
  45. }, [data?.notion_info])
  46. const firstWorkspaceId = notionWorkspaces[0]?.workspace_id
  47. const currentWorkspace = notionWorkspaces.find(workspace => workspace.workspace_id === currentWorkspaceId)
  48. const getPagesMapAndSelectedPagesId: [DataSourceNotionPageMap, Set<string>] = useMemo(() => {
  49. const selectedPagesId = new Set<string>()
  50. const pagesMap = notionWorkspaces.reduce((prev: DataSourceNotionPageMap, next: DataSourceNotionWorkspace) => {
  51. next.pages.forEach((page) => {
  52. if (page.is_bound)
  53. selectedPagesId.add(page.page_id)
  54. prev[page.page_id] = {
  55. ...page,
  56. workspace_id: next.workspace_id,
  57. }
  58. })
  59. return prev
  60. }, {})
  61. return [pagesMap, selectedPagesId]
  62. }, [notionWorkspaces])
  63. const defaultSelectedPagesId = [...Array.from(getPagesMapAndSelectedPagesId[1]), ...(value || [])]
  64. const [selectedPagesId, setSelectedPagesId] = useState<Set<string>>(new Set(defaultSelectedPagesId))
  65. if (prevData !== data) {
  66. setPrevData(data)
  67. setSelectedPagesId(new Set(defaultSelectedPagesId))
  68. }
  69. const handleSearchValueChange = useCallback((value: string) => {
  70. setSearchValue(value)
  71. }, [])
  72. const handleSelectWorkspace = useCallback((workspaceId: string) => {
  73. setCurrentWorkspaceId(workspaceId)
  74. }, [])
  75. const handleSelecPages = (newSelectedPagesId: Set<string>) => {
  76. const selectedPages = Array.from(newSelectedPagesId).map(pageId => getPagesMapAndSelectedPagesId[0][pageId])
  77. if (selectedPages.length > countLimit - countUsed) {
  78. notify({ type: 'error', message: t('datasetCreation.stepOne.overCountLimit', { countLimit }) })
  79. return false
  80. }
  81. setSelectedPagesId(new Set(Array.from(newSelectedPagesId)))
  82. onSelect(selectedPages)
  83. }
  84. const handlePreviewPage = (previewPageId: string) => {
  85. if (onPreview)
  86. onPreview(getPagesMapAndSelectedPagesId[0][previewPageId])
  87. }
  88. useEffect(() => {
  89. setCurrentWorkspaceId(firstWorkspaceId)
  90. }, [firstWorkspaceId])
  91. return (
  92. <div className='bg-gray-25 border border-gray-200 rounded-xl'>
  93. {
  94. data?.notion_info?.length
  95. ? (
  96. <>
  97. <div className='flex items-center pl-[10px] pr-2 h-11 bg-white border-b border-b-gray-200 rounded-t-xl'>
  98. <WorkspaceSelector
  99. value={currentWorkspaceId || firstWorkspaceId}
  100. items={notionWorkspaces}
  101. onSelect={handleSelectWorkspace}
  102. />
  103. <div className='mx-1 w-[1px] h-3 bg-gray-200' />
  104. <div
  105. className={cn(s['setting-icon'], 'w-6 h-6 cursor-pointer')}
  106. onClick={() => setShowDataSourceSetting(true)}
  107. />
  108. <div className='grow' />
  109. <SearchInput
  110. value={searchValue}
  111. onChange={handleSearchValueChange}
  112. />
  113. </div>
  114. <div className='rounded-b-xl overflow-hidden'>
  115. <PageSelector
  116. value={selectedPagesId}
  117. searchValue={searchValue}
  118. list={currentWorkspace?.pages || []}
  119. pagesMap={getPagesMapAndSelectedPagesId[0]}
  120. onSelect={handleSelecPages}
  121. canPreview={canPreview}
  122. previewPageId={previewPageId}
  123. onPreview={handlePreviewPage}
  124. />
  125. </div>
  126. </>
  127. )
  128. : (
  129. <NotionConnector onSetting={() => setShowDataSourceSetting(true)} />
  130. )
  131. }
  132. {
  133. showDataSourceSetting && (
  134. <AccountSetting activeTab='data-source' onCancel={() => {
  135. setShowDataSourceSetting(false)
  136. mutate()
  137. }} />
  138. )
  139. }
  140. </div>
  141. )
  142. }
  143. export default NotionPageSelector