From cf46fba753aef1deb6035393cd5a8eb1c30b3fd8 Mon Sep 17 00:00:00 2001 From: twwu Date: Wed, 13 Aug 2025 11:34:22 +0800 Subject: [PATCH] feat: Enhance Online Drive component with bucket handling and breadcrumb navigation --- .../file-list/header/breadcrumbs/drive.tsx | 35 +++++++++++++++++++ .../file-list/header/breadcrumbs/index.tsx | 25 ++++++++++--- .../data-source/online-drive/index.tsx | 4 ++- .../data-source/online-drive/utils.ts | 11 ++++-- .../data-source/store/slices/online-drive.ts | 6 ++++ web/i18n/en-US/dataset-pipeline.ts | 1 + web/i18n/zh-Hans/dataset-pipeline.ts | 1 + 7 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/drive.tsx diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/drive.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/drive.tsx new file mode 100644 index 0000000000..9ec69aa808 --- /dev/null +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/drive.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import cn from '@/utils/classnames' +import { useTranslation } from 'react-i18next' + +type DriveProps = { + prefix: string[] + handleBackToRoot: () => void +} + +const Drive = ({ + prefix, + handleBackToRoot, +}: DriveProps) => { + const { t } = useTranslation() + + return ( + <> + + {prefix.length > 0 && /} + + ) +} + +export default React.memo(Drive) 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 index da379d8eac..52d9846617 100644 --- 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 @@ -1,9 +1,10 @@ import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { useDataSourceStore } from '../../../../store' +import { useDataSourceStore, useDataSourceStoreWithSelector } from '../../../../store' import Bucket from './bucket' import BreadcrumbItem from './item' import Dropdown from './dropdown' +import Drive from './drive' type BreadcrumbsProps = { prefix: string[] @@ -22,8 +23,9 @@ const Breadcrumbs = ({ }: BreadcrumbsProps) => { const { t } = useTranslation() const dataSourceStore = useDataSourceStore() + const hasBucket = useDataSourceStoreWithSelector(s => s.hasBucket) const showSearchResult = !!keywords && searchResultsLength > 0 - const isRoot = prefix.length === 0 && bucket === '' + const showBucketListTitle = prefix.length === 0 && hasBucket && bucket === '' const displayBreadcrumbNum = useMemo(() => { const num = isInPipeline ? 2 : 3 @@ -57,6 +59,13 @@ const Breadcrumbs = ({ setPrefix([]) }, [dataSourceStore]) + const handleBackToRoot = useCallback(() => { + const { setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState() + setFileList([]) + setSelectedFileIds([]) + setPrefix([]) + }, [dataSourceStore]) + const handleClickBreadcrumb = useCallback((index: number) => { const { prefix, setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState() const newPrefix = prefix.slice(0, index + 1) @@ -75,14 +84,14 @@ const Breadcrumbs = ({ })} )} - {!showSearchResult && isRoot && ( + {!showSearchResult && showBucketListTitle && (
{t('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')}
)} - {!showSearchResult && !isRoot && ( + {!showSearchResult && !showBucketListTitle && (
- {bucket && ( + {hasBucket && bucket && ( 0} /> )} + {!hasBucket && ( + + )} {!breadcrumbs.needCollapsed && ( <> {breadcrumbs.original.map((breadcrumb, index) => { 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 db9977fc60..e15dac89fe 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 @@ -80,15 +80,17 @@ const OnlineDrive = ({ }, { onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => { - const { setFileList, isTruncated, currentNextPageParametersRef } = dataSourceStore.getState() + const { setFileList, isTruncated, currentNextPageParametersRef, setHasBucket } = dataSourceStore.getState() const { fileList: newFileList, isTruncated: newIsTruncated, nextPageParameters: newNextPageParameters, + hasBucket: newHasBucket, } = convertOnlineDriveData(documentsData.data, prefix, bucket) setFileList([...fileList, ...newFileList]) isTruncated.current = newIsTruncated currentNextPageParametersRef.current = newNextPageParameters + setHasBucket(newHasBucket) setIsLoading(false) }, onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => { 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 abf60232d8..44de43f213 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 @@ -7,20 +7,23 @@ export const isFile = (type: 'file' | 'folder'): boolean => { export const isBucketListInitiation = (data: OnlineDriveData[], prefix: string[], bucket: string): boolean => { if (bucket || prefix.length > 0) return false - return data.length > 1 || (data.length === 1 && !!data[0].bucket && data[0].files.length === 0) + const hasBucket = data.every(item => !!item.bucket) + return hasBucket && (data.length > 1 || (data.length === 1 && !!data[0].bucket && data[0].files.length === 0)) } export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[], bucket: string): { fileList: OnlineDriveFile[], isTruncated: boolean, nextPageParameters: Record + hasBucket: boolean } => { const fileList: OnlineDriveFile[] = [] let isTruncated = false let nextPageParameters: Record = {} + let hasBucket = false if (data.length === 0) - return { fileList, isTruncated, nextPageParameters } + return { fileList, isTruncated, nextPageParameters, hasBucket } if (isBucketListInitiation(data, prefix, bucket)) { data.forEach((item) => { @@ -30,6 +33,7 @@ export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[] type: OnlineDriveFileType.bucket, }) }) + hasBucket = true } else { data[0].files.forEach((file) => { @@ -44,6 +48,7 @@ export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[] }) isTruncated = data[0].is_truncated ?? false nextPageParameters = data[0].next_page_parameters ?? {} + hasBucket = !!data[0].bucket } - return { fileList, isTruncated, nextPageParameters } + return { fileList, isTruncated, nextPageParameters, hasBucket } } diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/store/slices/online-drive.ts b/web/app/components/datasets/documents/create-from-pipeline/data-source/store/slices/online-drive.ts index ab7b641a01..4c4d6a52a2 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/store/slices/online-drive.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/store/slices/online-drive.ts @@ -17,6 +17,8 @@ export type OnlineDriveSliceShape = { setNextPageParameters: (nextPageParameters: Record) => void isTruncated: React.RefObject previewOnlineDriveFileRef: React.RefObject + hasBucket: boolean + setHasBucket: (hasBucket: boolean) => void } export const createOnlineDriveSlice: StateCreator = (set, get) => { @@ -53,5 +55,9 @@ export const createOnlineDriveSlice: StateCreator = (set, })), isTruncated: { current: false }, previewOnlineDriveFileRef: { current: undefined }, + hasBucket: false, + setHasBucket: (hasBucket: boolean) => set(() => ({ + hasBucket, + })), }) } diff --git a/web/i18n/en-US/dataset-pipeline.ts b/web/i18n/en-US/dataset-pipeline.ts index eccafd0f8b..b31194bd7b 100644 --- a/web/i18n/en-US/dataset-pipeline.ts +++ b/web/i18n/en-US/dataset-pipeline.ts @@ -122,6 +122,7 @@ const translation = { notConnectedTip: 'To sync with {{name}}, connection to {{name}} must be established first.', breadcrumbs: { allBuckets: 'All Cloud Storage Buckets', + allFiles: 'All Files', searchResult: 'Find {{searchResultsLength}} items in "{{folderName}}" folder', searchPlaceholder: 'Search files...', }, diff --git a/web/i18n/zh-Hans/dataset-pipeline.ts b/web/i18n/zh-Hans/dataset-pipeline.ts index 37aeb656e3..63326ca81c 100644 --- a/web/i18n/zh-Hans/dataset-pipeline.ts +++ b/web/i18n/zh-Hans/dataset-pipeline.ts @@ -122,6 +122,7 @@ const translation = { notConnectedTip: '同步 {{name}} 内容前, 须先绑定 {{name}}。', breadcrumbs: { allBuckets: '所有云存储桶', + allFiles: '所有文件', searchResult: '在 "{{folderName}}" 文件夹中找到 {{searchResultsLength}} 个项目', searchPlaceholder: '搜索文件...', },