import React, { useState, useRef, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { CloudArrowUpIcon, PaperClipIcon } from '@heroicons/react/24/outline'; import { Attachment } from '../../../entities/Attachment'; import { uploadAttachment, deleteAttachment, downloadAttachment, fetchAttachments, validateFile, } from '../../../utils/attachmentsService'; import { useToast } from '../../Shared/ToastContext'; import AttachmentListItem from '../../Shared/AttachmentListItem'; import AttachmentPreview from '../../Shared/AttachmentPreview'; interface TaskAttachmentsCardProps { taskUid: string; } const TaskAttachmentsCard: React.FC = ({ taskUid, }) => { const { t } = useTranslation(); const { showSuccessToast, showErrorToast } = useToast(); const [attachments, setAttachments] = useState([]); const [loading, setLoading] = useState(true); const [uploading, setUploading] = useState(false); const [previewAttachment, setPreviewAttachment] = useState(null); const fileInputRef = useRef(null); // Load attachments on mount useEffect(() => { loadAttachments(); }, [taskUid]); const loadAttachments = async () => { try { setLoading(true); const data = await fetchAttachments(taskUid); setAttachments(data); } catch (error) { console.error('Error loading attachments:', error); } finally { setLoading(false); } }; const handleFileSelect = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; // Validate file const validation = validateFile(file); if (!validation.valid) { showErrorToast(validation.error || 'Invalid file'); if (fileInputRef.current) { fileInputRef.current.value = ''; } return; } // Check attachment limit if (attachments.length >= 20) { showErrorToast( t( 'task.attachments.limitReached', 'Maximum 20 attachments allowed per task' ) ); if (fileInputRef.current) { fileInputRef.current.value = ''; } return; } // Upload file setUploading(true); try { const newAttachment = await uploadAttachment(taskUid, file); setAttachments([...attachments, newAttachment]); showSuccessToast( t( 'task.attachments.uploadSuccess', 'File uploaded successfully' ) ); } catch (error: any) { showErrorToast( error.message || t('task.attachments.uploadError', 'Failed to upload file') ); } finally { setUploading(false); if (fileInputRef.current) { fileInputRef.current.value = ''; } } }; const handleDelete = async (attachment: Attachment) => { try { await deleteAttachment(taskUid, attachment.uid); setAttachments(attachments.filter((a) => a.uid !== attachment.uid)); showSuccessToast( t( 'task.attachments.deleteSuccess', 'Attachment deleted successfully' ) ); if (previewAttachment?.uid === attachment.uid) { setPreviewAttachment(null); } } catch (error: any) { showErrorToast( error.message || t( 'task.attachments.deleteError', 'Failed to delete attachment' ) ); } }; const handleDownload = (attachment: Attachment) => { downloadAttachment(attachment.uid); }; const handlePreview = (attachment: Attachment) => { setPreviewAttachment( previewAttachment?.uid === attachment.uid ? null : attachment ); }; if (loading) { return (

{t('task.attachments.title', 'Attachments')}

{t('common.loading', 'Loading...')}

); } return (

{t('task.attachments.title', 'Attachments')} ( {attachments.length})

{/* Upload Area */}
fileInputRef.current?.click()} >

{uploading ? t('task.attachments.uploading', 'Uploading...') : t( 'task.attachments.clickToUpload', 'Click to upload' )}

{t('task.attachments.maxSize', 'Max 10MB')}

{/* Attachments List */} {attachments.length > 0 ? (
{attachments.map((attachment) => (
{previewAttachment?.uid === attachment.uid && (
)}
))}
) : (

{t( 'task.attachments.noAttachments', 'No attachments yet' )}

)}
); }; export default TaskAttachmentsCard;