diff --git a/app/src/api/file.ts b/app/src/api/file.ts index df387ce..862c4ce 100644 --- a/app/src/api/file.ts +++ b/app/src/api/file.ts @@ -199,20 +199,24 @@ export const moveFile = async ( }; /** - * uploadFile uploads a file to a workspace + * uploadFile uploads multiple files to a workspace * @param workspaceName - The name of the workspace - * @param directoryPath - The directory path where the file should be uploaded - * @param file - The file to upload + * @param directoryPath - The directory path where files should be uploaded + * @param files - Multiple files to upload * @returns {Promise} A promise that resolves to the upload file response * @throws {Error} If the API call fails or returns an invalid response */ export const uploadFile = async ( workspaceName: string, directoryPath: string, - file: File + files: FileList ): Promise => { const formData = new FormData(); - formData.append('file', file); + + // Add all files to the form data + Array.from(files).forEach((file) => { + formData.append('file', file); + }); const response = await apiCall( `${API_BASE_URL}/workspaces/${encodeURIComponent( diff --git a/app/src/components/files/FileTree.tsx b/app/src/components/files/FileTree.tsx index 67f0c9e..f940016 100644 --- a/app/src/components/files/FileTree.tsx +++ b/app/src/components/files/FileTree.tsx @@ -103,7 +103,49 @@ function Node({ ); } -const FileTree: React.FC = ({ +// Utility function to recursively find file paths by IDs +const findFilePathsById = (files: FileNode[], ids: string[]): string[] => { + const paths: string[] = []; + + const searchFiles = (nodes: FileNode[]) => { + for (const node of nodes) { + if (ids.includes(node.id)) { + paths.push(node.path); + } + if (node.children) { + searchFiles(node.children); + } + } + }; + + searchFiles(files); + return paths; +}; + +// Utility function to find parent path by ID +const findParentPathById = ( + files: FileNode[], + parentId: string | null +): string => { + if (!parentId) return ''; + + const searchFiles = (nodes: FileNode[]): string | null => { + for (const node of nodes) { + if (node.id === parentId) { + return node.path; + } + if (node.children) { + const result = searchFiles(node.children); + if (result) return result; + } + } + return null; + }; + + return searchFiles(files) || ''; +}; + +export const FileTree: React.FC = ({ files, handleFileSelect, showHiddenFiles, @@ -143,7 +185,14 @@ const FileTree: React.FC = ({ index: number; }) => { try { - const success = await handleMove(dragIds, parentId, index); + // Map dragged file IDs to their corresponding paths + const dragPaths = findFilePathsById(filteredFiles, dragIds); + + // Find the parent path where files will be moved + const targetParentPath = findParentPathById(filteredFiles, parentId); + + // Move files to the new location + const success = await handleMove(dragPaths, targetParentPath, index); if (success) { await loadFileList(); } @@ -151,7 +200,7 @@ const FileTree: React.FC = ({ console.error('Error moving files:', error); } }, - [handleMove, loadFileList] + [handleMove, loadFileList, filteredFiles] ); // External file drag and drop handlers @@ -256,7 +305,6 @@ const FileTree: React.FC = ({ height={size.height} indent={24} rowHeight={28} - // Enable drag and drop for moving files onMove={handleTreeMove} onActivate={(node) => { const fileNode = node.data; diff --git a/app/src/hooks/useFileOperations.ts b/app/src/hooks/useFileOperations.ts index e2e5004..0bec62c 100644 --- a/app/src/hooks/useFileOperations.ts +++ b/app/src/hooks/useFileOperations.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react'; import { notifications } from '@mantine/notifications'; -import { saveFile, deleteFile } from '../api/file'; +import { saveFile, deleteFile, uploadFile, moveFile } from '../api/file'; import { useWorkspaceData } from '../contexts/WorkspaceDataContext'; import { useGitOperations } from './useGitOperations'; import { FileAction } from '@/types/models'; @@ -11,9 +11,9 @@ interface UseFileOperationsResult { handleCreate: (fileName: string, initialContent?: string) => Promise; handleUpload: (files: FileList, targetPath?: string) => Promise; handleMove: ( - dragIds: string[], - parentId: string | null, - index: number + filePaths: string[], + destinationParentPath: string, + index?: number ) => Promise; handleRename: (oldPath: string, newPath: string) => Promise; } @@ -117,13 +117,13 @@ export const useFileOperations = (): UseFileOperationsResult => { [currentWorkspace, autoCommit] ); - // Add these to your hook implementation: const handleUpload = useCallback( async (files: FileList, targetPath?: string): Promise => { if (!currentWorkspace) return false; try { - // TODO: Implement your file upload API call + // Use unified upload API that handles both single and multiple files + await uploadFile(currentWorkspace.name, targetPath || '', files); notifications.show({ title: 'Success', @@ -149,18 +149,32 @@ export const useFileOperations = (): UseFileOperationsResult => { const handleMove = useCallback( async ( - dragIds: string[], - parentId: string | null, - index: number + filePaths: string[], + destinationParentPath: string, + _index?: number ): Promise => { if (!currentWorkspace) return false; try { - // TODO: Implement your file move API call + // Move each file to the destination directory + const movePromises = filePaths.map(async (filePath) => { + // Extract the filename from the path + const fileName = filePath.split('/').pop() || ''; + + // Construct the destination path + const destinationPath = destinationParentPath + ? `${destinationParentPath}/${fileName}` + : fileName; + + // Call the API to move the file + await moveFile(currentWorkspace.name, filePath, destinationPath); + }); + + await Promise.all(movePromises); notifications.show({ title: 'Success', - message: `Successfully moved ${dragIds.length} file(s)`, + message: `Successfully moved ${filePaths.length} file(s)`, color: 'green', }); @@ -185,8 +199,8 @@ export const useFileOperations = (): UseFileOperationsResult => { if (!currentWorkspace) return false; try { - // TODO: Replace with your actual rename API call - // await renameFile(currentWorkspace.name, oldPath, newPath); + // Use moveFile API for renaming (rename is essentially a move operation) + await moveFile(currentWorkspace.name, oldPath, newPath); notifications.show({ title: 'Success',