refactor: Add loading state and bucket handling to Online Drive components

This commit is contained in:
twwu 2025-07-04 15:14:19 +08:00
parent d3b17ea567
commit 2ecbcd6a7f
5 changed files with 63 additions and 6 deletions

View File

@ -1,21 +1,34 @@
import React from 'react' import { BucketsGray } from '@/app/components/base/icons/src/public/knowledge/online-drive'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useDataSourceStore } from '../../../store'
import Tooltip from '@/app/components/base/tooltip'
type BreadcrumbsProps = { type BreadcrumbsProps = {
prefix: string[] prefix: string[]
keywords: string keywords: string
bucket: string
searchResultsLength: number searchResultsLength: number
} }
const Breadcrumbs = ({ const Breadcrumbs = ({
prefix, prefix,
keywords, keywords,
bucket,
searchResultsLength, searchResultsLength,
}: BreadcrumbsProps) => { }: BreadcrumbsProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const isRoot = prefix.length === 0 const { setFileList, setSelectedFileList, setPrefix, setBucket } = useDataSourceStore().getState()
const isRoot = prefix.length === 0 && bucket === ''
const isSearching = !!keywords const isSearching = !!keywords
const handleBackToBucketList = useCallback(() => {
setFileList([])
setSelectedFileList([])
setBucket('')
setPrefix([])
}, [setBucket, setFileList, setPrefix, setSelectedFileList])
return ( return (
<div className='flex grow items-center py-1'> <div className='flex grow items-center py-1'>
{isRoot && ( {isRoot && (
@ -24,7 +37,18 @@ const Breadcrumbs = ({
</div> </div>
)} )}
{!isRoot && ( {!isRoot && (
<div></div> <div className='flex items-center gap-x-0.5'>
<Tooltip
popupContent={t('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')}
>
<div
className='flex size-5 cursor-pointer items-center justify-center'
onClick={handleBackToBucketList}
>
<BucketsGray />
</div>
</Tooltip>
</div>
)} )}
</div> </div>
) )

View File

@ -7,6 +7,7 @@ type HeaderProps = {
prefix: string[] prefix: string[]
inputValue: string inputValue: string
keywords: string keywords: string
bucket: string
searchResultsLength: number searchResultsLength: number
handleInputChange: React.ChangeEventHandler<HTMLInputElement> handleInputChange: React.ChangeEventHandler<HTMLInputElement>
handleResetKeywords: () => void handleResetKeywords: () => void
@ -16,6 +17,7 @@ const Header = ({
prefix, prefix,
inputValue, inputValue,
keywords, keywords,
bucket,
searchResultsLength, searchResultsLength,
handleInputChange, handleInputChange,
handleResetKeywords, handleResetKeywords,
@ -27,6 +29,7 @@ const Header = ({
<Breadcrumbs <Breadcrumbs
prefix={prefix} prefix={prefix}
keywords={keywords} keywords={keywords}
bucket={bucket}
searchResultsLength={searchResultsLength} searchResultsLength={searchResultsLength}
/> />
<Input <Input

View File

@ -9,12 +9,14 @@ type FileListProps = {
selectedFileList: string[] selectedFileList: string[]
prefix: string[] prefix: string[]
keywords: string keywords: string
bucket: string
isInPipeline: boolean isInPipeline: boolean
resetKeywords: () => void resetKeywords: () => void
updateKeywords: (keywords: string) => void updateKeywords: (keywords: string) => void
searchResultsLength: number searchResultsLength: number
handleSelectFile: (file: OnlineDriveFile) => void handleSelectFile: (file: OnlineDriveFile) => void
handleOpenFolder: (file: OnlineDriveFile) => void handleOpenFolder: (file: OnlineDriveFile) => void
isLoading: boolean
} }
const FileList = ({ const FileList = ({
@ -22,12 +24,14 @@ const FileList = ({
selectedFileList, selectedFileList,
prefix, prefix,
keywords, keywords,
bucket,
resetKeywords, resetKeywords,
updateKeywords, updateKeywords,
searchResultsLength, searchResultsLength,
handleSelectFile, handleSelectFile,
handleOpenFolder, handleOpenFolder,
isInPipeline, isInPipeline,
isLoading,
}: FileListProps) => { }: FileListProps) => {
const [inputValue, setInputValue] = useState(keywords) const [inputValue, setInputValue] = useState(keywords)
@ -55,6 +59,7 @@ const FileList = ({
prefix={prefix} prefix={prefix}
inputValue={inputValue} inputValue={inputValue}
keywords={keywords} keywords={keywords}
bucket={bucket}
handleInputChange={handleInputChange} handleInputChange={handleInputChange}
searchResultsLength={searchResultsLength} searchResultsLength={searchResultsLength}
handleResetKeywords={handleResetKeywords} handleResetKeywords={handleResetKeywords}
@ -67,6 +72,7 @@ const FileList = ({
handleOpenFolder={handleOpenFolder} handleOpenFolder={handleOpenFolder}
handleSelectFile={handleSelectFile} handleSelectFile={handleSelectFile}
isInPipeline={isInPipeline} isInPipeline={isInPipeline}
isLoading={isLoading}
/> />
</div> </div>
) )

View File

@ -2,12 +2,15 @@ import type { OnlineDriveFile } from '@/models/pipeline'
import Item from './item' import Item from './item'
import EmptyFolder from './empty-folder' import EmptyFolder from './empty-folder'
import EmptySearchResult from './empty-search-result' import EmptySearchResult from './empty-search-result'
import Loading from '@/app/components/base/loading'
import { RiLoader2Line } from '@remixicon/react'
type FileListProps = { type FileListProps = {
fileList: OnlineDriveFile[] fileList: OnlineDriveFile[]
selectedFileList: string[] selectedFileList: string[]
keywords: string keywords: string
isInPipeline: boolean isInPipeline: boolean
isLoading: boolean
handleResetKeywords: () => void handleResetKeywords: () => void
handleSelectFile: (file: OnlineDriveFile) => void handleSelectFile: (file: OnlineDriveFile) => void
handleOpenFolder: (file: OnlineDriveFile) => void handleOpenFolder: (file: OnlineDriveFile) => void
@ -21,12 +24,20 @@ const List = ({
handleSelectFile, handleSelectFile,
handleOpenFolder, handleOpenFolder,
isInPipeline, isInPipeline,
isLoading,
}: FileListProps) => { }: FileListProps) => {
const isEmptyFolder = fileList.length === 0 && keywords.length === 0 const isAllLoading = isLoading && fileList.length === 0 && keywords.length === 0
const isSearchResultEmpty = fileList.length === 0 && keywords.length > 0 const isPartLoading = isLoading && fileList.length > 0
const isEmptyFolder = !isLoading && fileList.length === 0 && keywords.length === 0
const isSearchResultEmpty = !isLoading && fileList.length === 0 && keywords.length > 0
return ( return (
<div className='grow overflow-hidden p-1 pt-0'> <div className='grow overflow-hidden p-1 pt-0'>
{
isAllLoading && (
<Loading type='app' />
)
}
{ {
isEmptyFolder && ( isEmptyFolder && (
<EmptyFolder /> <EmptyFolder />
@ -54,6 +65,13 @@ const List = ({
) )
}) })
} }
{
isPartLoading && (
<div className='flex items-center justify-center py-2'>
<RiLoader2Line className='animation-spin size-4 text-text-tertiary' />
</div>
)
}
</div> </div>
)} )}
</div> </div>

View File

@ -1,6 +1,6 @@
import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types'
import Header from './header' import Header from './header'
import { useCallback, useEffect } from 'react' import { useCallback, useEffect, useState } from 'react'
import FileList from './file-list' import FileList from './file-list'
import type { OnlineDriveFile } from '@/models/pipeline' import type { OnlineDriveFile } from '@/models/pipeline'
import { DatasourceType, OnlineDriveFileType } from '@/models/pipeline' import { DatasourceType, OnlineDriveFileType } from '@/models/pipeline'
@ -36,12 +36,14 @@ const OnlineDrive = ({
const setSelectedFileList = useDataSourceStoreWithSelector(state => state.setSelectedFileList) const setSelectedFileList = useDataSourceStoreWithSelector(state => state.setSelectedFileList)
const fileList = useDataSourceStoreWithSelector(state => state.fileList) const fileList = useDataSourceStoreWithSelector(state => state.fileList)
const setFileList = useDataSourceStoreWithSelector(state => state.setFileList) const setFileList = useDataSourceStoreWithSelector(state => state.setFileList)
const [isLoading, setIsLoading] = useState(false)
const datasourceNodeRunURL = !isInPipeline const datasourceNodeRunURL = !isInPipeline
? `/rag/pipelines/${pipelineId}/workflows/published/datasource/nodes/${nodeId}/run` ? `/rag/pipelines/${pipelineId}/workflows/published/datasource/nodes/${nodeId}/run`
: `/rag/pipelines/${pipelineId}/workflows/draft/datasource/nodes/${nodeId}/run` : `/rag/pipelines/${pipelineId}/workflows/draft/datasource/nodes/${nodeId}/run`
const getOnlineDrive = useCallback(async () => { const getOnlineDrive = useCallback(async () => {
setIsLoading(true)
ssePost( ssePost(
datasourceNodeRunURL, datasourceNodeRunURL,
{ {
@ -59,12 +61,14 @@ const OnlineDrive = ({
onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => { onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => {
const newFileList = convertOnlineDriveDataToFileList(documentsData.data) const newFileList = convertOnlineDriveDataToFileList(documentsData.data)
setFileList([...fileList, ...newFileList]) setFileList([...fileList, ...newFileList])
setIsLoading(false)
}, },
onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => { onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => {
Toast.notify({ Toast.notify({
type: 'error', type: 'error',
message: error.error, message: error.error,
}) })
setIsLoading(false)
}, },
}, },
) )
@ -123,12 +127,14 @@ const OnlineDrive = ({
selectedFileList={selectedFileList} selectedFileList={selectedFileList}
prefix={prefix} prefix={prefix}
keywords={keywords} keywords={keywords}
bucket={bucket}
resetKeywords={resetPrefix} resetKeywords={resetPrefix}
updateKeywords={updateKeywords} updateKeywords={updateKeywords}
searchResultsLength={fileList.length} searchResultsLength={fileList.length}
handleSelectFile={handleSelectFile} handleSelectFile={handleSelectFile}
handleOpenFolder={handleOpenFolder} handleOpenFolder={handleOpenFolder}
isInPipeline={isInPipeline} isInPipeline={isInPipeline}
isLoading={isLoading}
/> />
</div> </div>
) )