dify/web/app/components/datasets/list/index.tsx
Jingyi 9b74df21d0
feat(web): refine onboarding UI (#37433)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: hjlarry <hjlarry@163.com>
Co-authored-by: fatelei <fatelei@gmail.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
Co-authored-by: gigglewang <gigglewang@dify.ai>
Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
Co-authored-by: chariri <w@chariri.moe>
Co-authored-by: Evan <2869018789@qq.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-06-15 08:47:15 +00:00

136 lines
6.0 KiB
TypeScript

'use client'
import { useBoolean, useDebounceFn } from 'ahooks'
// Libraries
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useAppContext, useSelector as useAppContextSelector } from '@/context/app-context'
import { useExternalApiPanel } from '@/context/external-api-panel-context'
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'
// 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 } = useAppContext()
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 isCurrentWorkspaceManager = useAppContextSelector(state => state.isCurrentWorkspaceManager)
const isCurrentWorkspaceEditor = useAppContextSelector(state => state.isCurrentWorkspaceEditor)
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 && isCurrentWorkspaceEditor && 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 ?? ''}
includeAll={includeAll}
isCurrentWorkspaceEditor={isCurrentWorkspaceEditor}
isCurrentWorkspaceManager={isCurrentWorkspaceManager}
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 />
</>
)
: (
<>
<DatasetListHeader
apiBaseUrl={apiBaseInfo?.api_base_url ?? ''}
includeAll={includeAll}
isCurrentWorkspaceEditor={isCurrentWorkspaceEditor}
isCurrentWorkspaceManager={isCurrentWorkspaceManager}
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 && <ExternalAPIPanel onClose={() => setShowExternalApiPanel(false)} />}
</div>
)
}
export default List