Implement file upload functionality in FileActions and FileTree components

This commit is contained in:
2025-07-07 19:13:25 +02:00
parent b10591ee60
commit 4a3df3a040
4 changed files with 139 additions and 27 deletions

View File

@@ -9,17 +9,18 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useModalContext } from '../../contexts/ModalContext'; import { useModalContext } from '../../contexts/ModalContext';
import { useWorkspace } from '../../hooks/useWorkspace'; import { useWorkspace } from '../../hooks/useWorkspace';
import { useFileOperations } from '../../hooks/useFileOperations';
interface FileActionsProps { interface FileActionsProps {
handlePullChanges: () => Promise<boolean>; handlePullChanges: () => Promise<boolean>;
selectedFile: string | null; selectedFile: string | null;
onFileUpload?: (files: FileList) => Promise<void>; loadFileList: () => Promise<void>;
} }
const FileActions: React.FC<FileActionsProps> = ({ const FileActions: React.FC<FileActionsProps> = ({
handlePullChanges, handlePullChanges,
selectedFile, selectedFile,
onFileUpload, loadFileList,
}) => { }) => {
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const { const {
@@ -28,6 +29,8 @@ const FileActions: React.FC<FileActionsProps> = ({
setCommitMessageModalVisible, setCommitMessageModalVisible,
} = useModalContext(); } = useModalContext();
const { handleUpload } = useFileOperations();
// Hidden file input for upload // Hidden file input for upload
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
@@ -43,10 +46,20 @@ const FileActions: React.FC<FileActionsProps> = ({
event: React.ChangeEvent<HTMLInputElement> event: React.ChangeEvent<HTMLInputElement>
): void => { ): void => {
const files = event.target.files; const files = event.target.files;
if (files && files.length > 0 && onFileUpload) { if (files && files.length > 0) {
onFileUpload(files).catch((error) => { const uploadFiles = async () => {
console.error('Error uploading files:', error); try {
}); const success = await handleUpload(files);
if (success) {
await loadFileList();
}
} catch (error) {
console.error('Error uploading files:', error);
}
};
void uploadFiles();
// Reset the input so the same file can be selected again // Reset the input so the same file can be selected again
event.target.value = ''; event.target.value = '';
} }

View File

@@ -8,6 +8,7 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { Tooltip, Text, Box } from '@mantine/core'; import { Tooltip, Text, Box } from '@mantine/core';
import useResizeObserver from '@react-hook/resize-observer'; import useResizeObserver from '@react-hook/resize-observer';
import { useFileOperations } from '../../hooks/useFileOperations';
import type { FileNode } from '@/types/models'; import type { FileNode } from '@/types/models';
interface Size { interface Size {
@@ -19,12 +20,7 @@ interface FileTreeProps {
files: FileNode[]; files: FileNode[];
handleFileSelect: (filePath: string | null) => Promise<void>; handleFileSelect: (filePath: string | null) => Promise<void>;
showHiddenFiles: boolean; showHiddenFiles: boolean;
onFileMove?: ( loadFileList: () => Promise<void>;
dragIds: string[],
parentId: string | null,
index: number
) => Promise<void>;
onFileUpload?: (files: FileList, targetPath?: string) => Promise<void>;
} }
const useSize = (target: React.RefObject<HTMLElement>): Size | undefined => { const useSize = (target: React.RefObject<HTMLElement>): Size | undefined => {
@@ -111,11 +107,11 @@ const FileTree: React.FC<FileTreeProps> = ({
files, files,
handleFileSelect, handleFileSelect,
showHiddenFiles, showHiddenFiles,
onFileMove, loadFileList,
onFileUpload,
}) => { }) => {
const target = useRef<HTMLDivElement>(null); const target = useRef<HTMLDivElement>(null);
const size = useSize(target); const size = useSize(target);
const { handleMove, handleUpload } = useFileOperations();
// State for drag and drop overlay // State for drag and drop overlay
const [isDragOver, setIsDragOver] = useState(false); const [isDragOver, setIsDragOver] = useState(false);
@@ -136,7 +132,7 @@ const FileTree: React.FC<FileTreeProps> = ({
}; };
// Handle file movement within the tree // Handle file movement within the tree
const handleMove = useCallback( const handleTreeMove = useCallback(
async ({ async ({
dragIds, dragIds,
parentId, parentId,
@@ -146,11 +142,16 @@ const FileTree: React.FC<FileTreeProps> = ({
parentId: string | null; parentId: string | null;
index: number; index: number;
}) => { }) => {
if (onFileMove) { try {
await onFileMove(dragIds, parentId, index); const success = await handleMove(dragIds, parentId, index);
if (success) {
await loadFileList();
}
} catch (error) {
console.error('Error moving files:', error);
} }
}, },
[onFileMove] [handleMove, loadFileList]
); );
// External file drag and drop handlers // External file drag and drop handlers
@@ -189,14 +190,22 @@ const FileTree: React.FC<FileTreeProps> = ({
setIsDragOver(false); setIsDragOver(false);
const { files } = e.dataTransfer; const { files } = e.dataTransfer;
if (files && files.length > 0 && onFileUpload) { if (files && files.length > 0) {
// Handle the upload without awaiting to avoid the eslint warning const uploadFiles = async () => {
onFileUpload(files).catch((error) => { try {
console.error('Error uploading files:', error); const success = await handleUpload(files);
}); if (success) {
await loadFileList();
}
} catch (error) {
console.error('Error uploading files:', error);
}
};
void uploadFiles();
} }
}, },
[onFileUpload] [handleUpload, loadFileList]
); );
return ( return (
@@ -247,7 +256,8 @@ const FileTree: React.FC<FileTreeProps> = ({
height={size.height} height={size.height}
indent={24} indent={24}
rowHeight={28} rowHeight={28}
onMove={handleMove} // Enable drag and drop for moving files
onMove={handleTreeMove}
onActivate={(node) => { onActivate={(node) => {
const fileNode = node.data; const fileNode = node.data;
if (!node.isInternal) { if (!node.isInternal) {

View File

@@ -37,11 +37,16 @@ const Sidebar: React.FC<SidebarProps> = ({
overflow: 'hidden', overflow: 'hidden',
}} }}
> >
<FileActions handlePullChanges={handlePull} selectedFile={selectedFile} /> <FileActions
handlePullChanges={handlePull}
selectedFile={selectedFile}
loadFileList={loadFileList}
/>
<FileTree <FileTree
files={files} files={files}
handleFileSelect={handleFileSelect} handleFileSelect={handleFileSelect}
showHiddenFiles={currentWorkspace?.showHiddenFiles || false} showHiddenFiles={currentWorkspace?.showHiddenFiles || false}
loadFileList={loadFileList}
/> />
</Box> </Box>
); );

View File

@@ -9,6 +9,12 @@ interface UseFileOperationsResult {
handleSave: (filePath: string, content: string) => Promise<boolean>; handleSave: (filePath: string, content: string) => Promise<boolean>;
handleDelete: (filePath: string) => Promise<boolean>; handleDelete: (filePath: string) => Promise<boolean>;
handleCreate: (fileName: string, initialContent?: string) => Promise<boolean>; handleCreate: (fileName: string, initialContent?: string) => Promise<boolean>;
handleUpload: (files: FileList, targetPath?: string) => Promise<boolean>;
handleMove: (
dragIds: string[],
parentId: string | null,
index: number
) => Promise<boolean>;
} }
export const useFileOperations = (): UseFileOperationsResult => { export const useFileOperations = (): UseFileOperationsResult => {
@@ -109,5 +115,83 @@ export const useFileOperations = (): UseFileOperationsResult => {
[currentWorkspace, autoCommit] [currentWorkspace, autoCommit]
); );
return { handleSave, handleDelete, handleCreate }; // Add these to your hook implementation:
const handleUpload = useCallback(
async (files: FileList, targetPath?: string): Promise<boolean> => {
if (!currentWorkspace) return false;
try {
// TODO: Implement your file upload API call
// Example:
// const formData = new FormData();
// Array.from(files).forEach((file, index) => {
// formData.append(`file${index}`, file);
// });
// if (targetPath) {
// formData.append('targetPath', targetPath);
// }
// await uploadFiles(currentWorkspace.name, formData);
notifications.show({
title: 'Success',
message: `Successfully uploaded ${files.length} file(s)`,
color: 'green',
});
// Auto-commit if enabled
await autoCommit('multiple files', FileAction.Create);
return true;
} catch (error) {
console.error('Error uploading files:', error);
notifications.show({
title: 'Error',
message: 'Failed to upload files',
color: 'red',
});
return false;
}
},
[currentWorkspace, autoCommit]
);
const handleMove = useCallback(
async (
dragIds: string[],
parentId: string | null,
index: number
): Promise<boolean> => {
if (!currentWorkspace) return false;
try {
// TODO: Implement your file move API call
// Example:
// await moveFiles(currentWorkspace.name, {
// sourceIds: dragIds,
// targetParentId: parentId,
// targetIndex: index
// });
notifications.show({
title: 'Success',
message: `Successfully moved ${dragIds.length} file(s)`,
color: 'green',
});
// Auto-commit if enabled
await autoCommit('multiple files', FileAction.Update);
return true;
} catch (error) {
console.error('Error moving files:', error);
notifications.show({
title: 'Error',
message: 'Failed to move files',
color: 'red',
});
return false;
}
},
[currentWorkspace, autoCommit]
);
return { handleSave, handleDelete, handleCreate, handleUpload, handleMove };
}; };