235 lines
No EOL
6.5 KiB
TypeScript
235 lines
No EOL
6.5 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { useApiRequest } from './useApi';
|
|
|
|
// File interfaces
|
|
export interface FileInfo {
|
|
id: number;
|
|
name: string;
|
|
mimeType: string;
|
|
size?: number;
|
|
creationDate: string;
|
|
fileHash?: string;
|
|
mandateId?: number;
|
|
userId?: number;
|
|
workflowId?: string;
|
|
source?: string; // 'user_uploaded', 'agent_created', or 'shared_with_me'
|
|
}
|
|
|
|
export interface UserFile {
|
|
id: number;
|
|
file_name: string;
|
|
action: string;
|
|
created_at: string;
|
|
size?: number;
|
|
source?: string; // 'user_uploaded', 'agent_created', or 'shared_with_me'
|
|
}
|
|
|
|
// Files list hook
|
|
export function useUserFiles() {
|
|
const [files, setFiles] = useState<UserFile[]>([]);
|
|
const { request, isLoading: loading, error } = useApiRequest<null, FileInfo[]>();
|
|
|
|
const fetchFiles = async () => {
|
|
try {
|
|
const data = await request({
|
|
url: '/api/files',
|
|
method: 'get'
|
|
});
|
|
|
|
// Map API response to our frontend model
|
|
const mappedFiles = data.map((apiFile: FileInfo): UserFile => {
|
|
// Derive a simplified action from the MIME type
|
|
let action = 'Document';
|
|
if (apiFile.mimeType) {
|
|
const mimePrefix = apiFile.mimeType.split('/')[0];
|
|
switch (mimePrefix) {
|
|
case 'image':
|
|
action = 'Bild';
|
|
break;
|
|
case 'application':
|
|
if (apiFile.mimeType.includes('pdf')) {
|
|
action = 'PDF';
|
|
} else if (apiFile.mimeType.includes('word') || apiFile.mimeType.includes('office')) {
|
|
action = 'Dokument';
|
|
} else if (apiFile.mimeType.includes('excel') || apiFile.mimeType.includes('spreadsheet')) {
|
|
action = 'Tabelle';
|
|
} else {
|
|
action = 'Datei';
|
|
}
|
|
break;
|
|
case 'text':
|
|
action = 'Text';
|
|
break;
|
|
case 'video':
|
|
action = 'Video';
|
|
break;
|
|
case 'audio':
|
|
action = 'Audio';
|
|
break;
|
|
default:
|
|
action = 'Datei';
|
|
}
|
|
}
|
|
|
|
return {
|
|
id: apiFile.id,
|
|
file_name: apiFile.name,
|
|
action: action,
|
|
created_at: apiFile.creationDate,
|
|
size: apiFile.size,
|
|
source: apiFile.source
|
|
};
|
|
});
|
|
|
|
setFiles(mappedFiles);
|
|
} catch (error) {
|
|
// Error is already handled by useApiRequest
|
|
}
|
|
};
|
|
|
|
// Optimistically remove a file from the local state
|
|
const removeFileOptimistically = (fileId: number) => {
|
|
setFiles(prevFiles => prevFiles.filter(file => file.id !== fileId));
|
|
};
|
|
|
|
// Add a file to the local state (for when upload completes)
|
|
const addFileOptimistically = (newFile: UserFile) => {
|
|
setFiles(prevFiles => [newFile, ...prevFiles]);
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchFiles();
|
|
}, []);
|
|
|
|
return {
|
|
files,
|
|
loading,
|
|
error,
|
|
refetch: fetchFiles,
|
|
removeFileOptimistically,
|
|
addFileOptimistically
|
|
};
|
|
}
|
|
|
|
// File operations hook
|
|
export function useFileOperations() {
|
|
const [downloadingFiles, setDownloadingFiles] = useState<Set<number>>(new Set());
|
|
const [deletingFiles, setDeletingFiles] = useState<Set<number>>(new Set());
|
|
const [uploadingFile, setUploadingFile] = useState(false);
|
|
const { request, error: apiError, isLoading } = useApiRequest();
|
|
const [downloadError, setDownloadError] = useState<string | null>(null);
|
|
const [deleteError, setDeleteError] = useState<string | null>(null);
|
|
const [uploadError, setUploadError] = useState<string | null>(null);
|
|
|
|
const handleFileDownload = async (fileId: number, fileName: string) => {
|
|
setDownloadError(null);
|
|
setDownloadingFiles(prev => new Set(prev).add(fileId));
|
|
|
|
try {
|
|
const blob = await request({
|
|
url: `/api/files/${fileId}`,
|
|
method: 'get',
|
|
// Override axios config for blob response
|
|
additionalConfig: { responseType: 'blob' }
|
|
});
|
|
|
|
// Create a download link and trigger the download
|
|
const url = window.URL.createObjectURL(new Blob([blob]));
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.setAttribute('download', fileName);
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
return true;
|
|
} catch (error: any) {
|
|
setDownloadError(error.message);
|
|
return false;
|
|
} finally {
|
|
setDownloadingFiles(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(fileId);
|
|
return newSet;
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleFileDelete = async (fileId: number, onOptimisticDelete?: () => void) => {
|
|
setDeleteError(null);
|
|
setDeletingFiles(prev => new Set(prev).add(fileId));
|
|
|
|
// Optimistically remove from UI if callback provided
|
|
if (onOptimisticDelete) {
|
|
onOptimisticDelete();
|
|
}
|
|
|
|
try {
|
|
await request({
|
|
url: `/api/files/${fileId}`,
|
|
method: 'delete'
|
|
});
|
|
|
|
// Add a small delay to ensure backend has time to process
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
return true;
|
|
} catch (error: any) {
|
|
setDeleteError(error.message);
|
|
// If deletion failed and we optimistically removed it, we should refetch to restore the file
|
|
return false;
|
|
} finally {
|
|
setDeletingFiles(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(fileId);
|
|
return newSet;
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleFileUpload = async (file: globalThis.File, workflowId?: string) => {
|
|
setUploadError(null);
|
|
setUploadingFile(true);
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
|
|
if (workflowId) {
|
|
formData.append('workflowId', workflowId);
|
|
}
|
|
|
|
const fileData = await request({
|
|
url: '/api/files/upload',
|
|
method: 'post',
|
|
data: formData,
|
|
// Override axios config for form data
|
|
additionalConfig: {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
}
|
|
}
|
|
});
|
|
|
|
return { success: true, fileData };
|
|
} catch (error: any) {
|
|
setUploadError(error.message);
|
|
return { success: false, error: error.message };
|
|
} finally {
|
|
setUploadingFile(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
downloadingFiles,
|
|
deletingFiles,
|
|
uploadingFile,
|
|
downloadError,
|
|
deleteError,
|
|
uploadError,
|
|
handleFileDownload,
|
|
handleFileDelete,
|
|
handleFileUpload,
|
|
isLoading
|
|
};
|
|
}
|