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-handler for file validation
  • @/lib/storage for Firebase Storage operations
  • @/lib/icons for file type icons

UI Components

  • @/components/ui/card for layout
  • @/components/ui/tabs for interface switching
  • @/components/ui/input for URL input
  • @/components/ui/button for 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
  • Assistant creation forms
  • File management interfaces
  • Chat document integration
  • Storage service configuration