fixed files attachment in messages
This commit is contained in:
parent
8d64ee7f9c
commit
912851d8b6
3 changed files with 180 additions and 19 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Message, Document } from "./dashboardChatAreaTypes";
|
||||
import messageStyles from './DashboardChatAreaStyles/DashboardChatMessages.module.css';
|
||||
import { useApiRequest } from '../../../hooks/useApi';
|
||||
|
||||
|
||||
|
||||
|
|
@ -18,10 +19,102 @@ const formatFileSize = (bytes?: number) => {
|
|||
|
||||
|
||||
const MessageItem: React.FC<MessageItemProps> = ({ message }) => {
|
||||
const [downloadingFiles, setDownloadingFiles] = useState<Set<string>>(new Set());
|
||||
const { request } = useApiRequest();
|
||||
|
||||
const handleDocumentClick = (doc: Document) => {
|
||||
const link = doc.downloadUrl || doc.url;
|
||||
if (link) window.open(link, '_blank');
|
||||
const handleDocumentClick = async (doc: Document) => {
|
||||
console.log('📋 Document object:', doc);
|
||||
|
||||
// Extract the UUID file ID from the downloadUrl (same as files page)
|
||||
let actualFileId: string | null = null;
|
||||
let downloadUrl: string;
|
||||
|
||||
if (doc.downloadUrl) {
|
||||
// Extract UUID from the downloadUrl: /api/workflows/files/{UUID}/download
|
||||
const match = doc.downloadUrl.match(/\/api\/workflows\/files\/([^\/]+)\/download/);
|
||||
if (match) {
|
||||
actualFileId = match[1];
|
||||
// Use the regular files endpoint with the UUID (same as files page)
|
||||
downloadUrl = `/api/files/${actualFileId}/download`;
|
||||
console.log(`🔗 Extracted UUID from downloadUrl: ${actualFileId}`);
|
||||
console.log(`🌐 Will use files endpoint: ${downloadUrl}`);
|
||||
} else {
|
||||
console.error('Could not extract file ID from downloadUrl:', doc.downloadUrl);
|
||||
alert('Could not extract file ID from download URL');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
console.error('No downloadUrl available in document');
|
||||
alert('No download URL available');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!actualFileId) {
|
||||
console.error('Could not determine actual file ID');
|
||||
alert('Could not determine file ID for download');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent multiple downloads of the same file
|
||||
if (downloadingFiles.has(actualFileId)) {
|
||||
console.log('⏭️ Download already in progress for this file');
|
||||
return;
|
||||
}
|
||||
|
||||
setDownloadingFiles(prev => new Set(prev).add(actualFileId));
|
||||
|
||||
try {
|
||||
console.log(`📥 Starting download for file: ${doc.name} (UUID: ${actualFileId})`);
|
||||
console.log(`🌐 Making request to: ${downloadUrl}`);
|
||||
|
||||
const blob = await request({
|
||||
url: downloadUrl,
|
||||
method: 'get',
|
||||
additionalConfig: {
|
||||
responseType: 'blob'
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ Download successful for: ${doc.name}`, {
|
||||
size: blob.size,
|
||||
type: blob.type
|
||||
});
|
||||
|
||||
// Create filename with extension
|
||||
const fileName = doc.ext ? `${doc.name}.${doc.ext}` : doc.name;
|
||||
|
||||
// Create a download link and trigger the download
|
||||
const url = window.URL.createObjectURL(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);
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(`❌ Download failed for ${doc.name}:`, error);
|
||||
console.error('Full error object:', error);
|
||||
console.error('Error response:', error.response);
|
||||
|
||||
let errorMessage = 'Download failed';
|
||||
if (error.response?.status === 404) {
|
||||
errorMessage = `File "${doc.name}" not found or has been deleted. (404)`;
|
||||
} else if (error.response?.status === 403) {
|
||||
errorMessage = `No permission to download "${doc.name}". (403)`;
|
||||
} else {
|
||||
errorMessage = `Download failed: ${error.message || 'Unknown error'}`;
|
||||
}
|
||||
|
||||
alert(errorMessage);
|
||||
} finally {
|
||||
setDownloadingFiles(prev => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.delete(actualFileId!);
|
||||
return newSet;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -66,12 +159,25 @@ const MessageItem: React.FC<MessageItemProps> = ({ message }) => {
|
|||
{isUser ? 'Uploaded' : 'Attached'} Files ({message.documents?.length || 0})
|
||||
</div>
|
||||
<div>
|
||||
{message.documents!.map((doc, i) => (
|
||||
{message.documents!.map((doc, i) => {
|
||||
// Extract UUID from downloadUrl for download state tracking
|
||||
let fileKey: string | null = null;
|
||||
if (doc.downloadUrl) {
|
||||
const match = doc.downloadUrl.match(/\/api\/workflows\/files\/([^\/]+)\/download/);
|
||||
fileKey = match ? match[1] : null;
|
||||
}
|
||||
const isDownloading = fileKey && downloadingFiles.has(fileKey);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={doc.id || i}
|
||||
className={messageStyles.message_document_item}
|
||||
onClick={() => handleDocumentClick(doc)}
|
||||
title={`Click to open ${doc.name}`}
|
||||
title={isDownloading ? 'Downloading...' : `Click to download ${doc.name}`}
|
||||
style={{
|
||||
cursor: isDownloading ? 'wait' : 'pointer',
|
||||
opacity: isDownloading ? 0.7 : 1
|
||||
}}
|
||||
>
|
||||
<div className={messageStyles.message_document_info}>
|
||||
<div className={messageStyles.message_document_name}>
|
||||
|
|
@ -107,7 +213,8 @@ const MessageItem: React.FC<MessageItemProps> = ({ message }) => {
|
|||
*/}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--color-gray);
|
||||
}
|
||||
|
||||
.message_document_size {
|
||||
|
|
|
|||
|
|
@ -115,30 +115,83 @@ export const transformWorkflowMessage = async (msg: any, request: any): Promise<
|
|||
let docs: any[] = [];
|
||||
|
||||
if (msg.documents?.length > 0) {
|
||||
docs = msg.documents.map((d: any) => ({
|
||||
id: d.id || d.fileId,
|
||||
fileId: typeof d.fileId === 'string' ? parseInt(d.fileId) : d.fileId,
|
||||
name: d.filename || `File_${d.id || d.fileId || 'unknown'}`,
|
||||
ext: (d.filename && d.filename.includes('.')) ? d.filename.split('.').pop() : 'unknown',
|
||||
type: d.mimeType,
|
||||
size: d.fileSize,
|
||||
downloadUrl: `/api/workflows/files/${d.fileId}/download`
|
||||
}));
|
||||
// Try to get filenames from the documents, but if missing, fetch from API
|
||||
const needsApiCall = msg.documents.some((d: any) => !d.filename && !d.fileName && !d.name);
|
||||
|
||||
if (needsApiCall) {
|
||||
// If any document is missing filename, fetch details from API for all
|
||||
const promises = msg.documents.map(async (d: any) => {
|
||||
try {
|
||||
const fileId = d.fileId || d.id;
|
||||
const res = await request({ url: `/api/workflows/files/${fileId}/preview`, method: 'get' });
|
||||
const fullName = res.name || res.fileName || d.filename || d.fileName || d.name || `File_${fileId}`;
|
||||
const lastDotIndex = fullName.lastIndexOf('.');
|
||||
const hasExtension = lastDotIndex > 0 && lastDotIndex < fullName.length - 1;
|
||||
|
||||
return {
|
||||
id: d.id || d.fileId,
|
||||
fileId: typeof d.fileId === 'string' ? parseInt(d.fileId) : d.fileId,
|
||||
name: hasExtension ? fullName.substring(0, lastDotIndex) : fullName,
|
||||
ext: res.extension || res.ext || (hasExtension ? fullName.substring(lastDotIndex + 1) : ''),
|
||||
type: d.mimeType || res.mimeType || res.type || 'application/octet-stream',
|
||||
size: d.fileSize || res.size || 0,
|
||||
downloadUrl: `/api/workflows/files/${d.fileId}/download`
|
||||
};
|
||||
} catch {
|
||||
// Fallback if API call fails
|
||||
const fullFilename = d.filename || d.fileName || d.name || `File_${d.id || d.fileId || 'unknown'}`;
|
||||
const lastDotIndex = fullFilename.lastIndexOf('.');
|
||||
const hasExtension = lastDotIndex > 0 && lastDotIndex < fullFilename.length - 1;
|
||||
|
||||
return {
|
||||
id: d.id || d.fileId,
|
||||
fileId: typeof d.fileId === 'string' ? parseInt(d.fileId) : d.fileId,
|
||||
name: hasExtension ? fullFilename.substring(0, lastDotIndex) : fullFilename,
|
||||
ext: hasExtension ? fullFilename.substring(lastDotIndex + 1) : '',
|
||||
type: d.mimeType || 'application/octet-stream',
|
||||
size: d.fileSize || 0,
|
||||
downloadUrl: `/api/workflows/files/${d.fileId}/download`
|
||||
};
|
||||
}
|
||||
});
|
||||
docs = await Promise.all(promises);
|
||||
} else {
|
||||
// All documents have filenames, process them directly
|
||||
docs = msg.documents.map((d: any) => {
|
||||
const fullFilename = d.filename || d.fileName || d.name || `File_${d.id || d.fileId || 'unknown'}`;
|
||||
const lastDotIndex = fullFilename.lastIndexOf('.');
|
||||
const hasExtension = lastDotIndex > 0 && lastDotIndex < fullFilename.length - 1;
|
||||
|
||||
return {
|
||||
id: d.id || d.fileId,
|
||||
fileId: typeof d.fileId === 'string' ? parseInt(d.fileId) : d.fileId,
|
||||
name: hasExtension ? fullFilename.substring(0, lastDotIndex) : fullFilename,
|
||||
ext: hasExtension ? fullFilename.substring(lastDotIndex + 1) : '',
|
||||
type: d.mimeType,
|
||||
size: d.fileSize,
|
||||
downloadUrl: `/api/workflows/files/${d.fileId}/download`
|
||||
};
|
||||
});
|
||||
}
|
||||
} else if (msg.fileIds?.length > 0) {
|
||||
const promises = msg.fileIds.map(async (id: number) => {
|
||||
try {
|
||||
const res = await request({ url: `/api/workflows/files/${id}/preview`, method: 'get' });
|
||||
const fullName = res.name || res.fileName || `File_${id}`;
|
||||
const lastDotIndex = fullName.lastIndexOf('.');
|
||||
const hasExtension = lastDotIndex > 0 && lastDotIndex < fullName.length - 1;
|
||||
|
||||
return {
|
||||
id: id.toString(),
|
||||
fileId: id,
|
||||
name: res.name || res.fileName || `File_${id}`,
|
||||
ext: res.extension || res.ext || (res.name ? res.name.split('.').pop() : 'txt'),
|
||||
name: hasExtension ? fullName.substring(0, lastDotIndex) : fullName,
|
||||
ext: res.extension || res.ext || (hasExtension ? fullName.substring(lastDotIndex + 1) : ''),
|
||||
type: res.mimeType || res.type || 'application/octet-stream',
|
||||
size: res.size || 0,
|
||||
downloadUrl: res.downloadUrl || res.url
|
||||
};
|
||||
} catch {
|
||||
return { id: id.toString(), fileId: id, name: `File_${id}`, ext: 'unknown', type: 'application/octet-stream', size: 0 };
|
||||
return { id: id.toString(), fileId: id, name: `File_${id}`, ext: '', type: 'application/octet-stream', size: 0 };
|
||||
}
|
||||
});
|
||||
docs = await Promise.all(promises);
|
||||
|
|
|
|||
Loading…
Reference in a new issue