From 80e55ef38508d5410e9486da5ed5d9251ffdb27d Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Fri, 28 Mar 2025 18:10:18 +0530 Subject: [PATCH] (feat:attach) functionality to upload files --- frontend/src/components/MessageInput.tsx | 132 +++++++++++---------- frontend/src/conversation/Conversation.tsx | 24 +++- 2 files changed, 89 insertions(+), 67 deletions(-) diff --git a/frontend/src/components/MessageInput.tsx b/frontend/src/components/MessageInput.tsx index c781dd48..7506d7d2 100644 --- a/frontend/src/components/MessageInput.tsx +++ b/frontend/src/components/MessageInput.tsx @@ -22,6 +22,7 @@ interface MessageInputProps { onChange: (e: React.ChangeEvent) => void; onSubmit: () => void; loading: boolean; + onAttachmentChange?: (attachments: { fileName: string; id: string }[]) => void; } interface UploadState { @@ -38,6 +39,7 @@ export default function MessageInput({ onChange, onSubmit, loading, + onAttachmentChange, }: MessageInputProps) { const { t } = useTranslation(); const [isDarkTheme] = useDarkTheme(); @@ -54,66 +56,66 @@ export default function MessageInput({ const handleFileAttachment = (e: React.ChangeEvent) => { if (!e.target.files || e.target.files.length === 0) return; - + const file = e.target.files[0]; const formData = new FormData(); formData.append('file', file); - + const apiHost = import.meta.env.VITE_API_HOST; const xhr = new XMLHttpRequest(); - + const uploadState: UploadState = { taskId: '', fileName: file.name, progress: 0, status: 'uploading' }; - + setUploads(prev => [...prev, uploadState]); const uploadIndex = uploads.length; - + xhr.upload.addEventListener('progress', (event) => { if (event.lengthComputable) { const progress = Math.round((event.loaded / event.total) * 100); - setUploads(prev => prev.map((upload, index) => - index === uploadIndex + setUploads(prev => prev.map((upload, index) => + index === uploadIndex ? { ...upload, progress } : upload )); } }); - + xhr.onload = () => { if (xhr.status === 200) { const response = JSON.parse(xhr.responseText); console.log('File uploaded successfully:', response); - + if (response.task_id) { - setUploads(prev => prev.map((upload, index) => - index === uploadIndex + setUploads(prev => prev.map((upload, index) => + index === uploadIndex ? { ...upload, taskId: response.task_id, status: 'processing' } : upload )); } } else { - setUploads(prev => prev.map((upload, index) => - index === uploadIndex + setUploads(prev => prev.map((upload, index) => + index === uploadIndex ? { ...upload, status: 'failed' } : upload )); console.error('Error uploading file:', xhr.responseText); } }; - + xhr.onerror = () => { - setUploads(prev => prev.map((upload, index) => - index === uploadIndex + setUploads(prev => prev.map((upload, index) => + index === uploadIndex ? { ...upload, status: 'failed' } : upload )); console.error('Network error during file upload'); }; - + xhr.open('POST', `${apiHost}${endpoints.USER.STORE_ATTACHMENT}`); xhr.setRequestHeader('Authorization', `Bearer ${token}`); xhr.send(formData); @@ -122,22 +124,22 @@ export default function MessageInput({ useEffect(() => { let timeoutIds: number[] = []; - + const checkTaskStatus = () => { - const processingUploads = uploads.filter(upload => + const processingUploads = uploads.filter(upload => upload.status === 'processing' && upload.taskId ); - + processingUploads.forEach(upload => { userService .getTaskStatus(upload.taskId, null) .then((data) => data.json()) .then((data) => { console.log('Task status:', data); - + setUploads(prev => prev.map(u => { if (u.taskId !== upload.taskId) return u; - + if (data.status === 'SUCCESS') { return { ...u, @@ -153,7 +155,7 @@ export default function MessageInput({ } return u; })); - + if (data.status !== 'SUCCESS' && data.status !== 'FAILURE') { const timeoutId = window.setTimeout(() => checkTaskStatus(), 2000); timeoutIds.push(timeoutId); @@ -161,20 +163,20 @@ export default function MessageInput({ }) .catch((error) => { console.error('Error checking task status:', error); - setUploads(prev => prev.map(u => - u.taskId === upload.taskId + setUploads(prev => prev.map(u => + u.taskId === upload.taskId ? { ...u, status: 'failed' } : u )); }); }); }; - + if (uploads.some(upload => upload.status === 'processing')) { const timeoutId = window.setTimeout(checkTaskStatus, 2000); timeoutIds.push(timeoutId); } - + return () => { timeoutIds.forEach(id => clearTimeout(id)); }; @@ -199,7 +201,7 @@ export default function MessageInput({ const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); - onSubmit(); + handleSubmit(); if (inputRef.current) { inputRef.current.value = ''; handleInput(); @@ -211,58 +213,62 @@ export default function MessageInput({ console.log('Selected document:', doc); }; - const renderUploadStatus = () => { - const activeUploads = uploads.filter(u => - u.status === 'uploading' || u.status === 'processing' - ); - - if (activeUploads.length === 0) { - return 'Attach'; + useEffect(() => { + if (onAttachmentChange) { + const completedAttachments = uploads + .filter(upload => upload.status === 'completed' && upload.attachment_id) + .map(upload => ({ + fileName: upload.fileName, + id: upload.attachment_id as string + })); + onAttachmentChange(completedAttachments); } - - return `Uploading ${activeUploads.length} file(s)`; - }; + }, [uploads, onAttachmentChange]); + const handleSubmit = () => { + onSubmit(); + setUploads(prevUploads => prevUploads.filter(upload => upload.status !== 'completed')); + }; return (
{uploads.map((upload, index) => ( -
{upload.fileName} - + {upload.status === 'completed' && ( )} - + {upload.status === 'failed' && ( )} - + {(upload.status === 'uploading' || upload.status === 'processing') && (
- - @@ -305,7 +311,7 @@ export default function MessageInput({ : t('conversation.sources.title')} - +