| }, [displayBreadcrumbNum, prefix]) | }, [displayBreadcrumbNum, prefix]) | ||||
| const handleBackToBucketList = useCallback(() => { | const handleBackToBucketList = useCallback(() => { | ||||
| const { setFileList, setSelectedFileKeys, setPrefix, setBucket } = dataSourceStore.getState() | |||||
| const { setFileList, setSelectedFileIds, setPrefix, setBucket } = dataSourceStore.getState() | |||||
| setFileList([]) | setFileList([]) | ||||
| setSelectedFileKeys([]) | |||||
| setSelectedFileIds([]) | |||||
| setBucket('') | setBucket('') | ||||
| setPrefix([]) | setPrefix([]) | ||||
| }, [dataSourceStore]) | }, [dataSourceStore]) | ||||
| const handleClickBucketName = useCallback(() => { | const handleClickBucketName = useCallback(() => { | ||||
| const { setFileList, setSelectedFileKeys, setPrefix } = dataSourceStore.getState() | |||||
| const { setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState() | |||||
| setFileList([]) | setFileList([]) | ||||
| setSelectedFileKeys([]) | |||||
| setSelectedFileIds([]) | |||||
| setPrefix([]) | setPrefix([]) | ||||
| }, [dataSourceStore]) | }, [dataSourceStore]) | ||||
| const handleClickBreadcrumb = useCallback((index: number) => { | const handleClickBreadcrumb = useCallback((index: number) => { | ||||
| const { prefix, setFileList, setSelectedFileKeys, setPrefix } = dataSourceStore.getState() | |||||
| const { prefix, setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState() | |||||
| const newPrefix = prefix.slice(0, index + 1) | const newPrefix = prefix.slice(0, index + 1) | ||||
| setFileList([]) | setFileList([]) | ||||
| setSelectedFileKeys([]) | |||||
| setSelectedFileIds([]) | |||||
| setPrefix(newPrefix) | setPrefix(newPrefix) | ||||
| }, [dataSourceStore]) | }, [dataSourceStore]) | ||||
| type FileListProps = { | type FileListProps = { | ||||
| fileList: OnlineDriveFile[] | fileList: OnlineDriveFile[] | ||||
| selectedFileKeys: string[] | |||||
| selectedFileIds: string[] | |||||
| prefix: string[] | prefix: string[] | ||||
| keywords: string | keywords: string | ||||
| bucket: string | bucket: string | ||||
| const FileList = ({ | const FileList = ({ | ||||
| fileList, | fileList, | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| prefix, | prefix, | ||||
| keywords, | keywords, | ||||
| bucket, | bucket, | ||||
| /> | /> | ||||
| <List | <List | ||||
| fileList={fileList} | fileList={fileList} | ||||
| selectedFileKeys={selectedFileKeys} | |||||
| selectedFileIds={selectedFileIds} | |||||
| keywords={keywords} | keywords={keywords} | ||||
| handleResetKeywords={handleResetKeywords} | handleResetKeywords={handleResetKeywords} | ||||
| handleOpenFolder={handleOpenFolder} | handleOpenFolder={handleOpenFolder} |
| type FileListProps = { | type FileListProps = { | ||||
| fileList: OnlineDriveFile[] | fileList: OnlineDriveFile[] | ||||
| selectedFileKeys: string[] | |||||
| selectedFileIds: string[] | |||||
| keywords: string | keywords: string | ||||
| isInPipeline: boolean | isInPipeline: boolean | ||||
| isLoading: boolean | isLoading: boolean | ||||
| const List = ({ | const List = ({ | ||||
| fileList, | fileList, | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| keywords, | keywords, | ||||
| handleResetKeywords, | handleResetKeywords, | ||||
| handleSelectFile, | handleSelectFile, | ||||
| useEffect(() => { | useEffect(() => { | ||||
| if (anchorRef.current) { | if (anchorRef.current) { | ||||
| observerRef.current = new IntersectionObserver((entries) => { | observerRef.current = new IntersectionObserver((entries) => { | ||||
| const { setStartAfter, isTruncated } = dataSourceStore.getState() | |||||
| const { setNextPageParameters, currentNextPageParametersRef, isTruncated } = dataSourceStore.getState() | |||||
| if (entries[0].isIntersecting && isTruncated.current && !isLoading) | if (entries[0].isIntersecting && isTruncated.current && !isLoading) | ||||
| setStartAfter(fileList[fileList.length - 1].key) | |||||
| setNextPageParameters(currentNextPageParametersRef.current) | |||||
| }, { | }, { | ||||
| rootMargin: '100px', | rootMargin: '100px', | ||||
| }) | }) | ||||
| <div className='flex h-full flex-col gap-y-px overflow-y-auto rounded-[10px] bg-background-section px-1 py-1.5'> | <div className='flex h-full flex-col gap-y-px overflow-y-auto rounded-[10px] bg-background-section px-1 py-1.5'> | ||||
| { | { | ||||
| fileList.map((file) => { | fileList.map((file) => { | ||||
| const isSelected = selectedFileKeys.includes(file.key) | |||||
| const isSelected = selectedFileIds.includes(file.id) | |||||
| return ( | return ( | ||||
| <Item | <Item | ||||
| key={file.key} | |||||
| key={file.id} | |||||
| file={file} | file={file} | ||||
| isSelected={isSelected} | isSelected={isSelected} | ||||
| onSelect={handleSelectFile} | onSelect={handleSelectFile} |
| import Checkbox from '@/app/components/base/checkbox' | import Checkbox from '@/app/components/base/checkbox' | ||||
| import Radio from '@/app/components/base/radio/ui' | import Radio from '@/app/components/base/radio/ui' | ||||
| import type { OnlineDriveFile } from '@/models/pipeline' | import type { OnlineDriveFile } from '@/models/pipeline' | ||||
| import React, { useCallback } from 'react' | |||||
| import React, { useCallback, useMemo } from 'react' | |||||
| import FileIcon from './file-icon' | import FileIcon from './file-icon' | ||||
| import { formatFileSize } from '@/utils/format' | import { formatFileSize } from '@/utils/format' | ||||
| import Tooltip from '@/app/components/base/tooltip' | import Tooltip from '@/app/components/base/tooltip' | ||||
| onOpen, | onOpen, | ||||
| }: ItemProps) => { | }: ItemProps) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const isBucket = file.type === 'bucket' | |||||
| const isFolder = file.type === 'folder' | |||||
| const { id, name, type, size } = file | |||||
| const isBucket = useMemo(() => type === 'bucket', [type]) | |||||
| const isFolder = useMemo(() => type === 'folder', [type]) | |||||
| const Wrapper = disabled ? Tooltip : React.Fragment | const Wrapper = disabled ? Tooltip : React.Fragment | ||||
| <Checkbox | <Checkbox | ||||
| className='shrink-0' | className='shrink-0' | ||||
| disabled={disabled} | disabled={disabled} | ||||
| id={file.key} | |||||
| id={id} | |||||
| checked={isSelected} | checked={isSelected} | ||||
| onCheck={handleSelect} | onCheck={handleSelect} | ||||
| /> | /> | ||||
| 'flex grow items-center gap-x-1 overflow-hidden py-0.5', | 'flex grow items-center gap-x-1 overflow-hidden py-0.5', | ||||
| disabled && 'opacity-30', | disabled && 'opacity-30', | ||||
| )}> | )}> | ||||
| <FileIcon type={file.type} fileName={file.displayName} className='shrink-0' /> | |||||
| <FileIcon type={type} fileName={name} className='shrink-0' /> | |||||
| <span | <span | ||||
| className='system-sm-medium grow truncate text-text-secondary' | className='system-sm-medium grow truncate text-text-secondary' | ||||
| title={file.displayName} | |||||
| title={name} | |||||
| > | > | ||||
| {file.displayName} | |||||
| {name} | |||||
| </span> | </span> | ||||
| {!isFolder && typeof file.size === 'number' && ( | |||||
| <span className='system-xs-regular shrink-0 text-text-tertiary'>{formatFileSize(file.size)}</span> | |||||
| {!isFolder && typeof size === 'number' && ( | |||||
| <span className='system-xs-regular shrink-0 text-text-tertiary'>{formatFileSize(size)}</span> | |||||
| )} | )} | ||||
| </div> | </div> | ||||
| </Wrapper> | </Wrapper> |
| const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id) | const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id) | ||||
| const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal) | const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal) | ||||
| const { | const { | ||||
| startAfter, | |||||
| nextPageParameters, | |||||
| prefix, | prefix, | ||||
| keywords, | keywords, | ||||
| bucket, | bucket, | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| fileList, | fileList, | ||||
| currentCredentialId, | currentCredentialId, | ||||
| } = useDataSourceStoreWithSelector(useShallow(state => ({ | } = useDataSourceStoreWithSelector(useShallow(state => ({ | ||||
| startAfter: state.startAfter, | |||||
| nextPageParameters: state.nextPageParameters, | |||||
| prefix: state.prefix, | prefix: state.prefix, | ||||
| keywords: state.keywords, | keywords: state.keywords, | ||||
| bucket: state.bucket, | bucket: state.bucket, | ||||
| selectedFileKeys: state.selectedFileKeys, | |||||
| selectedFileIds: state.selectedFileIds, | |||||
| fileList: state.fileList, | fileList: state.fileList, | ||||
| currentCredentialId: state.currentCredentialId, | currentCredentialId: state.currentCredentialId, | ||||
| }))) | }))) | ||||
| : `/rag/pipelines/${pipelineId}/workflows/draft/datasource/nodes/${nodeId}/run` | : `/rag/pipelines/${pipelineId}/workflows/draft/datasource/nodes/${nodeId}/run` | ||||
| const getOnlineDriveFiles = useCallback(async () => { | const getOnlineDriveFiles = useCallback(async () => { | ||||
| const { startAfter, prefix, bucket, fileList, currentCredentialId } = dataSourceStore.getState() | |||||
| const { nextPageParameters, prefix, bucket, fileList, currentCredentialId } = dataSourceStore.getState() | |||||
| const prefixString = prefix.length > 0 ? `${prefix.join('/')}/` : '' | const prefixString = prefix.length > 0 ? `${prefix.join('/')}/` : '' | ||||
| setIsLoading(true) | setIsLoading(true) | ||||
| ssePost( | ssePost( | ||||
| inputs: { | inputs: { | ||||
| prefix: prefixString, | prefix: prefixString, | ||||
| bucket, | bucket, | ||||
| start_after: startAfter, | |||||
| next_page_parameters: nextPageParameters, | |||||
| max_keys: 30, // Adjust as needed | max_keys: 30, // Adjust as needed | ||||
| }, | }, | ||||
| datasource_type: DatasourceType.onlineDrive, | datasource_type: DatasourceType.onlineDrive, | ||||
| }, | }, | ||||
| { | { | ||||
| onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => { | onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => { | ||||
| const { setFileList, isTruncated } = dataSourceStore.getState() | |||||
| const { fileList: newFileList, isTruncated: newIsTruncated } = convertOnlineDriveData(documentsData.data, prefix, bucket) | |||||
| const { setFileList, isTruncated, currentNextPageParametersRef } = dataSourceStore.getState() | |||||
| const { | |||||
| fileList: newFileList, | |||||
| isTruncated: newIsTruncated, | |||||
| nextPageParameters: newNextPageParameters, | |||||
| } = convertOnlineDriveData(documentsData.data, prefix, bucket) | |||||
| setFileList([...fileList, ...newFileList]) | setFileList([...fileList, ...newFileList]) | ||||
| isTruncated.current = newIsTruncated | isTruncated.current = newIsTruncated | ||||
| currentNextPageParametersRef.current = newNextPageParameters | |||||
| setIsLoading(false) | setIsLoading(false) | ||||
| }, | }, | ||||
| onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => { | onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => { | ||||
| else { | else { | ||||
| getOnlineDriveFiles() | getOnlineDriveFiles() | ||||
| } | } | ||||
| }, [startAfter, prefix, bucket, currentCredentialId]) | |||||
| }, [nextPageParameters, prefix, bucket, currentCredentialId]) | |||||
| const onlineDriveFileList = useMemo(() => { | const onlineDriveFileList = useMemo(() => { | ||||
| if (keywords) | if (keywords) | ||||
| return fileList.filter(file => file.key.toLowerCase().includes(keywords.toLowerCase())) | |||||
| return fileList.filter(file => file.name.toLowerCase().includes(keywords.toLowerCase())) | |||||
| return fileList | return fileList | ||||
| }, [fileList, keywords]) | }, [fileList, keywords]) | ||||
| }, [dataSourceStore]) | }, [dataSourceStore]) | ||||
| const handleSelectFile = useCallback((file: OnlineDriveFile) => { | const handleSelectFile = useCallback((file: OnlineDriveFile) => { | ||||
| const { selectedFileKeys, setSelectedFileKeys } = dataSourceStore.getState() | |||||
| const { selectedFileIds, setSelectedFileIds } = dataSourceStore.getState() | |||||
| if (file.type === OnlineDriveFileType.bucket) return | if (file.type === OnlineDriveFileType.bucket) return | ||||
| const newSelectedFileList = produce(selectedFileKeys, (draft) => { | |||||
| if (draft.includes(file.key)) { | |||||
| const index = draft.indexOf(file.key) | |||||
| const newSelectedFileList = produce(selectedFileIds, (draft) => { | |||||
| if (draft.includes(file.id)) { | |||||
| const index = draft.indexOf(file.id) | |||||
| draft.splice(index, 1) | draft.splice(index, 1) | ||||
| } | } | ||||
| else { | else { | ||||
| if (isInPipeline && draft.length >= 1) return | if (isInPipeline && draft.length >= 1) return | ||||
| draft.push(file.key) | |||||
| draft.push(file.id) | |||||
| } | } | ||||
| }) | }) | ||||
| setSelectedFileKeys(newSelectedFileList) | |||||
| setSelectedFileIds(newSelectedFileList) | |||||
| }, [dataSourceStore, isInPipeline]) | }, [dataSourceStore, isInPipeline]) | ||||
| const handleOpenFolder = useCallback((file: OnlineDriveFile) => { | const handleOpenFolder = useCallback((file: OnlineDriveFile) => { | ||||
| const { prefix, setPrefix, setBucket, setFileList, setSelectedFileKeys } = dataSourceStore.getState() | |||||
| const { prefix, setPrefix, setBucket, setFileList, setSelectedFileIds } = dataSourceStore.getState() | |||||
| if (file.type === OnlineDriveFileType.file) return | if (file.type === OnlineDriveFileType.file) return | ||||
| setFileList([]) | setFileList([]) | ||||
| if (file.type === OnlineDriveFileType.bucket) { | if (file.type === OnlineDriveFileType.bucket) { | ||||
| setBucket(file.displayName) | |||||
| setBucket(file.name) | |||||
| } | } | ||||
| else { | else { | ||||
| setSelectedFileKeys([]) | |||||
| const displayName = file.displayName.endsWith('/') ? file.displayName.slice(0, -1) : file.displayName | |||||
| setSelectedFileIds([]) | |||||
| const newPrefix = produce(prefix, (draft) => { | const newPrefix = produce(prefix, (draft) => { | ||||
| draft.push(displayName) | |||||
| draft.push(file.name) | |||||
| }) | }) | ||||
| setPrefix(newPrefix) | setPrefix(newPrefix) | ||||
| } | } | ||||
| /> | /> | ||||
| <FileList | <FileList | ||||
| fileList={onlineDriveFileList} | fileList={onlineDriveFileList} | ||||
| selectedFileKeys={selectedFileKeys} | |||||
| selectedFileIds={selectedFileIds} | |||||
| prefix={prefix} | prefix={prefix} | ||||
| keywords={keywords} | keywords={keywords} | ||||
| bucket={bucket} | bucket={bucket} |
| import { type OnlineDriveFile, OnlineDriveFileType } from '@/models/pipeline' | import { type OnlineDriveFile, OnlineDriveFileType } from '@/models/pipeline' | ||||
| import type { OnlineDriveData } from '@/types/pipeline' | import type { OnlineDriveData } from '@/types/pipeline' | ||||
| const filePathRegex = /^(?:.*\/)?[^\/]+\.[^\/\.]+$/ | |||||
| export const isFile = (path: string): boolean => { | |||||
| return filePathRegex.test(path) | |||||
| export const isFile = (type: 'file' | 'folder'): boolean => { | |||||
| return type === 'file' | |||||
| } | } | ||||
| export const isBucketListInitiation = (data: OnlineDriveData[], prefix: string[], bucket: string): boolean => { | export const isBucketListInitiation = (data: OnlineDriveData[], prefix: string[], bucket: string): boolean => { | ||||
| return data.length > 1 || (data.length === 1 && !!data[0].bucket && data[0].files.length === 0) | return 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 } => { | |||||
| export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[], bucket: string): { | |||||
| fileList: OnlineDriveFile[], | |||||
| isTruncated: boolean, | |||||
| nextPageParameters: Record<string, any> | |||||
| } => { | |||||
| const fileList: OnlineDriveFile[] = [] | const fileList: OnlineDriveFile[] = [] | ||||
| let isTruncated = false | let isTruncated = false | ||||
| let nextPageParameters: Record<string, any> = {} | |||||
| if (data.length === 0) | if (data.length === 0) | ||||
| return { fileList, isTruncated } | |||||
| return { fileList, isTruncated, nextPageParameters } | |||||
| if (isBucketListInitiation(data, prefix, bucket)) { | if (isBucketListInitiation(data, prefix, bucket)) { | ||||
| data.forEach((item) => { | data.forEach((item) => { | ||||
| fileList.push({ | fileList.push({ | ||||
| key: item.bucket, | |||||
| displayName: item.bucket, | |||||
| id: item.bucket, | |||||
| name: item.bucket, | |||||
| type: OnlineDriveFileType.bucket, | type: OnlineDriveFileType.bucket, | ||||
| }) | }) | ||||
| }) | }) | ||||
| } | } | ||||
| else { | else { | ||||
| data[0].files.forEach((file) => { | data[0].files.forEach((file) => { | ||||
| const isFileType = isFile(file.key) | |||||
| const filePathList = file.key.split('/') | |||||
| const { id, name, size, type } = file | |||||
| const isFileType = isFile(type) | |||||
| fileList.push({ | fileList.push({ | ||||
| key: file.key, | |||||
| displayName: `${isFileType ? filePathList.pop() : filePathList[filePathList.length - 2]}${isFileType ? '' : '/'}`, | |||||
| size: isFileType ? file.size : undefined, | |||||
| id, | |||||
| name, | |||||
| size: isFileType ? size : undefined, | |||||
| type: isFileType ? OnlineDriveFileType.file : OnlineDriveFileType.folder, | type: isFileType ? OnlineDriveFileType.file : OnlineDriveFileType.folder, | ||||
| }) | }) | ||||
| }) | }) | ||||
| isTruncated = data[0].is_truncated ?? false | isTruncated = data[0].is_truncated ?? false | ||||
| nextPageParameters = data[0].next_page_parameters ?? {} | |||||
| } | } | ||||
| return { fileList, isTruncated } | |||||
| return { fileList, isTruncated, nextPageParameters } | |||||
| } | } |
| setPrefix: (prefix: string[]) => void | setPrefix: (prefix: string[]) => void | ||||
| keywords: string | keywords: string | ||||
| setKeywords: (keywords: string) => void | setKeywords: (keywords: string) => void | ||||
| selectedFileKeys: string[] | |||||
| setSelectedFileKeys: (selectedFileKeys: string[]) => void | |||||
| selectedFileIds: string[] | |||||
| setSelectedFileIds: (selectedFileIds: string[]) => void | |||||
| fileList: OnlineDriveFile[] | fileList: OnlineDriveFile[] | ||||
| setFileList: (fileList: OnlineDriveFile[]) => void | setFileList: (fileList: OnlineDriveFile[]) => void | ||||
| bucket: string | bucket: string | ||||
| setBucket: (bucket: string) => void | setBucket: (bucket: string) => void | ||||
| startAfter: string | |||||
| setStartAfter: (startAfter: string) => void | |||||
| nextPageParameters: Record<string, any> | |||||
| currentNextPageParametersRef: React.RefObject<Record<string, any>> | |||||
| setNextPageParameters: (nextPageParameters: Record<string, any>) => void | |||||
| isTruncated: React.RefObject<boolean> | isTruncated: React.RefObject<boolean> | ||||
| previewOnlineDriveFileRef: React.RefObject<OnlineDriveFile | undefined> | previewOnlineDriveFileRef: React.RefObject<OnlineDriveFile | undefined> | ||||
| } | } | ||||
| setKeywords: (keywords: string) => set(() => ({ | setKeywords: (keywords: string) => set(() => ({ | ||||
| keywords, | keywords, | ||||
| })), | })), | ||||
| selectedFileKeys: [], | |||||
| setSelectedFileKeys: (selectedFileKeys: string[]) => { | |||||
| selectedFileIds: [], | |||||
| setSelectedFileIds: (selectedFileIds: string[]) => { | |||||
| set(() => ({ | set(() => ({ | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| })) | })) | ||||
| const key = selectedFileKeys[0] | |||||
| const id = selectedFileIds[0] | |||||
| const { fileList, previewOnlineDriveFileRef } = get() | const { fileList, previewOnlineDriveFileRef } = get() | ||||
| previewOnlineDriveFileRef.current = fileList.find(file => file.key === key) | |||||
| previewOnlineDriveFileRef.current = fileList.find(file => file.id === id) | |||||
| }, | }, | ||||
| fileList: [], | fileList: [], | ||||
| setFileList: (fileList: OnlineDriveFile[]) => set(() => ({ | setFileList: (fileList: OnlineDriveFile[]) => set(() => ({ | ||||
| setBucket: (bucket: string) => set(() => ({ | setBucket: (bucket: string) => set(() => ({ | ||||
| bucket, | bucket, | ||||
| })), | })), | ||||
| startAfter: '', | |||||
| setStartAfter: (startAfter: string) => set(() => ({ | |||||
| startAfter, | |||||
| nextPageParameters: {}, | |||||
| currentNextPageParametersRef: { current: {} }, | |||||
| setNextPageParameters: (nextPageParameters: Record<string, any>) => set(() => ({ | |||||
| nextPageParameters, | |||||
| })), | })), | ||||
| isTruncated: { current: false }, | isTruncated: { current: false }, | ||||
| previewOnlineDriveFileRef: { current: undefined }, | previewOnlineDriveFileRef: { current: undefined }, |
| export const useOnlineDrive = () => { | export const useOnlineDrive = () => { | ||||
| const { | const { | ||||
| fileList, | fileList, | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| } = useDataSourceStoreWithSelector(useShallow(state => ({ | } = useDataSourceStoreWithSelector(useShallow(state => ({ | ||||
| fileList: state.fileList, | fileList: state.fileList, | ||||
| selectedFileKeys: state.selectedFileKeys, | |||||
| selectedFileIds: state.selectedFileIds, | |||||
| }))) | }))) | ||||
| const dataSourceStore = useDataSourceStore() | const dataSourceStore = useDataSourceStore() | ||||
| const selectedOnlineDriveFileList = useMemo(() => { | const selectedOnlineDriveFileList = useMemo(() => { | ||||
| return selectedFileKeys.map(key => fileList.find(item => item.key === key)!) | |||||
| }, [fileList, selectedFileKeys]) | |||||
| return selectedFileIds.map(key => fileList.find(item => item.key === key)!) | |||||
| }, [fileList, selectedFileIds]) | |||||
| const clearOnlineDriveData = useCallback(() => { | const clearOnlineDriveData = useCallback(() => { | ||||
| const { | const { | ||||
| setBucket, | setBucket, | ||||
| setPrefix, | setPrefix, | ||||
| setKeywords, | setKeywords, | ||||
| setSelectedFileKeys, | |||||
| setSelectedFileIds, | |||||
| } = dataSourceStore.getState() | } = dataSourceStore.getState() | ||||
| setFileList([]) | setFileList([]) | ||||
| setBucket('') | setBucket('') | ||||
| setPrefix([]) | setPrefix([]) | ||||
| setKeywords('') | setKeywords('') | ||||
| setSelectedFileKeys([]) | |||||
| setSelectedFileIds([]) | |||||
| }, [dataSourceStore]) | }, [dataSourceStore]) | ||||
| return { | return { | ||||
| fileList, | fileList, | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| selectedOnlineDriveFileList, | selectedOnlineDriveFileList, | ||||
| clearOnlineDriveData, | clearOnlineDriveData, | ||||
| } | } |
| } = useWebsiteCrawl() | } = useWebsiteCrawl() | ||||
| const { | const { | ||||
| fileList: onlineDriveFileList, | fileList: onlineDriveFileList, | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| selectedOnlineDriveFileList, | selectedOnlineDriveFileList, | ||||
| clearOnlineDriveData, | clearOnlineDriveData, | ||||
| } = useOnlineDrive() | } = useOnlineDrive() | ||||
| if (datasourceType === DatasourceType.websiteCrawl) | if (datasourceType === DatasourceType.websiteCrawl) | ||||
| return isShowVectorSpaceFull || !websitePages.length | return isShowVectorSpaceFull || !websitePages.length | ||||
| if (datasourceType === DatasourceType.onlineDrive) | if (datasourceType === DatasourceType.onlineDrive) | ||||
| return isShowVectorSpaceFull || !selectedFileKeys.length | |||||
| return isShowVectorSpaceFull || !selectedFileIds.length | |||||
| return false | return false | ||||
| }, [datasource, datasourceType, isShowVectorSpaceFull, fileList.length, allFileLoaded, onlineDocuments.length, websitePages.length, selectedFileKeys.length]) | |||||
| }, [datasource, datasourceType, isShowVectorSpaceFull, fileList.length, allFileLoaded, onlineDocuments.length, websitePages.length, selectedFileIds.length]) | |||||
| const fileUploadConfig = useMemo(() => fileUploadConfigResponse ?? { | const fileUploadConfig = useMemo(() => fileUploadConfigResponse ?? { | ||||
| file_size_limit: 15, | file_size_limit: 15, | ||||
| if (datasourceType === DatasourceType.onlineDocument) | if (datasourceType === DatasourceType.onlineDocument) | ||||
| return onlineDocuments.length | return onlineDocuments.length | ||||
| if (datasourceType === DatasourceType.onlineDrive) | if (datasourceType === DatasourceType.onlineDrive) | ||||
| return selectedFileKeys.length | |||||
| }, [datasourceType, onlineDocuments.length, selectedFileKeys.length]) | |||||
| return selectedFileIds.length | |||||
| }, [datasourceType, onlineDocuments.length, selectedFileIds.length]) | |||||
| const tip = useMemo(() => { | const tip = useMemo(() => { | ||||
| if (datasourceType === DatasourceType.onlineDocument) | if (datasourceType === DatasourceType.onlineDocument) | ||||
| } | } | ||||
| if (datasourceType === DatasourceType.onlineDrive) { | if (datasourceType === DatasourceType.onlineDrive) { | ||||
| const { bucket } = dataSourceStore.getState() | const { bucket } = dataSourceStore.getState() | ||||
| const { key } = previewOnlineDriveFileRef.current! | |||||
| const { id } = previewOnlineDriveFileRef.current! | |||||
| datasourceInfoList.push({ | datasourceInfoList.push({ | ||||
| bucket, | bucket, | ||||
| key, | |||||
| id, | |||||
| credential_id: currentCredentialId, | credential_id: currentCredentialId, | ||||
| }) | }) | ||||
| } | } | ||||
| } | } | ||||
| if (datasourceType === DatasourceType.onlineDrive) { | if (datasourceType === DatasourceType.onlineDrive) { | ||||
| if (datasourceType === DatasourceType.onlineDrive) { | if (datasourceType === DatasourceType.onlineDrive) { | ||||
| selectedFileKeys.forEach((key) => { | |||||
| selectedFileIds.forEach((key) => { | |||||
| datasourceInfoList.push({ | datasourceInfoList.push({ | ||||
| bucket, | bucket, | ||||
| key, | key, | ||||
| handleNextStep() | handleNextStep() | ||||
| }, | }, | ||||
| }) | }) | ||||
| }, [dataSourceStore, datasource, datasourceType, fileList, handleNextStep, onlineDocuments, pipelineId, runPublishedPipeline, selectedFileKeys, websitePages]) | |||||
| }, [dataSourceStore, datasource, datasourceType, fileList, handleNextStep, onlineDocuments, pipelineId, runPublishedPipeline, selectedFileIds, websitePages]) | |||||
| const onClickProcess = useCallback(() => { | const onClickProcess = useCallback(() => { | ||||
| isPreview.current = false | isPreview.current = false | ||||
| const { | const { | ||||
| onlineDocuments, | onlineDocuments, | ||||
| fileList: onlineDriveFileList, | fileList: onlineDriveFileList, | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| setOnlineDocuments, | setOnlineDocuments, | ||||
| setSelectedFileKeys, | |||||
| setSelectedFileIds, | |||||
| setSelectedPagesId, | setSelectedPagesId, | ||||
| } = dataSourceStore.getState() | } = dataSourceStore.getState() | ||||
| if (datasourceType === DatasourceType.onlineDocument) { | if (datasourceType === DatasourceType.onlineDocument) { | ||||
| if (datasourceType === DatasourceType.onlineDrive) { | if (datasourceType === DatasourceType.onlineDrive) { | ||||
| const allKeys = onlineDriveFileList.filter((item) => { | const allKeys = onlineDriveFileList.filter((item) => { | ||||
| return item.type !== 'bucket' | return item.type !== 'bucket' | ||||
| }).map(file => file.key) | |||||
| if (selectedFileKeys.length < allKeys.length) | |||||
| setSelectedFileKeys(allKeys) | |||||
| }).map(file => file.id) | |||||
| if (selectedFileIds.length < allKeys.length) | |||||
| setSelectedFileIds(allKeys) | |||||
| else | else | ||||
| setSelectedFileKeys([]) | |||||
| setSelectedFileIds([]) | |||||
| } | } | ||||
| }, [PagesMapAndSelectedPagesId, currentWorkspace?.pages, dataSourceStore, datasourceType]) | }, [PagesMapAndSelectedPagesId, currentWorkspace?.pages, dataSourceStore, datasourceType]) | ||||
| <PortalToFollowElemContent className='z-[61]'> | <PortalToFollowElemContent className='z-[61]'> | ||||
| <div className='w-[240px] space-y-1.5 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-lg'> | <div className='w-[240px] space-y-1.5 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-lg'> | ||||
| { | { | ||||
| canOAuth && ( | |||||
| !!canOAuth && ( | |||||
| <AddOAuthButton | <AddOAuthButton | ||||
| {...oAuthButtonProps} | {...oAuthButtonProps} | ||||
| onUpdate={onUpdate} | onUpdate={onUpdate} | ||||
| ) | ) | ||||
| } | } | ||||
| { | { | ||||
| canApiKey && canOAuth && ( | |||||
| !!canApiKey && !!canOAuth && ( | |||||
| <div className='system-2xs-medium-uppercase flex h-4 items-center p-2 text-text-quaternary'> | <div className='system-2xs-medium-uppercase flex h-4 items-center p-2 text-text-quaternary'> | ||||
| <div className='mr-2 h-[1px] grow bg-gradient-to-l from-[rgba(16,24,40,0.08)]' /> | <div className='mr-2 h-[1px] grow bg-gradient-to-l from-[rgba(16,24,40,0.08)]' /> | ||||
| OR | OR | ||||
| ) | ) | ||||
| } | } | ||||
| { | { | ||||
| canApiKey && ( | |||||
| !!canApiKey && ( | |||||
| <AddApiKeyButton | <AddApiKeyButton | ||||
| {...apiKeyButtonProps} | {...apiKeyButtonProps} | ||||
| formSchemas={item.credential_schema} | formSchemas={item.credential_schema} |
| setBucket, | setBucket, | ||||
| setPrefix, | setPrefix, | ||||
| setKeywords, | setKeywords, | ||||
| setSelectedFileKeys, | |||||
| setSelectedFileIds, | |||||
| } = dataSourceStore.getState() | } = dataSourceStore.getState() | ||||
| setFileList([]) | setFileList([]) | ||||
| setBucket('') | setBucket('') | ||||
| setPrefix([]) | setPrefix([]) | ||||
| setKeywords('') | setKeywords('') | ||||
| setSelectedFileKeys([]) | |||||
| setSelectedFileIds([]) | |||||
| }, [dataSourceStore]) | }, [dataSourceStore]) | ||||
| return { | return { |
| localFileList: fileList, | localFileList: fileList, | ||||
| onlineDocuments, | onlineDocuments, | ||||
| websitePages, | websitePages, | ||||
| selectedFileKeys, | |||||
| selectedFileIds, | |||||
| } = useDataSourceStoreWithSelector(useShallow(state => ({ | } = useDataSourceStoreWithSelector(useShallow(state => ({ | ||||
| localFileList: state.localFileList, | localFileList: state.localFileList, | ||||
| onlineDocuments: state.onlineDocuments, | onlineDocuments: state.onlineDocuments, | ||||
| websitePages: state.websitePages, | websitePages: state.websitePages, | ||||
| selectedFileKeys: state.selectedFileKeys, | |||||
| selectedFileIds: state.selectedFileIds, | |||||
| }))) | }))) | ||||
| const dataSourceStore = useDataSourceStore() | const dataSourceStore = useDataSourceStore() | ||||
| const [datasource, setDatasource] = useState<Datasource>() | const [datasource, setDatasource] = useState<Datasource>() | ||||
| if (datasourceType === DatasourceType.websiteCrawl) | if (datasourceType === DatasourceType.websiteCrawl) | ||||
| return !websitePages.length | return !websitePages.length | ||||
| if (datasourceType === DatasourceType.onlineDrive) | if (datasourceType === DatasourceType.onlineDrive) | ||||
| return !selectedFileKeys.length | |||||
| return !selectedFileIds.length | |||||
| return false | return false | ||||
| }, [datasource, datasourceType, fileList, onlineDocuments.length, selectedFileKeys.length, websitePages.length]) | |||||
| }, [datasource, datasourceType, fileList, onlineDocuments.length, selectedFileIds.length, websitePages.length]) | |||||
| const handleClose = useCallback(() => { | const handleClose = useCallback(() => { | ||||
| setShowDebugAndPreviewPanel(false) | setShowDebugAndPreviewPanel(false) | ||||
| const { bucket } = dataSourceStore.getState() | const { bucket } = dataSourceStore.getState() | ||||
| datasourceInfoList.push({ | datasourceInfoList.push({ | ||||
| bucket, | bucket, | ||||
| key: selectedFileKeys[0], | |||||
| id: selectedFileIds[0], | |||||
| credential_id: credentialId, | credential_id: credentialId, | ||||
| }) | }) | ||||
| } | } | ||||
| datasource_type: datasourceType, | datasource_type: datasourceType, | ||||
| datasource_info_list: datasourceInfoList, | datasource_info_list: datasourceInfoList, | ||||
| }) | }) | ||||
| }, [dataSourceStore, datasource, datasourceType, fileList, handleRun, onlineDocuments, selectedFileKeys, websitePages]) | |||||
| }, [dataSourceStore, datasource, datasourceType, fileList, handleRun, onlineDocuments, selectedFileIds, websitePages]) | |||||
| const clearDataSourceData = useCallback((dataSource: Datasource) => { | const clearDataSourceData = useCallback((dataSource: Datasource) => { | ||||
| if (dataSource.nodeData.provider_type === DatasourceType.onlineDocument) | if (dataSource.nodeData.provider_type === DatasourceType.onlineDocument) |
| } | } | ||||
| export type OnlineDriveFile = { | export type OnlineDriveFile = { | ||||
| key: string | |||||
| displayName: string | |||||
| id: string | |||||
| name: string | |||||
| size?: number | size?: number | ||||
| type: OnlineDriveFileType | type: OnlineDriveFileType | ||||
| } | } |
| } | } | ||||
| export type OnlineDriveFile = { | export type OnlineDriveFile = { | ||||
| key: string | |||||
| id: string | |||||
| name: string | |||||
| size: number | size: number | ||||
| type: 'file' | 'folder' | |||||
| } | } | ||||
| export type OnlineDriveData = { | export type OnlineDriveData = { | ||||
| bucket: string | bucket: string | ||||
| files: OnlineDriveFile[] | files: OnlineDriveFile[] | ||||
| is_truncated: boolean | is_truncated: boolean | ||||
| next_page_parameters: Record<string, any> | |||||
| } | } | ||||
| export type DataSourceNodeCompletedResponse = { | export type DataSourceNodeCompletedResponse = { |