import React, { useState, useRef } from 'react'; import { Upload, X } from 'lucide-react'; import { useToast } from '../contexts/ToastContext'; // ⚠️ CONVENTION: Use relative API path from REACT_APP_API_BASE only — avoid hardcoded absolute URL fallback const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api'; const ACCEPTED_EXTENSIONS = '.pdf,.png,.jpg,.jpeg,.gif,.bmp,.tiff,.tif,.txt,.csv,.log,.msg,.eml,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.odt,.ods,.odp,.rtf,.html,.htm,.xml,.json,.yaml,.yml,.zip,.gz,.tar,.7z'; const DOC_TYPES = ['advisory', 'email', 'screenshot', 'patch', 'other']; export default function DocumentDropZone({ cveId, vendor, onUploadComplete }) { const toast = useToast(); const fileInputRef = useRef(null); const [dragOver, setDragOver] = useState(false); const [file, setFile] = useState(null); const [docType, setDocType] = useState('advisory'); const [notes, setNotes] = useState(''); const [uploading, setUploading] = useState(false); const handleDragOver = (e) => { e.preventDefault(); e.stopPropagation(); setDragOver(true); }; const handleDragLeave = (e) => { e.preventDefault(); e.stopPropagation(); setDragOver(false); }; const handleDrop = (e) => { e.preventDefault(); e.stopPropagation(); setDragOver(false); const droppedFile = e.dataTransfer.files[0]; if (droppedFile) setFile(droppedFile); }; const handleFileSelect = (e) => { const selected = e.target.files[0]; if (selected) setFile(selected); }; const handleUpload = async () => { if (!file) return; setUploading(true); const formData = new FormData(); formData.append('file', file); formData.append('cveId', cveId); formData.append('vendor', vendor); formData.append('type', docType); if (notes.trim()) formData.append('notes', notes.trim()); try { const response = await fetch(`${API_BASE}/cves/${cveId}/documents`, { method: 'POST', credentials: 'include', body: formData, }); if (!response.ok) throw new Error('Failed to upload document'); toast.success(`Uploaded ${file.name}`); setFile(null); setNotes(''); setDocType('advisory'); onUploadComplete(); } catch (err) { toast.error(err.message); } finally { setUploading(false); } }; const handleCancel = () => { setFile(null); setNotes(''); setDocType('advisory'); }; // No file selected — show drop zone if (!file) { return (
fileInputRef.current?.click()} className="mt-3 cursor-pointer transition-all" style={{ border: `2px dashed ${dragOver ? '#0EA5E9' : 'rgba(100, 116, 139, 0.4)'}`, borderRadius: '0.5rem', padding: '1rem', textAlign: 'center', background: dragOver ? 'rgba(14, 165, 233, 0.05)' : 'transparent', }} role="button" aria-label="Drop a file here or click to browse" >

Drop file here or click to browse

); } // File selected — show upload form return (
{file.name} ({(file.size / 1024).toFixed(0)} KB)
setNotes(e.target.value)} className="intel-input w-full text-xs" style={{ padding: '0.375rem 0.5rem' }} />
); }