diff --git a/web/app/components/datasets/list/dataset-card/components/corner-labels.tsx b/web/app/components/datasets/list/dataset-card/components/corner-labels.tsx
new file mode 100644
index 0000000000..03ca543ee7
--- /dev/null
+++ b/web/app/components/datasets/list/dataset-card/components/corner-labels.tsx
@@ -0,0 +1,36 @@
+import type { DataSet } from '@/models/datasets'
+import * as React from 'react'
+import { useTranslation } from 'react-i18next'
+import CornerLabel from '@/app/components/base/corner-label'
+
+type CornerLabelsProps = {
+ dataset: DataSet
+}
+
+const CornerLabels = ({ dataset }: CornerLabelsProps) => {
+ const { t } = useTranslation()
+
+ if (!dataset.embedding_available) {
+ return (
+
+ )
+ }
+
+ if (dataset.runtime_mode === 'rag_pipeline') {
+ return (
+
+ )
+ }
+
+ return null
+}
+
+export default React.memo(CornerLabels)
diff --git a/web/app/components/datasets/list/dataset-card/components/dataset-card-footer.tsx b/web/app/components/datasets/list/dataset-card/components/dataset-card-footer.tsx
new file mode 100644
index 0000000000..854f34f49c
--- /dev/null
+++ b/web/app/components/datasets/list/dataset-card/components/dataset-card-footer.tsx
@@ -0,0 +1,62 @@
+import type { DataSet } from '@/models/datasets'
+import { RiFileTextFill, RiRobot2Fill } from '@remixicon/react'
+import * as React from 'react'
+import { useMemo } from 'react'
+import { useTranslation } from 'react-i18next'
+import Tooltip from '@/app/components/base/tooltip'
+import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
+import { cn } from '@/utils/classnames'
+
+const EXTERNAL_PROVIDER = 'external'
+
+type DatasetCardFooterProps = {
+ dataset: DataSet
+}
+
+const DatasetCardFooter = ({ dataset }: DatasetCardFooterProps) => {
+ const { t } = useTranslation()
+ const { formatTimeFromNow } = useFormatTimeFromNow()
+ const isExternalProvider = dataset.provider === EXTERNAL_PROVIDER
+
+ const documentCount = useMemo(() => {
+ const availableDocCount = dataset.total_available_documents ?? 0
+ if (availableDocCount < dataset.document_count)
+ return `${availableDocCount} / ${dataset.document_count}`
+ return `${dataset.document_count}`
+ }, [dataset.document_count, dataset.total_available_documents])
+
+ const documentCountTooltip = useMemo(() => {
+ const availableDocCount = dataset.total_available_documents ?? 0
+ if (availableDocCount < dataset.document_count)
+ return t('partialEnabled', { ns: 'dataset', count: dataset.document_count, num: availableDocCount })
+ return t('docAllEnabled', { ns: 'dataset', count: availableDocCount })
+ }, [t, dataset.document_count, dataset.total_available_documents])
+
+ return (
+
+
+
+
+ {documentCount}
+
+
+ {!isExternalProvider && (
+
+
+
+ {dataset.app_count}
+
+
+ )}
+
/
+
{`${t('updated', { ns: 'dataset' })} ${formatTimeFromNow(dataset.updated_at * 1000)}`}
+
+ )
+}
+
+export default React.memo(DatasetCardFooter)
diff --git a/web/app/components/datasets/list/dataset-card/components/dataset-card-header.tsx b/web/app/components/datasets/list/dataset-card/components/dataset-card-header.tsx
new file mode 100644
index 0000000000..abe7595e14
--- /dev/null
+++ b/web/app/components/datasets/list/dataset-card/components/dataset-card-header.tsx
@@ -0,0 +1,148 @@
+import type { DataSet } from '@/models/datasets'
+import * as React from 'react'
+import { useMemo } from 'react'
+import { useTranslation } from 'react-i18next'
+import AppIcon from '@/app/components/base/app-icon'
+import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
+import { useKnowledge } from '@/hooks/use-knowledge'
+import { DOC_FORM_ICON_WITH_BG, DOC_FORM_TEXT } from '@/models/datasets'
+import { cn } from '@/utils/classnames'
+
+const EXTERNAL_PROVIDER = 'external'
+
+type DatasetCardHeaderProps = {
+ dataset: DataSet
+}
+
+// DocModeInfo component - placed before usage
+type DocModeInfoProps = {
+ dataset: DataSet
+ isExternalProvider: boolean
+ isShowDocModeInfo: boolean
+}
+
+const DocModeInfo = ({
+ dataset,
+ isExternalProvider,
+ isShowDocModeInfo,
+}: DocModeInfoProps) => {
+ const { t } = useTranslation()
+ const { formatIndexingTechniqueAndMethod } = useKnowledge()
+
+ if (isExternalProvider) {
+ return (
+
+ {t('externalKnowledgeBase', { ns: 'dataset' })}
+
+ )
+ }
+
+ if (!isShowDocModeInfo)
+ return null
+
+ const indexingText = dataset.indexing_technique
+ ? formatIndexingTechniqueAndMethod(
+ dataset.indexing_technique as 'economy' | 'high_quality',
+ dataset.retrieval_model_dict?.search_method as Parameters[1],
+ )
+ : ''
+
+ return (
+
+ {dataset.doc_form && (
+
+ {t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`, { ns: 'dataset' })}
+
+ )}
+ {dataset.indexing_technique && indexingText && (
+
+ {indexingText}
+
+ )}
+ {dataset.is_multimodal && (
+
+ {t('multimodal', { ns: 'dataset' })}
+
+ )}
+
+ )
+}
+
+// Main DatasetCardHeader component
+const DatasetCardHeader = ({ dataset }: DatasetCardHeaderProps) => {
+ const { t } = useTranslation()
+ const { formatTimeFromNow } = useFormatTimeFromNow()
+
+ const isExternalProvider = dataset.provider === EXTERNAL_PROVIDER
+
+ const isShowChunkingModeIcon = dataset.doc_form && (dataset.runtime_mode !== 'rag_pipeline' || dataset.is_published)
+ const isShowDocModeInfo = Boolean(
+ dataset.doc_form
+ && dataset.indexing_technique
+ && dataset.retrieval_model_dict?.search_method
+ && (dataset.runtime_mode !== 'rag_pipeline' || dataset.is_published),
+ )
+
+ const chunkingModeIcon = dataset.doc_form ? DOC_FORM_ICON_WITH_BG[dataset.doc_form] : React.Fragment
+ const Icon = isExternalProvider ? DOC_FORM_ICON_WITH_BG.external : chunkingModeIcon
+
+ const iconInfo = useMemo(() => dataset.icon_info || {
+ icon: '๐',
+ icon_type: 'emoji' as const,
+ icon_background: '#FFF4ED',
+ icon_url: '',
+ }, [dataset.icon_info])
+
+ const editTimeText = useMemo(
+ () => `${t('segment.editedAt', { ns: 'datasetDocuments' })} ${formatTimeFromNow(dataset.updated_at * 1000)}`,
+ [t, dataset.updated_at, formatTimeFromNow],
+ )
+
+ return (
+
+
+
+ {(isShowChunkingModeIcon || isExternalProvider) && (
+
+
+
+ )}
+
+
+
+ {dataset.name}
+
+
+
{dataset.author_name}
+
ยท
+
{editTimeText}
+
+
+
+
+ )
+}
+
+export default React.memo(DatasetCardHeader)
diff --git a/web/app/components/datasets/list/dataset-card/components/dataset-card-modals.tsx b/web/app/components/datasets/list/dataset-card/components/dataset-card-modals.tsx
new file mode 100644
index 0000000000..8162bc94c4
--- /dev/null
+++ b/web/app/components/datasets/list/dataset-card/components/dataset-card-modals.tsx
@@ -0,0 +1,55 @@
+import type { DataSet } from '@/models/datasets'
+import * as React from 'react'
+import { useTranslation } from 'react-i18next'
+import Confirm from '@/app/components/base/confirm'
+import RenameDatasetModal from '../../../rename-modal'
+
+type ModalState = {
+ showRenameModal: boolean
+ showConfirmDelete: boolean
+ confirmMessage: string
+}
+
+type DatasetCardModalsProps = {
+ dataset: DataSet
+ modalState: ModalState
+ onCloseRename: () => void
+ onCloseConfirm: () => void
+ onConfirmDelete: () => void
+ onSuccess?: () => void
+}
+
+const DatasetCardModals = ({
+ dataset,
+ modalState,
+ onCloseRename,
+ onCloseConfirm,
+ onConfirmDelete,
+ onSuccess,
+}: DatasetCardModalsProps) => {
+ const { t } = useTranslation()
+
+ return (
+ <>
+ {modalState.showRenameModal && (
+
+ )}
+ {modalState.showConfirmDelete && (
+
+ )}
+ >
+ )
+}
+
+export default React.memo(DatasetCardModals)
diff --git a/web/app/components/datasets/list/dataset-card/components/description.tsx b/web/app/components/datasets/list/dataset-card/components/description.tsx
new file mode 100644
index 0000000000..79604e92ab
--- /dev/null
+++ b/web/app/components/datasets/list/dataset-card/components/description.tsx
@@ -0,0 +1,18 @@
+import type { DataSet } from '@/models/datasets'
+import * as React from 'react'
+import { cn } from '@/utils/classnames'
+
+type DescriptionProps = {
+ dataset: DataSet
+}
+
+const Description = ({ dataset }: DescriptionProps) => (
+
+ {dataset.description}
+
+)
+
+export default React.memo(Description)
diff --git a/web/app/components/datasets/list/dataset-card/components/operations-popover.tsx b/web/app/components/datasets/list/dataset-card/components/operations-popover.tsx
new file mode 100644
index 0000000000..80ae2fb7a1
--- /dev/null
+++ b/web/app/components/datasets/list/dataset-card/components/operations-popover.tsx
@@ -0,0 +1,52 @@
+import type { DataSet } from '@/models/datasets'
+import { RiMoreFill } from '@remixicon/react'
+import * as React from 'react'
+import CustomPopover from '@/app/components/base/popover'
+import { cn } from '@/utils/classnames'
+import Operations from '../operations'
+
+type OperationsPopoverProps = {
+ dataset: DataSet
+ isCurrentWorkspaceDatasetOperator: boolean
+ openRenameModal: () => void
+ handleExportPipeline: (include?: boolean) => void
+ detectIsUsedByApp: () => void
+}
+
+const OperationsPopover = ({
+ dataset,
+ isCurrentWorkspaceDatasetOperator,
+ openRenameModal,
+ handleExportPipeline,
+ detectIsUsedByApp,
+}: OperationsPopoverProps) => (
+
+
+ )}
+ className="z-20 min-w-[186px]"
+ popupClassName="rounded-xl bg-none shadow-none ring-0 min-w-[186px]"
+ position="br"
+ trigger="click"
+ btnElement={(
+
+
+
+ )}
+ btnClassName={open =>
+ cn(
+ 'size-9 cursor-pointer justify-center rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0 shadow-lg shadow-shadow-shadow-5 ring-[2px] ring-inset ring-components-actionbar-bg hover:border-components-actionbar-border',
+ open ? 'border-components-actionbar-border bg-state-base-hover' : '',
+ )}
+ />
+
+)
+
+export default React.memo(OperationsPopover)
diff --git a/web/app/components/datasets/list/dataset-card/components/tag-area.tsx b/web/app/components/datasets/list/dataset-card/components/tag-area.tsx
new file mode 100644
index 0000000000..f55a064387
--- /dev/null
+++ b/web/app/components/datasets/list/dataset-card/components/tag-area.tsx
@@ -0,0 +1,55 @@
+import type { Tag } from '@/app/components/base/tag-management/constant'
+import type { DataSet } from '@/models/datasets'
+import * as React from 'react'
+import TagSelector from '@/app/components/base/tag-management/selector'
+import { cn } from '@/utils/classnames'
+
+type TagAreaProps = {
+ dataset: DataSet
+ tags: Tag[]
+ setTags: (tags: Tag[]) => void
+ onSuccess?: () => void
+ isHoveringTagSelector: boolean
+ onClick: (e: React.MouseEvent) => void
+}
+
+const TagArea = React.forwardRef(({
+ dataset,
+ tags,
+ setTags,
+ onSuccess,
+ isHoveringTagSelector,
+ onClick,
+}, ref) => (
+
+
0 && 'visible',
+ )}
+ >
+ tag.id)}
+ selectedTags={tags}
+ onCacheUpdate={setTags}
+ onChange={onSuccess}
+ />
+
+
+
+))
+TagArea.displayName = 'TagArea'
+
+export default TagArea
diff --git a/web/app/components/datasets/list/dataset-card/hooks/use-dataset-card-state.ts b/web/app/components/datasets/list/dataset-card/hooks/use-dataset-card-state.ts
new file mode 100644
index 0000000000..ad68a1df1c
--- /dev/null
+++ b/web/app/components/datasets/list/dataset-card/hooks/use-dataset-card-state.ts
@@ -0,0 +1,138 @@
+import type { Tag } from '@/app/components/base/tag-management/constant'
+import type { DataSet } from '@/models/datasets'
+import { useCallback, useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import Toast from '@/app/components/base/toast'
+import { useCheckDatasetUsage, useDeleteDataset } from '@/service/use-dataset-card'
+import { useExportPipelineDSL } from '@/service/use-pipeline'
+
+type ModalState = {
+ showRenameModal: boolean
+ showConfirmDelete: boolean
+ confirmMessage: string
+}
+
+type UseDatasetCardStateOptions = {
+ dataset: DataSet
+ onSuccess?: () => void
+}
+
+export const useDatasetCardState = ({ dataset, onSuccess }: UseDatasetCardStateOptions) => {
+ const { t } = useTranslation()
+ const [tags, setTags] = useState(dataset.tags)
+
+ useEffect(() => {
+ setTags(dataset.tags)
+ }, [dataset.tags])
+
+ // Modal state
+ const [modalState, setModalState] = useState({
+ showRenameModal: false,
+ showConfirmDelete: false,
+ confirmMessage: '',
+ })
+
+ // Export state
+ const [exporting, setExporting] = useState(false)
+
+ // Modal handlers
+ const openRenameModal = useCallback(() => {
+ setModalState(prev => ({ ...prev, showRenameModal: true }))
+ }, [])
+
+ const closeRenameModal = useCallback(() => {
+ setModalState(prev => ({ ...prev, showRenameModal: false }))
+ }, [])
+
+ const closeConfirmDelete = useCallback(() => {
+ setModalState(prev => ({ ...prev, showConfirmDelete: false }))
+ }, [])
+
+ // API mutations
+ const { mutateAsync: checkUsage } = useCheckDatasetUsage()
+ const { mutateAsync: deleteDatasetMutation } = useDeleteDataset()
+ const { mutateAsync: exportPipelineConfig } = useExportPipelineDSL()
+
+ // Export pipeline handler
+ const handleExportPipeline = useCallback(async (include: boolean = false) => {
+ const { pipeline_id, name } = dataset
+ if (!pipeline_id || exporting)
+ return
+
+ try {
+ setExporting(true)
+ const { data } = await exportPipelineConfig({
+ pipelineId: pipeline_id,
+ include,
+ })
+ const a = document.createElement('a')
+ const file = new Blob([data], { type: 'application/yaml' })
+ const url = URL.createObjectURL(file)
+ a.href = url
+ a.download = `${name}.pipeline`
+ a.click()
+ URL.revokeObjectURL(url)
+ }
+ catch {
+ Toast.notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
+ }
+ finally {
+ setExporting(false)
+ }
+ }, [dataset, exportPipelineConfig, exporting, t])
+
+ // Delete flow handlers
+ const detectIsUsedByApp = useCallback(async () => {
+ try {
+ const { is_using: isUsedByApp } = await checkUsage(dataset.id)
+ const message = isUsedByApp
+ ? t('datasetUsedByApp', { ns: 'dataset' })!
+ : t('deleteDatasetConfirmContent', { ns: 'dataset' })!
+ setModalState(prev => ({
+ ...prev,
+ confirmMessage: message,
+ showConfirmDelete: true,
+ }))
+ }
+ catch (e: unknown) {
+ if (e instanceof Response) {
+ const res = await e.json()
+ Toast.notify({ type: 'error', message: res?.message || 'Unknown error' })
+ }
+ else {
+ Toast.notify({ type: 'error', message: (e as Error)?.message || 'Unknown error' })
+ }
+ }
+ }, [dataset.id, checkUsage, t])
+
+ const onConfirmDelete = useCallback(async () => {
+ try {
+ await deleteDatasetMutation(dataset.id)
+ Toast.notify({ type: 'success', message: t('datasetDeleted', { ns: 'dataset' }) })
+ onSuccess?.()
+ }
+ finally {
+ closeConfirmDelete()
+ }
+ }, [dataset.id, deleteDatasetMutation, onSuccess, t, closeConfirmDelete])
+
+ return {
+ // Tag state
+ tags,
+ setTags,
+
+ // Modal state
+ modalState,
+ openRenameModal,
+ closeRenameModal,
+ closeConfirmDelete,
+
+ // Export state
+ exporting,
+
+ // Handlers
+ handleExportPipeline,
+ detectIsUsedByApp,
+ onConfirmDelete,
+ }
+}
diff --git a/web/app/components/datasets/list/dataset-card/index.tsx b/web/app/components/datasets/list/dataset-card/index.tsx
index 99404b0454..85dba7e8ff 100644
--- a/web/app/components/datasets/list/dataset-card/index.tsx
+++ b/web/app/components/datasets/list/dataset-card/index.tsx
@@ -1,28 +1,17 @@
'use client'
-import type { Tag } from '@/app/components/base/tag-management/constant'
import type { DataSet } from '@/models/datasets'
-import { RiFileTextFill, RiMoreFill, RiRobot2Fill } from '@remixicon/react'
import { useHover } from 'ahooks'
import { useRouter } from 'next/navigation'
-import * as React from 'react'
-import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import AppIcon from '@/app/components/base/app-icon'
-import Confirm from '@/app/components/base/confirm'
-import CornerLabel from '@/app/components/base/corner-label'
-import CustomPopover from '@/app/components/base/popover'
-import TagSelector from '@/app/components/base/tag-management/selector'
-import Toast from '@/app/components/base/toast'
-import Tooltip from '@/app/components/base/tooltip'
+import { useMemo, useRef } from 'react'
import { useSelector as useAppContextWithSelector } from '@/context/app-context'
-import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
-import { useKnowledge } from '@/hooks/use-knowledge'
-import { DOC_FORM_ICON_WITH_BG, DOC_FORM_TEXT } from '@/models/datasets'
-import { checkIsUsedInApp, deleteDataset } from '@/service/datasets'
-import { useExportPipelineDSL } from '@/service/use-pipeline'
-import { cn } from '@/utils/classnames'
-import RenameDatasetModal from '../../rename-modal'
-import Operations from './operations'
+import CornerLabels from './components/corner-labels'
+import DatasetCardFooter from './components/dataset-card-footer'
+import DatasetCardHeader from './components/dataset-card-header'
+import DatasetCardModals from './components/dataset-card-modals'
+import Description from './components/description'
+import OperationsPopover from './components/operations-popover'
+import TagArea from './components/tag-area'
+import { useDatasetCardState } from './hooks/use-dataset-card-state'
const EXTERNAL_PROVIDER = 'external'
@@ -35,320 +24,80 @@ const DatasetCard = ({
dataset,
onSuccess,
}: DatasetCardProps) => {
- const { t } = useTranslation()
const { push } = useRouter()
const isCurrentWorkspaceDatasetOperator = useAppContextWithSelector(state => state.isCurrentWorkspaceDatasetOperator)
- const [tags, setTags] = useState(dataset.tags)
const tagSelectorRef = useRef(null)
const isHoveringTagSelector = useHover(tagSelectorRef)
- const [showRenameModal, setShowRenameModal] = useState(false)
- const [showConfirmDelete, setShowConfirmDelete] = useState(false)
- const [confirmMessage, setConfirmMessage] = useState('')
- const [exporting, setExporting] = useState(false)
+ const {
+ tags,
+ setTags,
+ modalState,
+ openRenameModal,
+ closeRenameModal,
+ closeConfirmDelete,
+ handleExportPipeline,
+ detectIsUsedByApp,
+ onConfirmDelete,
+ } = useDatasetCardState({ dataset, onSuccess })
- const isExternalProvider = useMemo(() => {
- return dataset.provider === EXTERNAL_PROVIDER
- }, [dataset.provider])
+ const isExternalProvider = dataset.provider === EXTERNAL_PROVIDER
const isPipelineUnpublished = useMemo(() => {
return dataset.runtime_mode === 'rag_pipeline' && !dataset.is_published
}, [dataset.runtime_mode, dataset.is_published])
- const isShowChunkingModeIcon = useMemo(() => {
- return dataset.doc_form && (dataset.runtime_mode !== 'rag_pipeline' || dataset.is_published)
- }, [dataset.doc_form, dataset.runtime_mode, dataset.is_published])
- const isShowDocModeInfo = useMemo(() => {
- return dataset.doc_form && dataset.indexing_technique && dataset.retrieval_model_dict?.search_method && (dataset.runtime_mode !== 'rag_pipeline' || dataset.is_published)
- }, [dataset.doc_form, dataset.indexing_technique, dataset.retrieval_model_dict?.search_method, dataset.runtime_mode, dataset.is_published])
- const chunkingModeIcon = dataset.doc_form ? DOC_FORM_ICON_WITH_BG[dataset.doc_form] : React.Fragment
- const Icon = isExternalProvider ? DOC_FORM_ICON_WITH_BG.external : chunkingModeIcon
- const iconInfo = dataset.icon_info || {
- icon: '๐',
- icon_type: 'emoji',
- icon_background: '#FFF4ED',
- icon_url: '',
+ const handleCardClick = (e: React.MouseEvent) => {
+ e.preventDefault()
+ if (isExternalProvider)
+ push(`/datasets/${dataset.id}/hitTesting`)
+ else if (isPipelineUnpublished)
+ push(`/datasets/${dataset.id}/pipeline`)
+ else
+ push(`/datasets/${dataset.id}/documents`)
}
- const { formatIndexingTechniqueAndMethod } = useKnowledge()
- const documentCount = useMemo(() => {
- const availableDocCount = dataset.total_available_documents ?? 0
- if (availableDocCount === dataset.document_count)
- return `${dataset.document_count}`
- if (availableDocCount < dataset.document_count)
- return `${availableDocCount} / ${dataset.document_count}`
- }, [dataset.document_count, dataset.total_available_documents])
- const documentCountTooltip = useMemo(() => {
- const availableDocCount = dataset.total_available_documents ?? 0
- if (availableDocCount === dataset.document_count)
- return t('docAllEnabled', { ns: 'dataset', count: availableDocCount })
- if (availableDocCount < dataset.document_count)
- return t('partialEnabled', { ns: 'dataset', count: dataset.document_count, num: availableDocCount })
- }, [t, dataset.document_count, dataset.total_available_documents])
- const { formatTimeFromNow } = useFormatTimeFromNow()
- const editTimeText = useMemo(() => {
- return `${t('segment.editedAt', { ns: 'datasetDocuments' })} ${formatTimeFromNow(dataset.updated_at * 1000)}`
- }, [t, dataset.updated_at, formatTimeFromNow])
-
- const openRenameModal = useCallback(() => {
- setShowRenameModal(true)
- }, [])
-
- const { mutateAsync: exportPipelineConfig } = useExportPipelineDSL()
-
- const handleExportPipeline = useCallback(async (include = false) => {
- const { pipeline_id, name } = dataset
- if (!pipeline_id)
- return
-
- if (exporting)
- return
-
- try {
- setExporting(true)
- const { data } = await exportPipelineConfig({
- pipelineId: pipeline_id,
- include,
- })
- const a = document.createElement('a')
- const file = new Blob([data], { type: 'application/yaml' })
- const url = URL.createObjectURL(file)
- a.href = url
- a.download = `${name}.pipeline`
- a.click()
- URL.revokeObjectURL(url)
- }
- catch {
- Toast.notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
- }
- finally {
- setExporting(false)
- }
- }, [dataset, exportPipelineConfig, exporting, t])
-
- const detectIsUsedByApp = useCallback(async () => {
- try {
- const { is_using: isUsedByApp } = await checkIsUsedInApp(dataset.id)
- setConfirmMessage(isUsedByApp ? t('datasetUsedByApp', { ns: 'dataset' })! : t('deleteDatasetConfirmContent', { ns: 'dataset' })!)
- setShowConfirmDelete(true)
- }
- catch (e: any) {
- const res = await e.json()
- Toast.notify({ type: 'error', message: res?.message || 'Unknown error' })
- }
- }, [dataset.id, t])
-
- const onConfirmDelete = useCallback(async () => {
- try {
- await deleteDataset(dataset.id)
- Toast.notify({ type: 'success', message: t('datasetDeleted', { ns: 'dataset' }) })
- if (onSuccess)
- onSuccess()
- }
- finally {
- setShowConfirmDelete(false)
- }
- }, [dataset.id, onSuccess, t])
-
- useEffect(() => {
- setTags(dataset.tags)
- }, [dataset])
+ const handleTagAreaClick = (e: React.MouseEvent) => {
+ e.stopPropagation()
+ e.preventDefault()
+ }
return (
<>
{
- e.preventDefault()
- if (isExternalProvider)
- push(`/datasets/${dataset.id}/hitTesting`)
- else if (isPipelineUnpublished)
- push(`/datasets/${dataset.id}/pipeline`)
- else
- push(`/datasets/${dataset.id}/documents`)
- }}
+ onClick={handleCardClick}
>
- {!dataset.embedding_available && (
-
- )}
- {dataset.embedding_available && dataset.runtime_mode === 'rag_pipeline' && (
-
- )}
-
-
-
- {(isShowChunkingModeIcon || isExternalProvider) && (
-
-
-
- )}
-
-
-
- {dataset.name}
-
-
-
{dataset.author_name}
-
ยท
-
{editTimeText}
-
-
- {isExternalProvider && {t('externalKnowledgeBase', { ns: 'dataset' })}}
- {!isExternalProvider && isShowDocModeInfo && (
- <>
- {dataset.doc_form && (
-
- {t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`, { ns: 'dataset' })}
-
- )}
- {dataset.indexing_technique && (
-
- {formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method) as any}
-
- )}
- {dataset.is_multimodal && (
-
- {t('multimodal', { ns: 'dataset' })}
-
- )}
- >
- )}
-
-
-
-
- {dataset.description}
-
-
{
- e.stopPropagation()
- e.preventDefault()
- }}
- >
-
0 && 'visible',
- )}
- >
- tag.id)}
- selectedTags={tags}
- onCacheUpdate={setTags}
- onChange={onSuccess}
- />
-
- {/* Tag Mask */}
-
-
-
-
-
-
- {documentCount}
-
-
- {!isExternalProvider && (
-
-
-
- {dataset.app_count}
-
-
- )}
-
/
-
{`${t('updated', { ns: 'dataset' })} ${formatTimeFromNow(dataset.updated_at * 1000)}`}
-
-
-
- )}
- className="z-20 min-w-[186px]"
- popupClassName="rounded-xl bg-none shadow-none ring-0 min-w-[186px]"
- position="br"
- trigger="click"
- btnElement={(
-
-
-
- )}
- btnClassName={open =>
- cn(
- 'size-9 cursor-pointer justify-center rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0 shadow-lg shadow-shadow-shadow-5 ring-[2px] ring-inset ring-components-actionbar-bg hover:border-components-actionbar-border',
- open ? 'border-components-actionbar-border bg-state-base-hover' : '',
- )}
- />
-
-
- {showRenameModal && (
-
+
+
+ setShowRenameModal(false)}
+ tags={tags}
+ setTags={setTags}
onSuccess={onSuccess}
+ isHoveringTagSelector={isHoveringTagSelector}
+ onClick={handleTagAreaClick}
/>
- )}
- {showConfirmDelete && (
- setShowConfirmDelete(false)}
+
+
- )}
+
+
>
)
}
diff --git a/web/service/use-dataset-card.ts b/web/service/use-dataset-card.ts
new file mode 100644
index 0000000000..05365479dc
--- /dev/null
+++ b/web/service/use-dataset-card.ts
@@ -0,0 +1,18 @@
+import { useMutation } from '@tanstack/react-query'
+import { checkIsUsedInApp, deleteDataset } from './datasets'
+
+const NAME_SPACE = 'dataset-card'
+
+export const useCheckDatasetUsage = () => {
+ return useMutation({
+ mutationKey: [NAME_SPACE, 'check-usage'],
+ mutationFn: (datasetId: string) => checkIsUsedInApp(datasetId),
+ })
+}
+
+export const useDeleteDataset = () => {
+ return useMutation({
+ mutationKey: [NAME_SPACE, 'delete'],
+ mutationFn: (datasetId: string) => deleteDataset(datasetId),
+ })
+}