base.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { useCallback, useEffect, useMemo, useState } from 'react'
  2. import useSWR from 'swr'
  3. import { RiEqualizer2Line } from '@remixicon/react'
  4. import WorkspaceSelector from './workspace-selector'
  5. import SearchInput from './search-input'
  6. import PageSelector from './page-selector'
  7. import { preImportNotionPages } from '@/service/datasets'
  8. import { NotionConnector } from '@/app/components/datasets/create/step-one'
  9. import type { DataSourceNotionPageMap, DataSourceNotionWorkspace, NotionPage } from '@/models/common'
  10. import { useModalContext } from '@/context/modal-context'
  11. type NotionPageSelectorProps = {
  12. value?: string[]
  13. onSelect: (selectedPages: NotionPage[]) => void
  14. canPreview?: boolean
  15. previewPageId?: string
  16. onPreview?: (selectedPage: NotionPage) => void
  17. datasetId?: string
  18. }
  19. const NotionPageSelector = ({
  20. value,
  21. onSelect,
  22. canPreview,
  23. previewPageId,
  24. onPreview,
  25. datasetId = '',
  26. }: NotionPageSelectorProps) => {
  27. const { data, mutate } = useSWR({ url: '/notion/pre-import/pages', datasetId }, preImportNotionPages)
  28. const [prevData, setPrevData] = useState(data)
  29. const [searchValue, setSearchValue] = useState('')
  30. const [currentWorkspaceId, setCurrentWorkspaceId] = useState('')
  31. const { setShowAccountSettingModal } = useModalContext()
  32. const notionWorkspaces = useMemo(() => {
  33. return data?.notion_info || []
  34. }, [data?.notion_info])
  35. const firstWorkspaceId = notionWorkspaces[0]?.workspace_id
  36. const currentWorkspace = notionWorkspaces.find(workspace => workspace.workspace_id === currentWorkspaceId)
  37. const getPagesMapAndSelectedPagesId: [DataSourceNotionPageMap, Set<string>, Set<string>] = useMemo(() => {
  38. const selectedPagesId = new Set<string>()
  39. const boundPagesId = new Set<string>()
  40. const pagesMap = notionWorkspaces.reduce((prev: DataSourceNotionPageMap, next: DataSourceNotionWorkspace) => {
  41. next.pages.forEach((page) => {
  42. if (page.is_bound) {
  43. selectedPagesId.add(page.page_id)
  44. boundPagesId.add(page.page_id)
  45. }
  46. prev[page.page_id] = {
  47. ...page,
  48. workspace_id: next.workspace_id,
  49. }
  50. })
  51. return prev
  52. }, {})
  53. return [pagesMap, selectedPagesId, boundPagesId]
  54. }, [notionWorkspaces])
  55. const defaultSelectedPagesId = [...Array.from(getPagesMapAndSelectedPagesId[1]), ...(value || [])]
  56. const [selectedPagesId, setSelectedPagesId] = useState<Set<string>>(new Set(defaultSelectedPagesId))
  57. if (prevData !== data) {
  58. setPrevData(data)
  59. setSelectedPagesId(new Set(defaultSelectedPagesId))
  60. }
  61. const handleSearchValueChange = useCallback((value: string) => {
  62. setSearchValue(value)
  63. }, [])
  64. const handleSelectWorkspace = useCallback((workspaceId: string) => {
  65. setCurrentWorkspaceId(workspaceId)
  66. }, [])
  67. const handleSelectPages = (newSelectedPagesId: Set<string>) => {
  68. const selectedPages = Array.from(newSelectedPagesId).map(pageId => getPagesMapAndSelectedPagesId[0][pageId])
  69. setSelectedPagesId(new Set(Array.from(newSelectedPagesId)))
  70. onSelect(selectedPages)
  71. }
  72. const handlePreviewPage = (previewPageId: string) => {
  73. if (onPreview)
  74. onPreview(getPagesMapAndSelectedPagesId[0][previewPageId])
  75. }
  76. useEffect(() => {
  77. setCurrentWorkspaceId(firstWorkspaceId)
  78. }, [firstWorkspaceId])
  79. return (
  80. <div className='rounded-xl border border-components-panel-border bg-background-default-subtle'>
  81. {
  82. data?.notion_info?.length
  83. ? (
  84. <>
  85. <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'>
  86. <div className='flex grow items-center gap-x-1'>
  87. <WorkspaceSelector
  88. value={currentWorkspaceId || firstWorkspaceId}
  89. items={notionWorkspaces}
  90. onSelect={handleSelectWorkspace}
  91. />
  92. <div className='mx-1 h-3 w-[1px] bg-divider-regular' />
  93. <RiEqualizer2Line
  94. className='h-4 w-4 cursor-pointer text-text-tertiary'
  95. onClick={() => setShowAccountSettingModal({ payload: 'data-source', onCancelCallback: mutate })}
  96. />
  97. </div>
  98. <SearchInput
  99. value={searchValue}
  100. onChange={handleSearchValueChange}
  101. />
  102. </div>
  103. <div className='overflow-hidden rounded-b-xl'>
  104. <PageSelector
  105. value={selectedPagesId}
  106. disabledValue={getPagesMapAndSelectedPagesId[2]}
  107. searchValue={searchValue}
  108. list={currentWorkspace?.pages || []}
  109. pagesMap={getPagesMapAndSelectedPagesId[0]}
  110. onSelect={handleSelectPages}
  111. canPreview={canPreview}
  112. previewPageId={previewPageId}
  113. onPreview={handlePreviewPage}
  114. />
  115. </div>
  116. </>
  117. )
  118. : (
  119. <NotionConnector onSetting={() => setShowAccountSettingModal({ payload: 'data-source', onCancelCallback: mutate })} />
  120. )
  121. }
  122. </div>
  123. )
  124. }
  125. export default NotionPageSelector