diff --git a/web/app/components/datasets/documents/list.tsx b/web/app/components/datasets/documents/list.tsx index 82b4d8127e..26195be647 100644 --- a/web/app/components/datasets/documents/list.tsx +++ b/web/app/components/datasets/documents/list.tsx @@ -33,383 +33,8 @@ import { useDocumentArchive, useDocumentDelete, useDocumentDisable, useDocumentE import { extensionToFileType } from '@/app/components/datasets/hit-testing/utils/extension-to-file-type' import useBatchEditDocumentMetadata from '../metadata/hooks/use-batch-edit-document-metadata' import EditMetadataBatchModal from '@/app/components/datasets/metadata/edit-metadata-batch/modal' -import { noop } from 'lodash-es' - -export const useIndexStatus = () => { - const { t } = useTranslation() - return { - queuing: { color: 'orange', text: t('datasetDocuments.list.status.queuing') }, // waiting - indexing: { color: 'blue', text: t('datasetDocuments.list.status.indexing') }, // indexing splitting parsing cleaning - paused: { color: 'orange', text: t('datasetDocuments.list.status.paused') }, // paused - error: { color: 'red', text: t('datasetDocuments.list.status.error') }, // error - available: { color: 'green', text: t('datasetDocuments.list.status.available') }, // completed,archived = false,enabled = true - enabled: { color: 'green', text: t('datasetDocuments.list.status.enabled') }, // completed,archived = false,enabled = true - disabled: { color: 'gray', text: t('datasetDocuments.list.status.disabled') }, // completed,archived = false,enabled = false - archived: { color: 'gray', text: t('datasetDocuments.list.status.archived') }, // completed,archived = true - } -} - -const STATUS_TEXT_COLOR_MAP: ColorMap = { - green: 'text-util-colors-green-green-600', - orange: 'text-util-colors-warning-warning-600', - red: 'text-util-colors-red-red-600', - blue: 'text-util-colors-blue-light-blue-light-600', - yellow: 'text-util-colors-warning-warning-600', - gray: 'text-text-tertiary', -} - -// status item for list -export const StatusItem: FC<{ - status: DocumentDisplayStatus - reverse?: boolean - scene?: 'list' | 'detail' - textCls?: string - errorMessage?: string - detail?: { - enabled: boolean - archived: boolean - id: string - } - datasetId?: string - onUpdate?: (operationName?: string) => void - -}> = ({ status, reverse = false, scene = 'list', textCls = '', errorMessage, datasetId = '', detail, onUpdate }) => { - const DOC_INDEX_STATUS_MAP = useIndexStatus() - const localStatus = status.toLowerCase() as keyof typeof DOC_INDEX_STATUS_MAP - const { enabled = false, archived = false, id = '' } = detail || {} - const { notify } = useContext(ToastContext) - const { t } = useTranslation() - const { mutateAsync: enableDocument } = useDocumentEnable() - const { mutateAsync: disableDocument } = useDocumentDisable() - const { mutateAsync: deleteDocument } = useDocumentDelete() - - const onOperate = async (operationName: OperationName) => { - let opApi = deleteDocument - switch (operationName) { - case 'enable': - opApi = enableDocument - break - case 'disable': - opApi = disableDocument - break - } - const [e] = await asyncRunSafe(opApi({ datasetId, documentId: id }) as Promise) - if (!e) { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - onUpdate?.() - // onUpdate?.(operationName) - } - else { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) } - } - - const { run: handleSwitch } = useDebounceFn((operationName: OperationName) => { - if (operationName === 'enable' && enabled) - return - if (operationName === 'disable' && !enabled) - return - onOperate(operationName) - }, { wait: 500 }) - - const embedding = useMemo(() => { - return ['queuing', 'indexing', 'paused'].includes(localStatus) - }, [localStatus]) - - return
- - - {DOC_INDEX_STATUS_MAP[localStatus]?.text} - - { - errorMessage && ( - {errorMessage}
- } - triggerClassName='ml-1 w-4 h-4' - /> - ) - } - { - scene === 'detail' && ( -
- - !archived && handleSwitch(v ? 'enable' : 'disable')} - disabled={embedding || archived} - size='md' - /> - -
- ) - } - -} - -type OperationName = 'delete' | 'archive' | 'enable' | 'disable' | 'sync' | 'un_archive' | 'pause' | 'resume' - -// operation action for list and detail -export const OperationAction: FC<{ - embeddingAvailable: boolean - detail: { - name: string - enabled: boolean - archived: boolean - id: string - data_source_type: string - doc_form: string - display_status?: string - } - datasetId: string - onUpdate: (operationName?: string) => void - scene?: 'list' | 'detail' - className?: string -}> = ({ embeddingAvailable, datasetId, detail, onUpdate, scene = 'list', className = '' }) => { - const downloadDocument = useDocumentDownload() - const { id, enabled = false, archived = false, data_source_type, display_status } = detail || {} - const [showModal, setShowModal] = useState(false) - const [deleting, setDeleting] = useState(false) - const { notify } = useContext(ToastContext) - const { t } = useTranslation() - const router = useRouter() - const { mutateAsync: archiveDocument } = useDocumentArchive() - const { mutateAsync: unArchiveDocument } = useDocumentUnArchive() - const { mutateAsync: enableDocument } = useDocumentEnable() - const { mutateAsync: disableDocument } = useDocumentDisable() - const { mutateAsync: deleteDocument } = useDocumentDelete() - const { mutateAsync: syncDocument } = useSyncDocument() - const { mutateAsync: syncWebsite } = useSyncWebsite() - const { mutateAsync: pauseDocument } = useDocumentPause() - const { mutateAsync: resumeDocument } = useDocumentResume() - const isListScene = scene === 'list' - - const onOperate = async (operationName: OperationName) => { - let opApi - switch (operationName) { - case 'archive': - opApi = archiveDocument - break - case 'un_archive': - opApi = unArchiveDocument - break - case 'enable': - opApi = enableDocument - break - case 'disable': - opApi = disableDocument - break - case 'sync': - if (data_source_type === 'notion_import') - opApi = syncDocument - else - opApi = syncWebsite - break - case 'pause': - opApi = pauseDocument - break - case 'resume': - opApi = resumeDocument - break - default: - opApi = deleteDocument - setDeleting(true) - break - } - const [e] = await asyncRunSafe(opApi({ datasetId, documentId: id }) as Promise) - if (!e) { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - onUpdate(operationName) - } - else { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) } - if (operationName === 'delete') - setDeleting(false) - } - - const { run: handleSwitch } = useDebounceFn((operationName: OperationName) => { - if (operationName === 'enable' && enabled) - return - if (operationName === 'disable' && !enabled) - return - onOperate(operationName) - }, { wait: 500 }) - - const [currDocument, setCurrDocument] = useState<{ - id: string - name: string - } | null>(null) - const [isShowRenameModal, { - setTrue: setShowRenameModalTrue, - setFalse: setShowRenameModalFalse, - }] = useBoolean(false) - const handleShowRenameModal = useCallback((doc: { - id: string - name: string - }) => { - setCurrDocument(doc) - setShowRenameModalTrue() - }, [setShowRenameModalTrue]) - const handleRenamed = useCallback(() => { - onUpdate() - }, [onUpdate]) - - return
e.stopPropagation()}> - {isListScene && !embeddingAvailable && ( - - )} - {isListScene && embeddingAvailable && ( - <> - {archived - ? -
- -
-
- : handleSwitch(v ? 'enable' : 'disable')} size='md' /> - } - - - )} - {embeddingAvailable && ( - <> - - - - - - - - {!archived && ( - <> -
{ - handleShowRenameModal({ - id: detail.id, - name: detail.name, - }) - }}> - - {t('datasetDocuments.list.table.rename')} -
- {['notion_import', DataSourceType.WEB].includes(data_source_type) && ( -
onOperate('sync')}> - - {t('datasetDocuments.list.action.sync')} -
- )} - - - )} - {!archived && display_status?.toLowerCase() === 'indexing' && ( -
onOperate('pause')}> - - {t('datasetDocuments.list.action.pause')} -
- )} - {!archived && display_status?.toLowerCase() === 'paused' && ( -
onOperate('resume')}> - - {t('datasetDocuments.list.action.resume')} -
- )} - {!archived &&
onOperate('archive')}> - - {t('datasetDocuments.list.action.archive')} -
} - {archived && ( -
onOperate('un_archive')}> - - {t('datasetDocuments.list.action.unarchive')} -
- )} -
setShowModal(true)}> - - {t('datasetDocuments.list.action.delete')} -
-
- } - trigger='click' - position='br' - btnElement={ -
- -
- } - btnClassName={open => cn(isListScene ? s.actionIconWrapperList : s.actionIconWrapperDetail, open ? '!hover:bg-state-base-hover !shadow-none' : '!bg-transparent')} - popupClassName='!w-full' - className={`!z-20 flex h-fit !w-[200px] justify-end ${className}`} - /> - - )} - {showModal - && onOperate('delete')} - onCancel={() => setShowModal(false)} - /> - } - - {isShowRenameModal && currDocument && ( - - )} - -} +import StatusItem from './status-item' +import Operations from './operations' export const renderTdValue = (value: string | number | null, isEmptyStyle = false) => { return ( diff --git a/web/app/components/datasets/documents/operations.tsx b/web/app/components/datasets/documents/operations.tsx index 18cc2908b3..66289870c8 100644 --- a/web/app/components/datasets/documents/operations.tsx +++ b/web/app/components/datasets/documents/operations.tsx @@ -3,7 +3,18 @@ import { useContext } from 'use-context-selector' import { ToastContext } from '../../base/toast' import { useTranslation } from 'react-i18next' import { useRouter } from 'next/navigation' -import { useDocumentArchive, useDocumentDelete, useDocumentDisable, useDocumentEnable, useDocumentPause, useDocumentResume, useDocumentUnArchive, useSyncDocument, useSyncWebsite } from '@/service/knowledge/use-document' +import { + useDocumentArchive, + useDocumentDelete, + useDocumentDisable, + useDocumentDownload, + useDocumentEnable, + useDocumentPause, + useDocumentResume, + useDocumentUnArchive, + useSyncDocument, + useSyncWebsite, +} from '@/service/knowledge/use-document' import type { OperationName } from './types' import { asyncRunSafe } from '@/utils' import type { CommonResponse } from '@/models/common' @@ -13,7 +24,17 @@ import { noop } from 'lodash-es' import Tooltip from '../../base/tooltip' import Divider from '../../base/divider' import cn from '@/utils/classnames' -import { RiArchive2Line, RiDeleteBinLine, RiEditLine, RiEqualizer2Line, RiLoopLeftLine, RiMoreFill, RiPauseCircleLine, RiPlayCircleLine } from '@remixicon/react' +import { + RiArchive2Line, + RiDeleteBinLine, + RiDownloadLine, + RiEditLine, + RiEqualizer2Line, + RiLoopLeftLine, + RiMoreFill, + RiPauseCircleLine, + RiPlayCircleLine, +} from '@remixicon/react' import CustomPopover from '../../base/popover' import s from './style.module.css' import { DataSourceType } from '@/models/datasets' @@ -60,6 +81,7 @@ const Operations = ({ const { mutateAsync: syncWebsite } = useSyncWebsite() const { mutateAsync: pauseDocument } = useDocumentPause() const { mutateAsync: resumeDocument } = useDocumentResume() + const downloadDocument = useDocumentDownload() const isListScene = scene === 'list' const onOperate = async (operationName: OperationName) => { @@ -141,7 +163,6 @@ const Operations = ({ ?
@@ -154,9 +175,36 @@ const Operations = ({ )} {embeddingAvailable && ( <> + + +