dify/web/app/components/datasets/list/index.tsx
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

172 lines
6.5 KiB
TypeScript

'use client'
import { useBoolean, useDebounceFn } from 'ahooks'
import { useAtomValue } from 'jotai'
// Libraries
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useExternalApiPanel } from '@/context/external-api-panel-context'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { isCurrentWorkspaceOwnerAtom } from '@/context/workspace-state'
import { TagManagementModal } from '@/features/tag-management/components/tag-management-modal'
import useDocumentTitle from '@/hooks/use-document-title'
import { useRouter } from '@/next/navigation'
import {
useDatasetApiBaseUrl,
useDatasetList,
useInvalidDatasetList,
} from '@/service/knowledge/use-dataset'
import { hasPermission } from '@/utils/permission'
// Components
import FilterEmptyState from '../../base/filter-empty-state'
import ExternalAPIPanel from '../external-api/external-api-panel'
import Datasets from './datasets'
import DatasetFirstEmptyState from './first-empty-state'
import DatasetListHeader from './header'
const List = () => {
const { t } = useTranslation()
const { push } = useRouter()
const isCurrentWorkspaceOwner = useAtomValue(isCurrentWorkspaceOwnerAtom)
const [showTagManagementModal, setShowTagManagementModal] = useState(false)
const { showExternalApiPanel, setShowExternalApiPanel } = useExternalApiPanel()
const [includeAll, { toggle: toggleIncludeAll }] = useBoolean(false)
const invalidDatasetList = useInvalidDatasetList()
useDocumentTitle(t(($) => $.knowledge, { ns: 'dataset' }))
const [keywords, setKeywords] = useState('')
const [searchKeywords, setSearchKeywords] = useState('')
const { run: handleSearch } = useDebounceFn(
() => {
setSearchKeywords(keywords)
},
{ wait: 500 },
)
const handleKeywordsChange = (value: string) => {
setKeywords(value)
handleSearch()
}
const [tagFilterValue, setTagFilterValue] = useState<string[]>([])
const [tagIDs, setTagIDs] = useState<string[]>([])
const { run: handleTagsUpdate } = useDebounceFn(
() => {
setTagIDs(tagFilterValue)
},
{ wait: 500 },
)
const handleTagsChange = (value: string[]) => {
setTagFilterValue(value)
handleTagsUpdate()
}
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const canCreateDataset = hasPermission(workspacePermissionKeys, 'dataset.create_and_management')
const canConnectExternalDataset = hasPermission(
workspacePermissionKeys,
'dataset.external.connect',
)
const { data: apiBaseInfo } = useDatasetApiBaseUrl()
const datasetListQuery = useDatasetList({
initialPage: 1,
tag_ids: tagIDs,
limit: 30,
include_all: includeAll,
keyword: searchKeywords,
})
const pages = datasetListQuery.data?.pages ?? []
const hasResolvedFirstPage = pages.length > 0
const hasAnyDataset = (pages[0]?.total ?? 0) > 0
const hasActiveFilters =
tagIDs.length > 0 ||
keywords.trim().length > 0 ||
searchKeywords.trim().length > 0 ||
includeAll
const showEmptyDataList =
!hasAnyDataset &&
(canCreateDataset || canConnectExternalDataset) &&
hasResolvedFirstPage &&
!hasActiveFilters
const showFilteredEmptyState = !hasAnyDataset && hasResolvedFirstPage && hasActiveFilters
return (
<div className="relative flex grow flex-col overflow-y-auto bg-background-body">
{showEmptyDataList ? (
<>
<DatasetListHeader
apiBaseUrl={apiBaseInfo?.api_base_url ?? ''}
canConnectExternalDataset={canConnectExternalDataset}
canCreateDataset={canCreateDataset}
includeAll={includeAll}
isCurrentWorkspaceOwner={isCurrentWorkspaceOwner}
keywords={keywords}
tagFilterValue={tagFilterValue}
onCreateDataset={() => push('/datasets/create')}
onCreateFromPipeline={() => push('/datasets/create-from-pipeline')}
onConnectDataset={() => push('/datasets/connect')}
onExternalApiClick={() => setShowExternalApiPanel(true)}
onIncludeAllChange={toggleIncludeAll}
onKeywordsChange={handleKeywordsChange}
onOpenTagManagement={() => setShowTagManagementModal(true)}
onTagsChange={handleTagsChange}
/>
<DatasetFirstEmptyState
canConnectExternalDataset={canConnectExternalDataset}
canCreateDataset={canCreateDataset}
/>
</>
) : (
<>
<DatasetListHeader
apiBaseUrl={apiBaseInfo?.api_base_url ?? ''}
canConnectExternalDataset={canConnectExternalDataset}
canCreateDataset={canCreateDataset}
includeAll={includeAll}
isCurrentWorkspaceOwner={isCurrentWorkspaceOwner}
keywords={keywords}
tagFilterValue={tagFilterValue}
onCreateDataset={() => push('/datasets/create')}
onCreateFromPipeline={() => push('/datasets/create-from-pipeline')}
onConnectDataset={() => push('/datasets/connect')}
onExternalApiClick={() => setShowExternalApiPanel(true)}
onIncludeAllChange={toggleIncludeAll}
onKeywordsChange={handleKeywordsChange}
onOpenTagManagement={() => setShowTagManagementModal(true)}
onTagsChange={handleTagsChange}
/>
<Datasets
datasetList={datasetListQuery.data}
emptyElement={
showFilteredEmptyState ? (
<FilterEmptyState
title={t(($) => $['filterEmpty.noKnowledge'], { ns: 'dataset' })}
/>
) : undefined
}
fetchNextPage={datasetListQuery.fetchNextPage}
hasNextPage={datasetListQuery.hasNextPage}
isFetching={datasetListQuery.isFetching}
isFetchingNextPage={datasetListQuery.isFetchingNextPage}
isLoading={datasetListQuery.isLoading}
isPlaceholderData={datasetListQuery.isPlaceholderData}
onOpenTagManagement={() => setShowTagManagementModal(true)}
/>
</>
)}
<TagManagementModal
type="knowledge"
show={showTagManagementModal}
onClose={() => setShowTagManagementModal(false)}
onTagsChange={invalidDatasetList}
/>
{showExternalApiPanel && canConnectExternalDataset && (
<ExternalAPIPanel
canManageExternalKnowledgeApi={canConnectExternalDataset}
onClose={() => setShowExternalApiPanel(false)}
/>
)}
</div>
)
}
export default List