'use client' import type { FC } from 'react' import { useRouter } from 'next/navigation' import { useCallback } from 'react' import Loading from '@/app/components/base/loading' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useProviderContext } from '@/context/provider-context' import { DataSourceType } from '@/models/datasets' import { useDocumentList, useInvalidDocumentDetail, useInvalidDocumentList } from '@/service/knowledge/use-document' import { useChildSegmentListKey, useSegmentListKey } from '@/service/knowledge/use-segment' import { useInvalid } from '@/service/use-base' import useEditDocumentMetadata from '../metadata/hooks/use-edit-dataset-metadata' import DocumentsHeader from './components/documents-header' import EmptyElement from './components/empty-element' import List from './components/list' import { useDocumentsPageState } from './hooks/use-documents-page-state' type IDocumentsProps = { datasetId: string } const POLLING_INTERVAL = 2500 const TERMINAL_INDEXING_STATUSES = new Set(['completed', 'paused', 'error']) const FORCED_POLLING_STATUSES = new Set(['queuing', 'indexing', 'paused']) const Documents: FC = ({ datasetId }) => { const router = useRouter() const { plan } = useProviderContext() const isFreePlan = plan.type === 'sandbox' const dataset = useDatasetDetailContextWithSelector(s => s.dataset) const embeddingAvailable = !!dataset?.embedding_available // Use custom hook for page state management const { inputValue, debouncedSearchValue, handleInputChange, statusFilterValue, sortValue, normalizedStatusFilterValue, handleStatusFilterChange, handleStatusFilterClear, handleSortChange, currPage, limit, handlePageChange, handleLimitChange, selectedIds, setSelectedIds, } = useDocumentsPageState() // Fetch document list const { data: documentsRes, isLoading: isListLoading } = useDocumentList({ datasetId, query: { page: currPage + 1, limit, keyword: debouncedSearchValue, status: normalizedStatusFilterValue, sort: sortValue, }, refetchInterval: (query) => { const shouldForcePolling = normalizedStatusFilterValue !== 'all' && FORCED_POLLING_STATUSES.has(normalizedStatusFilterValue) const documents = query.state.data?.data if (!documents) return POLLING_INTERVAL const hasIncompleteDocuments = documents.some(({ indexing_status }) => !TERMINAL_INDEXING_STATUSES.has(indexing_status)) return shouldForcePolling || hasIncompleteDocuments ? POLLING_INTERVAL : false }, }) // Invalidation hooks const invalidDocumentList = useInvalidDocumentList(datasetId) const invalidDocumentDetail = useInvalidDocumentDetail() const invalidChunkList = useInvalid(useSegmentListKey) const invalidChildChunkList = useInvalid(useChildSegmentListKey) const handleUpdate = useCallback(() => { invalidDocumentList() invalidDocumentDetail() setTimeout(() => { invalidChunkList() invalidChildChunkList() }, 5000) }, [invalidDocumentList, invalidDocumentDetail, invalidChunkList, invalidChildChunkList]) // Metadata editing hook const { isShowEditModal: isShowEditMetadataModal, showEditModal: showEditMetadataModal, hideEditModal: hideEditMetadataModal, datasetMetaData, handleAddMetaData, handleRename, handleDeleteMetaData, builtInEnabled, setBuiltInEnabled, builtInMetaData, } = useEditDocumentMetadata({ datasetId, dataset, onUpdateDocList: invalidDocumentList, }) // Route to document creation page const routeToDocCreate = useCallback(() => { if (dataset?.runtime_mode === 'rag_pipeline') { router.push(`/datasets/${datasetId}/documents/create-from-pipeline`) return } router.push(`/datasets/${datasetId}/documents/create`) }, [dataset?.runtime_mode, datasetId, router]) const total = documentsRes?.total || 0 const documentsList = documentsRes?.data // Render content based on loading and data state const renderContent = () => { if (isListLoading && !documentsRes) return if (total > 0) { return ( ) } const isDataSourceNotion = dataset?.data_source_type === DataSourceType.NOTION return ( ) } return (
{renderContent()}
) } export default Documents