DOCUMENT_UPLOAD_COMPONENT.md
Component documentation for the DocumentUpload file upload interface.
Overview
The DocumentUpload component provides a comprehensive file upload solution with support for drag-and-drop, URL imports, and various file types. It integrates with Firebase Storage and includes file validation, progress tracking, and document management features.
Component Location
- File:
src/components/DocumentUpload.tsx - Type: Form Input Component
- Framework: React with TypeScript
Features
- Multiple Upload Methods: Drag-and-drop, file picker, and URL import
- File Type Support: PDF, Office documents, images, text, code, video, audio
- URL Import: Direct PDF import from web URLs
- Storage Integration: Firebase Storage with automatic file management
- Visual Preview: Image thumbnails and file type icons
- Progress Tracking: Upload status and error handling
- Document Limits: Configurable maximum file count and size limits
- Responsive Design: Mobile-optimized with touch-friendly interface
Props Interface
interface DocumentUploadProps {
documents: Document[]; // Current documents array
userId?: string; // User identifier for storage
shareId?: string; // Share/assistant identifier
onUpload: (doc: Document) => void; // Upload success callback
onDelete: (index: number) => void; // Delete callback
isUploading: boolean; // Upload state indicator
}
Document Data Structure
interface Document {
name: string; // Display name
url: string; // Access URL
contentType: string; // MIME type
storagePath?: string; // Storage location
isFromUrl?: boolean; // URL import flag
sourceUrl?: string; // Original URL (for URL imports)
type?: string; // File type classification
}
Usage Examples
Basic Implementation
import { DocumentUpload } from '@/components/DocumentUpload';
import { useState } from 'react';
function AssistantCreator() {
const [documents, setDocuments] = useState<Document[]>([]);
const [isUploading, setIsUploading] = useState(false);
const handleUpload = (doc: Document) => {
setDocuments(prev => [...prev, doc]);
};
const handleDelete = (index: number) => {
setDocuments(prev => prev.filter((_, i) => i !== index));
};
return (
<DocumentUpload
documents={documents}
userId={currentUser.uid}
shareId={assistantId}
onUpload={handleUpload}
onDelete={handleDelete}
isUploading={isUploading}
/>
);
}
With Error Handling
function DocumentManager() {
const [uploadError, setUploadError] = useState<string | null>(null);
const handleUpload = async (doc: Document) => {
try {
setUploadError(null);
setDocuments(prev => [...prev, doc]);
} catch (error) {
setUploadError(error.message);
}
};
return (
<div>
<DocumentUpload
documents={documents}
onUpload={handleUpload}
onDelete={handleDelete}
isUploading={isUploading}
/>
{uploadError && (
<div className="text-red-500 mt-2">{uploadError}</div>
)}
</div>
);
}
Upload Methods
File Upload (Drag & Drop / Click)
const uploadFile = async (file: File) => {
if (!userId || !shareId) return;
try {
// Validate file size and document count
DocumentHandler.validateFileSize(file);
DocumentHandler.validateDocumentCount(documents.length);
// Upload to Firebase Storage
const document = await StorageService.uploadDocument(file, userId, shareId);
onUpload(document);
} catch (error) {
console.error('Upload error:', error);
}
};
URL Import
const importFromUrl = async () => {
try {
// URL validation
const url = new URL(urlInput);
if (!url.protocol.startsWith('http')) {
setUrlError('URL must start with http:// or https://');
return;
}
// Fetch file from URL
const fetchResponse = await fetch(urlInput);
if (!fetchResponse.ok) {
setUrlError(`Failed to fetch file: ${fetchResponse.statusText}`);
return;
}
// Validate content type (PDF only for URLs)
const contentType = fetchResponse.headers.get('content-type') || '';
if (!contentType.includes('application/pdf')) {
setUrlError('The URL must point to a PDF file');
return;
}
const fileBlob = await fetchResponse.blob();
const file = new File([fileBlob], filename, { type: contentType });
// Upload the file
const document = await StorageService.uploadDocument(file, userId, shareId);
document.isFromUrl = true;
document.sourceUrl = urlInput;
onUpload(document);
} catch (error) {
setUrlError(error.message);
}
};
Drag & Drop Implementation
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
setIsDragging(true);
};
const handleDragLeave = (e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);
};
const handleDrop = async (e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);
const file = e.dataTransfer.files[0];
if (file) {
await uploadFile(file);
}
};
Document List Item Component
Individual document display with preview and actions:
const DocumentListItem = ({ doc, onDelete, isUploading }) => {
const isImage = doc.contentType?.startsWith('image/');
const iconType = DocumentHandler.getFileIcon(doc.type || doc.contentType || '');
const IconComponent = IconMap[iconType];
return (
<div className="flex items-center justify-between p-3 rounded-md border">
<div className="flex items-center gap-3">
{/* Image preview or file icon */}
{isImage && doc.url ? (
<Image
src={doc.url}
alt={doc.name}
width={40}
height={40}
className="object-cover rounded"
/>
) : (
<div className="w-10 h-10 flex items-center justify-center bg-muted/30 rounded">
<IconComponent className="w-6 h-6" />
</div>
)}
{/* File information */}
<div className="flex flex-col">
<span className="text-sm font-medium truncate" title={doc.name}>
{doc.name}
</span>
<span className="text-xs text-muted-foreground">
{doc.contentType?.split('/')[1]?.toUpperCase() || 'Unknown Type'}
{doc.isFromUrl && " • From URL"}
</span>
</div>
</div>
{/* Delete button */}
<button
onClick={() => onDelete()}
disabled={isUploading}
className="text-muted-foreground hover:text-foreground p-2 rounded-md"
>
<X className="w-5 h-5" />
</button>
</div>
);
};
Tab Interface
Dual interface for file upload and URL import:
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="upload">Upload File</TabsTrigger>
<TabsTrigger value="url">Import from URL</TabsTrigger>
</TabsList>
<TabsContent value="upload">
{/* Drag & drop area */}
</TabsContent>
<TabsContent value="url">
{/* URL input form */}
</TabsContent>
</Tabs>
File Validation
Supported File Types
// From document-handler.ts
const ACCEPTED_FILE_TYPES = [
// Documents
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
// Text
'.txt', '.md', '.rtf',
// Code
'.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cpp', '.c',
// Images
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp',
// Media
'.mp4', '.mov', '.avi', '.mp3', '.wav'
];
Size and Count Limits
const DEFAULT_MAX_DOCUMENTS = 10;
const MAX_FILE_SIZE = 30 * 1024 * 1024; // 30MB
DocumentHandler.validateFileSize(file);
DocumentHandler.validateDocumentCount(documents.length);
Drag & Drop Visual States
<div
className={cn(
"border-2 border-dashed rounded-lg p-8 text-center transition-colors",
isDragging ? "border-primary bg-primary/5" : "border-muted",
isUploading && "opacity-50 pointer-events-none"
)}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
{isUploading ? (
<Loader2 className="h-8 w-8 animate-spin" />
) : (
<Upload className="h-8 w-8" />
)}
</div>
Error Handling
Upload Errors
- File size validation
- File type validation
- Storage quota limits
- Network connectivity issues
URL Import Errors
- Invalid URL format
- Network fetch failures
- Unsupported content types
- Server response errors
File Storage Integration
Firebase Storage Upload
const document = await StorageService.uploadDocument(file, userId, shareId);
File Deletion
const handleDelete = async (index: number) => {
try {
const doc = documents[index];
if (doc.storagePath) {
await StorageService.deleteDocument(doc.storagePath);
}
onDelete(index);
} catch (error) {
console.error('Delete error:', error);
}
};
Accessibility Features
- Keyboard Navigation: Full keyboard support for all interactions
- Screen Reader Support: ARIA labels and semantic HTML
- Focus Management: Proper focus handling for drag & drop
- Alternative Actions: Click-to-upload as alternative to drag & drop
Mobile Optimization
- Touch-friendly drag & drop areas
- Responsive layout for small screens
- Mobile file picker integration
- Optimized image preview sizes
Dependencies
Core Services
@/lib/document-handlerfor file validation@/lib/storagefor Firebase Storage operations@/lib/iconsfor file type icons
UI Components
@/components/ui/cardfor layout@/components/ui/tabsfor interface switching@/components/ui/inputfor URL input@/components/ui/buttonfor actions
External Libraries
- Next.js Image component for previews
- File validation utilities
- Drag & drop event handling
Performance Considerations
- Efficient file processing with blob handling
- Image preview optimization
- Minimal re-renders during upload
- Proper cleanup of object URLs
Security Considerations
- File type validation on both client and server
- Size limit enforcement
- URL validation for import feature
- Storage path isolation by user/share
Related Components
- Assistant creation forms
- File management interfaces
- Chat document integration
- Storage service configuration