diff --git a/frontend/src/components/FileTreeComponent.tsx b/frontend/src/components/FileTreeComponent.tsx index 19cf006b..e0f06663 100644 --- a/frontend/src/components/FileTreeComponent.tsx +++ b/frontend/src/components/FileTreeComponent.tsx @@ -1,6 +1,7 @@ import React, { useState, useRef, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; +import { selectToken } from '../preferences/preferenceSlice'; import Chunks from './Chunks'; import ContextMenu, { MenuOption } from './ContextMenu'; import userService from '../api/services/userService'; @@ -49,7 +50,7 @@ const FileTreeComponent: React.FC = ({ const [directoryStructure, setDirectoryStructure] = useState(null); const [currentPath, setCurrentPath] = useState([]); - const token = useSelector((state: any) => state.auth?.token); + const token = useSelector(selectToken); const [activeMenuId, setActiveMenuId] = useState(null); const menuRefs = useRef<{ [key: string]: React.RefObject; @@ -61,10 +62,17 @@ const FileTreeComponent: React.FC = ({ const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const searchDropdownRef = useRef(null); - const currentOpRef = useRef(null); + const currentOpRef = useRef( + null, + ); - const [deleteModalState, setDeleteModalState] = useState<'ACTIVE' | 'INACTIVE'>('INACTIVE'); - const [itemToDelete, setItemToDelete] = useState<{ name: string; isFile: boolean } | null>(null); + const [deleteModalState, setDeleteModalState] = useState< + 'ACTIVE' | 'INACTIVE' + >('INACTIVE'); + const [itemToDelete, setItemToDelete] = useState<{ + name: string; + isFile: boolean; + } | null>(null); type QueuedOperation = { operation: 'add' | 'remove' | 'remove_directory'; @@ -84,7 +92,7 @@ const FileTreeComponent: React.FC = ({ setSearchResults([]); }, [], - false + false, ); const handleFileClick = (fileName: string) => { @@ -144,7 +152,10 @@ const FileTreeComponent: React.FC = ({ try { structure = JSON.parse(structure); } catch (e) { - console.error('Error parsing directory structure in getCurrentDirectory:', e); + console.error( + 'Error parsing directory structure in getCurrentDirectory:', + e, + ); return {}; } } @@ -155,7 +166,11 @@ const FileTreeComponent: React.FC = ({ let current: any = structure; for (const dir of currentPath) { - if (current[dir] && typeof current[dir] === 'object' && !current[dir].type) { + if ( + current[dir] && + typeof current[dir] === 'object' && + !current[dir].type + ) { current = current[dir]; } else { return {}; @@ -231,13 +246,13 @@ const FileTreeComponent: React.FC = ({ return options; }; - + const confirmDeleteItem = (name: string, isFile: boolean) => { setItemToDelete({ name, isFile }); setDeleteModalState('ACTIVE'); - setActiveMenuId(null); + setActiveMenuId(null); }; - + const handleConfirmedDelete = async () => { if (itemToDelete) { await handleDeleteFile(itemToDelete.name, itemToDelete.isFile); @@ -245,12 +260,12 @@ const FileTreeComponent: React.FC = ({ setItemToDelete(null); } }; - + const handleCancelDelete = () => { setDeleteModalState('INACTIVE'); setItemToDelete(null); }; - + const manageSource = async ( operation: 'add' | 'remove' | 'remove_directory', files?: File[] | null, @@ -277,36 +292,48 @@ const FileTreeComponent: React.FC = ({ } else if (operation === 'remove_directory' && directoryPath) { formData.append('directory_path', directoryPath); } - + const response = await userService.manageSourceFiles(formData, token); const result = await response.json(); - + if (result.success && result.reingest_task_id) { if (operation === 'add') { console.log('Files uploaded successfully:', result.added_files); } else if (operation === 'remove') { console.log('Files deleted successfully:', result.removed_files); } else if (operation === 'remove_directory') { - console.log('Directory deleted successfully:', result.removed_directory); + console.log( + 'Directory deleted successfully:', + result.removed_directory, + ); } console.log('Reingest task started:', result.reingest_task_id); - + const maxAttempts = 30; const pollInterval = 2000; - + for (let attempt = 0; attempt < maxAttempts; attempt++) { try { - const statusResponse = await userService.getTaskStatus(result.reingest_task_id, token); + const statusResponse = await userService.getTaskStatus( + result.reingest_task_id, + token, + ); const statusData = await statusResponse.json(); - - console.log(`Task status (attempt ${attempt + 1}):`, statusData.status); - + + console.log( + `Task status (attempt ${attempt + 1}):`, + statusData.status, + ); + if (statusData.status === 'SUCCESS') { console.log('Task completed successfully'); - - const structureResponse = await userService.getDirectoryStructure(docId, token); + + const structureResponse = await userService.getDirectoryStructure( + docId, + token, + ); const structureData = await structureResponse.json(); - + if (structureData && structureData.directory_structure) { setDirectoryStructure(structureData.directory_structure); currentOpRef.current = null; @@ -317,19 +344,31 @@ const FileTreeComponent: React.FC = ({ console.error('Task failed'); break; } - - await new Promise(resolve => setTimeout(resolve, pollInterval)); + + await new Promise((resolve) => setTimeout(resolve, pollInterval)); } catch (error) { console.error('Error polling task status:', error); break; } } } else { - throw new Error(`Failed to ${operation} ${operation === 'remove_directory' ? 'directory' : 'file(s)'}`); + throw new Error( + `Failed to ${operation} ${operation === 'remove_directory' ? 'directory' : 'file(s)'}`, + ); } } catch (error) { - const actionText = operation === 'add' ? 'uploading' : operation === 'remove_directory' ? 'deleting directory' : 'deleting file(s)'; - const errorText = operation === 'add' ? 'upload' : operation === 'remove_directory' ? 'delete directory' : 'delete file(s)'; + const actionText = + operation === 'add' + ? 'uploading' + : operation === 'remove_directory' + ? 'deleting directory' + : 'deleting file(s)'; + const errorText = + operation === 'add' + ? 'upload' + : operation === 'remove_directory' + ? 'delete directory' + : 'delete file(s)'; console.error(`Error ${actionText}:`, error); setError(`Failed to ${errorText}`); } finally { @@ -371,13 +410,18 @@ const FileTreeComponent: React.FC = ({ const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.multiple = true; - fileInput.accept = '.rst,.md,.pdf,.txt,.docx,.csv,.epub,.html,.mdx,.json,.xlsx,.pptx,.png,.jpg,.jpeg'; + fileInput.accept = + '.rst,.md,.pdf,.txt,.docx,.csv,.epub,.html,.mdx,.json,.xlsx,.pptx,.png,.jpg,.jpeg'; fileInput.onchange = async (event) => { const fileList = (event.target as HTMLInputElement).files; if (!fileList || fileList.length === 0) return; const files = Array.from(fileList); - enqueueOperation({ operation: 'add', files, parentDirPath: currentPath.join('/') }); + enqueueOperation({ + operation: 'add', + files, + parentDirPath: currentPath.join('/'), + }); }; fileInput.click(); @@ -390,14 +434,17 @@ const FileTreeComponent: React.FC = ({ if (isFile) { enqueueOperation({ operation: 'remove', filePath: itemPath }); } else { - enqueueOperation({ operation: 'remove_directory', directoryPath: itemPath }); + enqueueOperation({ + operation: 'remove_directory', + directoryPath: itemPath, + }); } }; const renderPathNavigation = () => { return ( -
-
+
+
-
- source - {sourceName} +
+ source + + {sourceName} + {currentPath.length > 0 && ( <> - / + / {currentPath.map((dir, index) => ( - + {dir} {index < currentPath.length - 1 && ( - / + + / + )} ))} @@ -425,8 +480,8 @@ const FileTreeComponent: React.FC = ({ )} {selectedFile && ( <> - / - + / + {selectedFile.name} @@ -435,17 +490,29 @@ const FileTreeComponent: React.FC = ({
{!selectedFile && ( -
+
{(processingRef.current || queueLength > 0) && ( - {processingRef.current ? (currentOpRef.current === 'add' ? 'Uploading…' : 'Deleting…') : null} - {queueLength > 0 ? `${processingRef.current ? ' • ' : ''}Queued: ${queueLength}` : ''} + {processingRef.current + ? currentOpRef.current === 'add' + ? 'Uploading…' + : 'Deleting…' + : null} + {queueLength > 0 + ? `${processingRef.current ? ' • ' : ''}Queued: ${queueLength}` + : ''} )} @@ -487,26 +554,32 @@ const FileTreeComponent: React.FC = ({ const parentRow = currentPath.length > 0 ? [ - - -
- {t('settings.sources.parentFolderAlt')} - .. -
- - - - - - - , - ] + + +
+ {t('settings.sources.parentFolderAlt')} + + .. + +
+ + + - + + + - + + + , + ] : []; // Render directories first, then files @@ -523,21 +596,27 @@ const FileTreeComponent: React.FC = ({ className="cursor-pointer border-b border-[#D1D9E0] hover:bg-[#ECEEEF] dark:border-[#6A6A6A] dark:hover:bg-[#27282D]" onClick={() => navigateToDirectory(name)} > - -
- {t('settings.sources.folderAlt')} - {name} + +
+ {t('settings.sources.folderAlt')} + + {name} +
- + {dirStats.totalTokens > 0 ? dirStats.totalTokens.toLocaleString() : '-'} - + {dirStats.totalSize > 0 ? formatBytes(dirStats.totalSize) : '-'} - +