Spaces:
Build error
Build error
'use client' | |
import type { FC } from 'react' | |
import React, { useState } from 'react' | |
import useSWR from 'swr' | |
import { ArrowLeftIcon } from '@heroicons/react/24/solid' | |
import { createContext, useContext } from 'use-context-selector' | |
import { useTranslation } from 'react-i18next' | |
import { useRouter } from 'next/navigation' | |
import { omit } from 'lodash-es' | |
import { OperationAction, StatusItem } from '../list' | |
import s from '../style.module.css' | |
import Completed from './completed' | |
import Embedding from './embedding' | |
import Metadata from './metadata' | |
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 type { MetadataType } from '@/service/datasets' | |
import { checkSegmentBatchImportProgress, fetchDocumentDetail, segmentBatchImport } from '@/service/datasets' | |
import { ToastContext } from '@/app/components/base/toast' | |
import type { DocForm } 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' | |
export const DocumentContext = createContext<{ datasetId?: string; documentId?: string; docForm: string }>({ docForm: '' }) | |
type DocumentTitleProps = { | |
extension?: string | |
name?: string | |
iconCls?: string | |
textCls?: string | |
wrapperCls?: string | |
} | |
export const DocumentTitle: FC<DocumentTitleProps> = ({ extension, name, iconCls, textCls, wrapperCls }) => { | |
const localExtension = extension?.toLowerCase() || name?.split('.')?.pop()?.toLowerCase() | |
return <div className={cn('flex items-center justify-start flex-1', wrapperCls)}> | |
<div className={cn(s[`${localExtension || 'txt'}Icon`], style.titleIcon, iconCls)}></div> | |
<span className={cn('font-semibold text-lg text-gray-900 ml-1', textCls)}> {name || '--'}</span> | |
</div> | |
} | |
type Props = { | |
datasetId: string | |
documentId: string | |
} | |
const DocumentDetail: FC<Props> = ({ 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<ProcessStatus | string>() | |
const showNewSegmentModal = () => setNewSegmentModalVisible(true) | |
const showBatchModal = () => setBatchModalVisible(true) | |
const hideBatchModal = () => setBatchModalVisible(false) | |
const resetProcessStatus = () => setImportStatus('') | |
const checkProcess = async (jobID: string) => { | |
try { | |
const res = await checkSegmentBatchImportProgress({ jobID }) | |
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')}` }) | |
} | |
catch (e: any) { | |
notify({ type: 'error', message: `${t('datasetDocuments.list.batchModal.runError')}${'message' in e ? `: ${e.message}` : ''}` }) | |
} | |
} | |
const runBatch = async (csv: File) => { | |
const formData = new FormData() | |
formData.append('file', csv) | |
try { | |
const res = await segmentBatchImport({ | |
url: `/datasets/${datasetId}/documents/${documentId}/segments/batch_import`, | |
body: formData, | |
}) | |
setImportStatus(res.job_status) | |
checkProcess(res.job_id) | |
} | |
catch (e: any) { | |
notify({ type: 'error', message: `${t('datasetDocuments.list.batchModal.runError')}${'message' in e ? `: ${e.message}` : ''}` }) | |
} | |
} | |
const { data: documentDetail, error, mutate: detailMutate } = useSWR({ | |
action: 'fetchDocumentDetail', | |
datasetId, | |
documentId, | |
params: { metadata: 'without' as MetadataType }, | |
}, apiParams => fetchDocumentDetail(omit(apiParams, 'action'))) | |
const { data: documentMetadata, error: metadataErr, mutate: metadataMutate } = useSWR({ | |
action: 'fetchDocumentDetail', | |
datasetId, | |
documentId, | |
params: { metadata: 'only' as MetadataType }, | |
}, apiParams => fetchDocumentDetail(omit(apiParams, 'action')), | |
) | |
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 handleOperate = (operateName?: string) => { | |
if (operateName === 'delete') | |
backToPrev() | |
else | |
detailMutate() | |
} | |
return ( | |
<DocumentContext.Provider value={{ datasetId, documentId, docForm: documentDetail?.doc_form || '' }}> | |
<div className='flex flex-col h-full'> | |
<div className='flex min-h-16 border-b-gray-100 border-b items-center p-4 justify-between flex-wrap gap-y-2'> | |
<div onClick={backToPrev} className={'shrink-0 rounded-full w-8 h-8 flex justify-center items-center border-gray-100 cursor-pointer border hover:border-gray-300 shadow-[0px_12px_16px_-4px_rgba(16,24,40,0.08),0px_4px_6px_-2px_rgba(16,24,40,0.03)]'}> | |
<ArrowLeftIcon className='text-primary-600 fill-current stroke-current h-4 w-4' /> | |
</div> | |
<Divider className='!h-4' type='vertical' /> | |
<DocumentTitle extension={documentDetail?.data_source_info?.upload_file?.extension} name={documentDetail?.name} /> | |
<div className='flex items-center flex-wrap gap-y-2'> | |
<StatusItem status={documentDetail?.display_status || 'available'} scene='detail' errorMessage={documentDetail?.error || ''} /> | |
{embeddingAvailable && documentDetail && !documentDetail.archived && ( | |
<SegmentAdd | |
importStatus={importStatus} | |
clearProcessStatus={resetProcessStatus} | |
showNewSegmentModal={showNewSegmentModal} | |
showBatchModal={showBatchModal} | |
/> | |
)} | |
<OperationAction | |
scene='detail' | |
embeddingAvailable={embeddingAvailable} | |
detail={{ | |
name: documentDetail?.name || '', | |
enabled: documentDetail?.enabled || false, | |
archived: documentDetail?.archived || false, | |
id: documentId, | |
data_source_type: documentDetail?.data_source_type || '', | |
doc_form: documentDetail?.doc_form || '', | |
}} | |
datasetId={datasetId} | |
onUpdate={handleOperate} | |
className='!w-[216px]' | |
/> | |
<button | |
className={cn(style.layoutRightIcon, showMetadata ? style.iconShow : style.iconClose)} | |
onClick={() => setShowMetadata(!showMetadata)} | |
/> | |
</div> | |
</div> | |
<div className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}> | |
{isDetailLoading | |
? <Loading type='app' /> | |
: <div className={`h-full w-full flex flex-col ${embedding ? 'px-6 py-3 sm:py-12 sm:px-16' : 'pb-[30px] pt-3 px-6'}`}> | |
{embedding | |
? <Embedding detail={documentDetail} detailUpdate={detailMutate} /> | |
: <Completed | |
embeddingAvailable={embeddingAvailable} | |
showNewSegmentModal={newSegmentModalVisible} | |
onNewSegmentModalChange={setNewSegmentModalVisible} | |
importStatus={importStatus} | |
archived={documentDetail?.archived} | |
/> | |
} | |
</div> | |
} | |
<FloatRightContainer showClose isOpen={showMetadata} onClose={() => setShowMetadata(false)} isMobile={isMobile} panelClassname='!justify-start' footer={null}> | |
<Metadata | |
docDetail={{ ...documentDetail, ...documentMetadata, doc_type: documentMetadata?.doc_type === 'others' ? '' : documentMetadata?.doc_type } as any} | |
loading={isMetadataLoading} | |
onUpdate={metadataMutate} | |
/> | |
</FloatRightContainer> | |
</div> | |
<BatchModal | |
isShow={batchModalVisible} | |
onCancel={hideBatchModal} | |
onConfirm={runBatch} | |
docForm={documentDetail?.doc_form as DocForm} | |
/> | |
</div> | |
</DocumentContext.Provider> | |
) | |
} | |
export default DocumentDetail | |