diff --git a/web/app/components/datasets/documents/list.tsx b/web/app/components/datasets/documents/list.tsx index 0d3a8d5000..a56b7d5614 100644 --- a/web/app/components/datasets/documents/list.tsx +++ b/web/app/components/datasets/documents/list.tsx @@ -1,20 +1,12 @@ 'use client' import type { FC } from 'react' import React, { useCallback, useEffect, useMemo, useState } from 'react' -import { useBoolean, useDebounceFn } from 'ahooks' +import { useBoolean } from 'ahooks' import { ArrowDownIcon } from '@heroicons/react/24/outline' import { pick, uniq } from 'lodash-es' import { - RiArchive2Line, - RiDeleteBinLine, RiEditLine, - RiEqualizer2Line, - RiLoopLeftLine, - RiMoreFill, - RiPauseCircleLine, - RiPlayCircleLine, } from '@remixicon/react' -import { useContext } from 'use-context-selector' import { useRouter } from 'next/navigation' import { useTranslation } from 'react-i18next' import dayjs from 'dayjs' @@ -26,12 +18,11 @@ import RenameModal from './rename-modal' import BatchAction from './detail/completed/common/batch-action' import cn from '@/utils/classnames' import Tooltip from '@/app/components/base/tooltip' -import Toast, { ToastContext } from '@/app/components/base/toast' +import Toast from '@/app/components/base/toast' import { asyncRunSafe } from '@/utils' import { formatNumber } from '@/utils/format' import NotionIcon from '@/app/components/base/notion-icon' import ProgressBar from '@/app/components/base/progress-bar' -import type { DocumentDisplayStatus } from '@/models/datasets' import { ChunkingMode, DataSourceType, DocumentActionType, type SimpleDocumentDetail } from '@/models/datasets' import type { CommonResponse } from '@/models/common' import useTimestamp from '@/hooks/use-timestamp' @@ -39,369 +30,12 @@ import { useDatasetDetailContextWithSelector as useDatasetDetailContext } from ' import type { Props as PaginationProps } from '@/app/components/base/pagination' import Pagination from '@/app/components/base/pagination' import Checkbox from '@/app/components/base/checkbox' -import { useDocumentArchive, useDocumentDelete, useDocumentDisable, useDocumentEnable, useDocumentPause, useDocumentResume, useDocumentUnArchive, useSyncDocument, useSyncWebsite } from '@/service/knowledge/use-document' +import { useDocumentArchive, useDocumentDelete, useDocumentDisable, useDocumentEnable } from '@/service/knowledge/use-document' 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 StatusItem from './status-item' -// import Operations from './operations' -import { noop } from 'lodash-es' -import type { ColorMap, IndicatorProps } from '../../header/indicator' -import Indicator from '../../header/indicator' -import Switch from '../../base/switch' -import Divider from '../../base/divider' -import CustomPopover from '../../base/popover' -import Confirm from '../../base/confirm' - -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 { 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 ( @@ -650,7 +284,7 @@ const DocumentList: FC = ({ } - void @@ -44,12 +45,12 @@ const Operations = ({ scene = 'list', className = '', }: OperationsProps) => { - const { t } = useTranslation() - const router = useRouter() - const { id, enabled = false, archived = false, data_source_type } = detail || {} + 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() @@ -57,6 +58,8 @@ const Operations = ({ 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) => { @@ -80,6 +83,12 @@ const Operations = ({ else opApi = syncWebsite break + case 'pause': + opApi = pauseDocument + break + case 'resume': + opApi = resumeDocument + break default: opApi = deleteDocument setDeleting(true) @@ -181,6 +190,18 @@ const Operations = ({ )} + {!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')} diff --git a/web/app/components/datasets/documents/types.ts b/web/app/components/datasets/documents/types.ts index 8934fe661f..b1e13b8d4f 100644 --- a/web/app/components/datasets/documents/types.ts +++ b/web/app/components/datasets/documents/types.ts @@ -1 +1 @@ -export type OperationName = 'delete' | 'archive' | 'enable' | 'disable' | 'sync' | 'un_archive' +export type OperationName = 'delete' | 'archive' | 'enable' | 'disable' | 'sync' | 'un_archive' | 'pause' | 'resume'