| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 | '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 cn from 'classnames'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 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'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 { notify } = useContext(ToastContext)  const { dataset } = useDatasetDetailContext()  const embeddingAvailable = !!dataset?.embedding_available  const [showMetadata, setShowMetadata] = useState(true)  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 h-16 border-b-gray-100 border-b items-center p-4'>          <div onClick={backToPrev} className={'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} />          <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={{              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 className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}>          {isDetailLoading            ? <Loading type='app' />            : <div className={`box-border h-full w-full overflow-y-scroll ${embedding ? 'py-12 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>          }          {showMetadata && <Metadata            docDetail={{ ...documentDetail, ...documentMetadata, doc_type: documentDetail?.doc_type === 'others' ? '' : documentDetail?.doc_type } as any}            loading={isMetadataLoading}            onUpdate={metadataMutate}          />}        </div>        <BatchModal          isShow={batchModalVisible}          onCancel={hideBatchModal}          onConfirm={runBatch}          docForm={documentDetail?.doc_form as DocForm}        />      </div>    </DocumentContext.Provider>  )}export default DocumentDetail
 |