'use client' import type { FC } from 'react' import React, { useMemo, useState } from 'react' import { createContext, useContext, useContextSelector } from 'use-context-selector' import { useTranslation } from 'react-i18next' import { useRouter } from 'next/navigation' import { RiArrowLeftLine, RiLayoutRight2Line } from '@remixicon/react' import { OperationAction, StatusItem } from '../list' import DocumentPicker from '../../common/document-picker' import Completed from './completed' import Embedding from './embedding' import Metadata from '@/app/components/datasets/metadata/metadata-document' import SegmentAdd, { ProcessStatus } from './segment-add' import BatchModal from './batch-modal' import style from './style.module.css' import cn from '@/utils/classnames' import Divider from '@/app/components/base/divider' import Loading from '@/app/components/base/loading' import { ToastContext } from '@/app/components/base/toast' import type { ChunkingMode, ParentMode, ProcessMode } from '@/models/datasets' import { useDatasetDetailContext } from '@/context/dataset-detail' import FloatRightContainer from '@/app/components/base/float-right-container' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import { LayoutRight2LineMod } from '@/app/components/base/icons/src/public/knowledge' import { useCheckSegmentBatchImportProgress, useChildSegmentListKey, useSegmentBatchImport, useSegmentListKey } from '@/service/knowledge/use-segment' import { useDocumentDetail, useDocumentMetadata, useInvalidDocumentList } from '@/service/knowledge/use-document' import { useInvalid } from '@/service/use-base' type DocumentContextValue = { datasetId?: string documentId?: string docForm: string mode?: ProcessMode parentMode?: ParentMode } export const DocumentContext = createContext({ docForm: '' }) export const useDocumentContext = (selector: (value: DocumentContextValue) => any) => { return useContextSelector(DocumentContext, selector) } type DocumentTitleProps = { datasetId: string extension?: string name?: string processMode?: ProcessMode parent_mode?: ParentMode iconCls?: string textCls?: string wrapperCls?: string } export const DocumentTitle: FC = ({ datasetId, extension, name, processMode, parent_mode, wrapperCls }) => { const router = useRouter() return (
{ router.push(`/datasets/${datasetId}/documents/${doc.id}`) }} />
) } type Props = { datasetId: string documentId: string } const DocumentDetail: FC = ({ datasetId, documentId }) => { const router = useRouter() const { t } = useTranslation() const media = useBreakpoints() const isMobile = media === MediaType.mobile const { notify } = useContext(ToastContext) const { dataset } = useDatasetDetailContext() const embeddingAvailable = !!dataset?.embedding_available const [showMetadata, setShowMetadata] = useState(!isMobile) const [newSegmentModalVisible, setNewSegmentModalVisible] = useState(false) const [batchModalVisible, setBatchModalVisible] = useState(false) const [importStatus, setImportStatus] = useState() const showNewSegmentModal = () => setNewSegmentModalVisible(true) const showBatchModal = () => setBatchModalVisible(true) const hideBatchModal = () => setBatchModalVisible(false) const resetProcessStatus = () => setImportStatus('') const { mutateAsync: checkSegmentBatchImportProgress } = useCheckSegmentBatchImportProgress() const checkProcess = async (jobID: string) => { await checkSegmentBatchImportProgress({ jobID }, { onSuccess: (res) => { setImportStatus(res.job_status) if (res.job_status === ProcessStatus.WAITING || res.job_status === ProcessStatus.PROCESSING) setTimeout(() => checkProcess(res.job_id), 2500) if (res.job_status === ProcessStatus.ERROR) notify({ type: 'error', message: `${t('datasetDocuments.list.batchModal.runError')}` }) }, onError: (e) => { notify({ type: 'error', message: `${t('datasetDocuments.list.batchModal.runError')}${'message' in e ? `: ${e.message}` : ''}` }) }, }) } const { mutateAsync: segmentBatchImport } = useSegmentBatchImport() const runBatch = async (csv: File) => { const formData = new FormData() formData.append('file', csv) await segmentBatchImport({ url: `/datasets/${datasetId}/documents/${documentId}/segments/batch_import`, body: formData, }, { onSuccess: (res) => { setImportStatus(res.job_status) checkProcess(res.job_id) }, onError: (e) => { notify({ type: 'error', message: `${t('datasetDocuments.list.batchModal.runError')}${'message' in e ? `: ${e.message}` : ''}` }) }, }) } const { data: documentDetail, error, refetch: detailMutate } = useDocumentDetail({ datasetId, documentId, params: { metadata: 'without' }, }) const { data: documentMetadata, error: metadataErr, refetch: metadataMutate } = useDocumentMetadata({ datasetId, documentId, params: { metadata: 'only' }, }) const backToPrev = () => { router.push(`/datasets/${datasetId}/documents`) } const isDetailLoading = !documentDetail && !error const isMetadataLoading = !documentMetadata && !metadataErr const embedding = ['queuing', 'indexing', 'paused'].includes((documentDetail?.display_status || '').toLowerCase()) const invalidChunkList = useInvalid(useSegmentListKey) const invalidChildChunkList = useInvalid(useChildSegmentListKey) const invalidDocumentList = useInvalidDocumentList(datasetId) const handleOperate = (operateName?: string) => { invalidDocumentList() if (operateName === 'delete') { backToPrev() } else { detailMutate() // If operation is not rename, refresh the chunk list after 5 seconds if (operateName) { setTimeout(() => { invalidChunkList() invalidChildChunkList() }, 5000) } } } const mode = useMemo(() => { return documentDetail?.document_process_rule?.mode }, [documentDetail?.document_process_rule]) const parentMode = useMemo(() => { return documentDetail?.document_process_rule?.rules?.parent_mode }, [documentDetail?.document_process_rule]) const isFullDocMode = useMemo(() => { return mode === 'hierarchical' && parentMode === 'full-doc' }, [mode, parentMode]) return (
{embeddingAvailable && documentDetail && !documentDetail.archived && !isFullDocMode && ( <> )}
{isDetailLoading ? :
{embedding ? : }
} setShowMetadata(false)} isMobile={isMobile} panelClassname='!justify-start' footer={null}>
) } export default DocumentDetail