diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs.tsx deleted file mode 100644 index 56fd9b62aa..0000000000 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { BucketsGray } from '@/app/components/base/icons/src/public/knowledge/online-drive' -import React, { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import { useDataSourceStore } from '../../../store' -import Tooltip from '@/app/components/base/tooltip' - -type BreadcrumbsProps = { - prefix: string[] - keywords: string - bucket: string - searchResultsLength: number -} - -const Breadcrumbs = ({ - prefix, - keywords, - bucket, - searchResultsLength, -}: BreadcrumbsProps) => { - const { t } = useTranslation() - const { setFileList, setSelectedFileList, setPrefix, setBucket } = useDataSourceStore().getState() - const isRoot = prefix.length === 0 && bucket === '' - const isSearching = !!keywords - - const handleBackToBucketList = useCallback(() => { - setFileList([]) - setSelectedFileList([]) - setBucket('') - setPrefix([]) - }, [setBucket, setFileList, setPrefix, setSelectedFileList]) - - return ( -
- {isRoot && ( -
- {t('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')} -
- )} - {!isRoot && ( -
- -
- -
-
-
- )} -
- ) -} - -export default React.memo(Breadcrumbs) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx new file mode 100644 index 0000000000..c7ff203f2a --- /dev/null +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx @@ -0,0 +1,33 @@ +import React from 'react' +import { BucketsGray } from '@/app/components/base/icons/src/public/knowledge/online-drive' +import Tooltip from '@/app/components/base/tooltip' +import { useTranslation } from 'react-i18next' + +type BucketProps = { + handleBackToBucketList: () => void +} + +const Bucket = ({ + handleBackToBucketList, +}: BucketProps) => { + const { t } = useTranslation() + + return ( + <> + + + + / + + ) +} + +export default React.memo(Bucket) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx new file mode 100644 index 0000000000..1e9de5d464 --- /dev/null +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx @@ -0,0 +1,98 @@ +import React, { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { useDataSourceStore } from '../../../../store' +import Bucket from './bucket' +import BreadcrumbItem from './item' + +type BreadcrumbsProps = { + prefix: string[] + keywords: string + bucket: string + searchResultsLength: number + isInPipeline: boolean +} + +const Breadcrumbs = ({ + prefix, + keywords, + bucket, + searchResultsLength, + isInPipeline, +}: BreadcrumbsProps) => { + const { t } = useTranslation() + const { setFileList, setSelectedFileList, setPrefix, setBucket } = useDataSourceStore().getState() + const showSearchResult = !!keywords && searchResultsLength > 0 + const isRoot = prefix.length === 0 && bucket === '' + + const breadcrumbs = useMemo(() => { + const displayBreadcrumbNum = isInPipeline ? 2 : 3 + const prefixToDisplay = prefix.slice(0, displayBreadcrumbNum - 1) + const collapsedBreadcrumbs = prefix.slice(displayBreadcrumbNum - 1, prefix.length - 1) + return { + original: prefix, + needCollapsed: prefix.length > displayBreadcrumbNum, + prefixBreadcrumbs: prefixToDisplay, + collapsedBreadcrumbs, + lastBreadcrumb: prefix[prefix.length - 1], + } + }, [isInPipeline, prefix]) + + const handleBackToBucketList = useCallback(() => { + setFileList([]) + setSelectedFileList([]) + setBucket('') + setPrefix([]) + }, [setBucket, setFileList, setPrefix, setSelectedFileList]) + + const handleClickBreadcrumb = useCallback((index: number) => { + const newPrefix = breadcrumbs.prefixBreadcrumbs.slice(0, index - 1) + setFileList([]) + setSelectedFileList([]) + setPrefix(newPrefix) + }, [breadcrumbs.prefixBreadcrumbs, setFileList, setPrefix, setSelectedFileList]) + + return ( +
+ {showSearchResult && ( +
+ {t('datasetPipeline.onlineDrive.breadcrumbs.searchResult', { + searchResultsLength, + folderName: prefix.length > 0 ? prefix[prefix.length - 1] : bucket, + })} +
+ )} + {!showSearchResult && isRoot && ( +
+ {t('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')} +
+ )} + {!showSearchResult && !isRoot && ( +
+ {bucket && ( + + )} + {!breadcrumbs.needCollapsed && ( + <> + {breadcrumbs.original.map((breadcrumb, index) => { + const isLast = index === breadcrumbs.original.length - 1 + return ( + + ) + })} + + )} +
+ )} +
+ ) +} + +export default React.memo(Breadcrumbs) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/item.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/item.tsx new file mode 100644 index 0000000000..92e98b30b3 --- /dev/null +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/item.tsx @@ -0,0 +1,47 @@ +import React, { useCallback } from 'react' +import cn from '@/utils/classnames' + +type BreadcrumbItemProps = { + name: string + index: number + handleClick: (index: number) => void + disabled?: boolean + isActive?: boolean + showSeparator?: boolean +} + +const BreadcrumbItem = ({ + name, + index, + handleClick, + disabled = false, + isActive = false, + showSeparator = true, +}: BreadcrumbItemProps) => { + const handleClickItem = useCallback(() => { + if (!disabled) + handleClick(index) + }, [disabled, handleClick, index]) + + return ( + <> + + {showSeparator && /} + + ) +} + +BreadcrumbItem.displayName = 'BreadcrumbItem' + +export default React.memo(BreadcrumbItem) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.tsx index 1acb5b64e6..5cdeb5faaa 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.tsx @@ -11,6 +11,7 @@ type HeaderProps = { searchResultsLength: number handleInputChange: React.ChangeEventHandler handleResetKeywords: () => void + isInPipeline: boolean } const Header = ({ @@ -18,6 +19,7 @@ const Header = ({ inputValue, keywords, bucket, + isInPipeline, searchResultsLength, handleInputChange, handleResetKeywords, @@ -31,6 +33,7 @@ const Header = ({ keywords={keywords} bucket={bucket} searchResultsLength={searchResultsLength} + isInPipeline={isInPipeline} /> - + - {file.key} + {file.displayName} {!isFolder && file.size && ( {formatFileSize(file.size)} diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx index e3a2ea6517..b670d9a3d4 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx @@ -43,13 +43,14 @@ const OnlineDrive = ({ : `/rag/pipelines/${pipelineId}/workflows/draft/datasource/nodes/${nodeId}/run` const getOnlineDrive = useCallback(async () => { + const prefixString = prefix.length > 0 ? `${prefix.join('/')}/` : '' setIsLoading(true) ssePost( datasourceNodeRunURL, { body: { inputs: { - prefix: prefix.join('/'), + prefix: prefixString, bucket, start_after: startAfter, max_keys: 30, // Adjust as needed @@ -111,11 +112,12 @@ const OnlineDrive = ({ if (file.type === OnlineDriveFileType.file) return setFileList([]) if (file.type === OnlineDriveFileType.bucket) { - setBucket(file.key) + setBucket(file.displayName) } else { + const key = file.displayName.endsWith('/') ? file.displayName.slice(0, -1) : file.displayName const newPrefix = produce(prefix, (draft) => { - const newList = file.key.split('/') + const newList = key.split('/') draft.push(...newList) }) setPrefix(newPrefix) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts index e608b50b64..47286850f7 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts @@ -18,16 +18,20 @@ export const convertOnlineDriveDataToFileList = (data: OnlineDriveData[]): Onlin data.forEach((item) => { fileList.push({ key: item.bucket, + displayName: item.bucket, type: OnlineDriveFileType.bucket, }) }) } else { data[0].files.forEach((file) => { + const isFileType = isFile(file.key) + const filePathList = file.key.split('/') fileList.push({ key: file.key, - size: isFile(file.key) ? file.size : undefined, - type: isFile(file.key) ? OnlineDriveFileType.file : OnlineDriveFileType.folder, + displayName: `${isFileType ? filePathList.pop() : filePathList[filePathList.length - 2]}${isFileType ? '' : '/'}`, + size: isFileType ? file.size : undefined, + type: isFileType ? OnlineDriveFileType.file : OnlineDriveFileType.folder, }) }) } diff --git a/web/models/pipeline.ts b/web/models/pipeline.ts index eb0b7b36fe..93304c9a0c 100644 --- a/web/models/pipeline.ts +++ b/web/models/pipeline.ts @@ -276,6 +276,7 @@ export enum OnlineDriveFileType { export type OnlineDriveFile = { key: string + displayName: string size?: number type: OnlineDriveFileType }