Skip to main content

useDocuments

The useDocuments hook provides file upload, document management, and search capabilities with progress tracking and error handling. Optimized for drag-and-drop uploads and document organization.

Import

import { useDocuments } from '@enterpriseaigroup/core';

Basic Usage

import { useDocuments } from '@enterpriseaigroup/core';

function DocumentUploader() {
const { documents, upload, isUploading } = useDocuments();

const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || []);

for (const file of files) {
await upload(file, {
type: 'contract',
autoClassify: true
});
}
};

return (
<div>
<input
type="file"
onChange={handleFileChange}
disabled={isUploading}
multiple
/>

{documents?.map(doc => (
<div key={doc.id}>{doc.filename}</div>
))}
</div>
);
}

Signature

useDocuments(options?: UseDocumentsOptions): UseDocumentsResult

Parameters

UseDocumentsOptions

PropertyTypeRequiredDescription
typestringNoFilter by document type
tagsstring[]NoFilter by tags
indexedbooleanNoFilter by indexing status
autoRefetchbooleanNoAuto-refetch after mutations (default: true)
onUploadComplete(doc: Document) => voidNoCallback when upload completes
onUploadError(error: Error) => voidNoUpload error callback

Return Value

UseDocumentsResult

PropertyTypeDescription
documentsDocument[] | undefinedArray of documents
loadingbooleanLoading state
errorError | nullError object if request failed
upload(file: File, options?) => Promise<Document>Upload a file
classify(docId: string) => Promise<Classification>Classify document
index(docId: string) => Promise<IndexResult>Index for RAG
search(query: string, options?) => Promise<SearchResult[]>Search documents
delete(docId: string) => Promise<void>Delete document
isUploadingbooleanTrue during upload
uploadProgressRecord<string, number>Upload progress by file ID
refetch() => Promise<void>Manually refetch documents

Document Interface

interface Document {
id: string;
filename: string;
type?: string;
contentType: string;
size: number;
url: string;
status: 'uploaded' | 'processing' | 'ready' | 'failed';
classification?: Classification;
indexed: boolean;
tags?: string[];
metadata?: Record<string, any>;
createdAt: string;
updatedAt: string;
}

interface Classification {
category: string;
subcategory?: string;
confidence: number;
extractedData?: Record<string, any>;
}

Uploading Files

Basic Upload

function FileUpload() {
const { upload, isUploading } = useDocuments();

const handleUpload = async (file: File) => {
try {
const document = await upload(file, {
type: 'contract',
tags: ['legal', 'pending-review'],
autoClassify: true,
autoIndex: true
});

console.log('Uploaded:', document.id);
} catch (error) {
console.error('Upload failed:', error);
}
};

return (
<input
type="file"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) handleUpload(file);
}}
disabled={isUploading}
/>
);
}

Multiple File Upload

function MultiFileUpload() {
const { upload, isUploading, uploadProgress } = useDocuments();
const [uploadingFiles, setUploadingFiles] = useState<string[]>([]);

const handleFilesChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || []);

for (const file of files) {
const fileId = `${Date.now()}-${file.name}`;
setUploadingFiles(prev => [...prev, fileId]);

try {
await upload(file, {
type: 'document',
metadata: { fileId }
});

setUploadingFiles(prev => prev.filter(id => id !== fileId));
} catch (error) {
console.error(`Failed to upload ${file.name}:`, error);
setUploadingFiles(prev => prev.filter(id => id !== fileId));
}
}
};

return (
<div>
<input
type="file"
onChange={handleFilesChange}
multiple
disabled={isUploading}
/>

{uploadingFiles.map(fileId => (
<div key={fileId}>
Uploading... {uploadProgress[fileId] || 0}%
</div>
))}
</div>
);
}

Drag and Drop Upload

function DragDropUpload() {
const { upload, isUploading } = useDocuments();
const [isDragging, setIsDragging] = useState(false);

const handleDrop = async (e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);

const files = Array.from(e.dataTransfer.files);

for (const file of files) {
try {
await upload(file, {
type: 'document',
autoClassify: true
});
} catch (error) {
console.error(`Failed to upload ${file.name}:`, error);
}
}
};

return (
<div
onDrop={handleDrop}
onDragOver={(e) => {
e.preventDefault();
setIsDragging(true);
}}
onDragLeave={() => setIsDragging(false)}
className={isDragging ? 'dragging' : ''}
style={{
border: '2px dashed #ccc',
padding: 40,
textAlign: 'center'
}}
>
{isUploading ? 'Uploading...' : 'Drag files here or click to upload'}
</div>
);
}

Document List

Displaying Documents

function DocumentList() {
const { documents, loading, error } = useDocuments({
type: 'contract',
indexed: true
});

if (loading) return <div>Loading documents...</div>;
if (error) return <div>Error: {error.message}</div>;

return (
<ul>
{documents?.map(doc => (
<li key={doc.id}>
<a href={doc.url} target="_blank" rel="noopener">
{doc.filename}
</a>
<span> ({(doc.size / 1024).toFixed(1)} KB)</span>
{doc.classification && (
<span className="badge">{doc.classification.category}</span>
)}
</li>
))}
</ul>
);
}

With Status Indicators

function DocumentListWithStatus() {
const { documents } = useDocuments();

return (
<div>
{documents?.map(doc => (
<div key={doc.id} className="document-item">
<div className="document-name">{doc.filename}</div>

<div className={`status ${doc.status}`}>
{doc.status === 'processing' && '⏳ Processing...'}
{doc.status === 'ready' && '✓ Ready'}
{doc.status === 'failed' && '✗ Failed'}
</div>

{doc.indexed && <span className="badge">Indexed</span>}

{doc.tags?.map(tag => (
<span key={tag} className="tag">{tag}</span>
))}
</div>
))}
</div>
);
}

Document Operations

Classification

function ClassifyDocument({ documentId }: { documentId: string }) {
const { classify } = useDocuments();
const [classification, setClassification] = useState<Classification | null>(null);

const handleClassify = async () => {
try {
const result = await classify(documentId);
setClassification(result);
} catch (error) {
console.error('Classification failed:', error);
}
};

return (
<div>
<button onClick={handleClassify}>Classify</button>

{classification && (
<div>
<p>Category: {classification.category}</p>
<p>Confidence: {(classification.confidence * 100).toFixed(0)}%</p>
{classification.extractedData && (
<pre>{JSON.stringify(classification.extractedData, null, 2)}</pre>
)}
</div>
)}
</div>
);
}

Indexing for RAG

function IndexDocument({ documentId }: { documentId: string }) {
const { index } = useDocuments();
const [indexing, setIndexing] = useState(false);

const handleIndex = async () => {
setIndexing(true);
try {
const result = await index(documentId);
console.log(`Indexed ${result.chunks} chunks`);
} catch (error) {
console.error('Indexing failed:', error);
} finally {
setIndexing(false);
}
};

return (
<button onClick={handleIndex} disabled={indexing}>
{indexing ? 'Indexing...' : 'Index for Search'}
</button>
);
}

Deleting Documents

function DeleteButton({ documentId }: { documentId: string }) {
const { delete: deleteDoc } = useDocuments();

const handleDelete = async () => {
if (!confirm('Delete this document?')) return;

try {
await deleteDoc(documentId);
console.log('Document deleted');
} catch (error) {
console.error('Delete failed:', error);
}
};

return (
<button onClick={handleDelete} className="delete-button">
Delete
</button>
);
}
function DocumentSearch() {
const { search } = useDocuments();
const [query, setQuery] = useState('');
const [results, setResults] = useState<SearchResult[]>([]);
const [searching, setSearching] = useState(false);

const handleSearch = async () => {
if (!query.trim()) return;

setSearching(true);
try {
const searchResults = await search(query, {
limit: 10,
minScore: 0.7
});
setResults(searchResults);
} catch (error) {
console.error('Search failed:', error);
} finally {
setSearching(false);
}
};

return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search documents..."
/>
<button onClick={handleSearch} disabled={searching}>
Search
</button>

<div className="results">
{results.map((result, i) => (
<div key={i} className="result">
<h4>{result.filename}</h4>
<p>{result.chunk}</p>
<small>Relevance: {(result.score * 100).toFixed(0)}%</small>
</div>
))}
</div>
</div>
);
}

Upload Progress

function UploadWithProgress() {
const { upload, uploadProgress, isUploading } = useDocuments();
const [currentFileId, setCurrentFileId] = useState<string | null>(null);

const handleUpload = async (file: File) => {
const fileId = `${Date.now()}-${file.name}`;
setCurrentFileId(fileId);

try {
await upload(file, {
type: 'document',
metadata: { fileId }
});

console.log('Upload complete');
} catch (error) {
console.error('Upload failed:', error);
} finally {
setCurrentFileId(null);
}
};

const progress = currentFileId ? uploadProgress[currentFileId] || 0 : 0;

return (
<div>
<input
type="file"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) handleUpload(file);
}}
disabled={isUploading}
/>

{isUploading && (
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress}%` }}
/>
<span>{progress}%</span>
</div>
)}
</div>
);
}

Complete Example

Full document management component:

import { useDocuments } from '@enterpriseaigroup/core';
import { useState } from 'react';

function DocumentManager() {
const {
documents,
loading,
error,
upload,
classify,
index,
delete: deleteDoc,
isUploading,
uploadProgress
} = useDocuments({
type: 'contract',
onUploadComplete: (doc) => {
console.log('Upload complete:', doc.filename);
},
onUploadError: (error) => {
alert(`Upload failed: ${error.message}`);
}
});

const [uploadingFiles, setUploadingFiles] = useState<Set<string>>(new Set());

const handleUpload = async (files: File[]) => {
for (const file of files) {
const fileId = `${Date.now()}-${file.name}`;
setUploadingFiles(prev => new Set(prev).add(fileId));

try {
await upload(file, {
type: 'contract',
tags: ['new'],
autoClassify: true,
autoIndex: true,
metadata: { fileId }
});
} catch (error) {
console.error(`Failed to upload ${file.name}:`, error);
} finally {
setUploadingFiles(prev => {
const next = new Set(prev);
next.delete(fileId);
return next;
});
}
}
};

const handleFilesChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || []);
handleUpload(files);
};

const handleDelete = async (docId: string) => {
if (!confirm('Delete this document?')) return;

try {
await deleteDoc(docId);
} catch (error) {
alert(`Delete failed: ${error.message}`);
}
};

if (loading) return <div>Loading documents...</div>;
if (error) return <div>Error: {error.message}</div>;

return (
<div className="document-manager">
<h2>Document Manager</h2>

<div className="upload-section">
<input
type="file"
onChange={handleFilesChange}
multiple
accept=".pdf,.doc,.docx,.txt"
disabled={isUploading}
/>

{uploadingFiles.size > 0 && (
<div className="uploading-files">
{Array.from(uploadingFiles).map(fileId => (
<div key={fileId}>
Uploading... {uploadProgress[fileId] || 0}%
</div>
))}
</div>
)}
</div>

<div className="documents-list">
<h3>Documents ({documents?.length || 0})</h3>

{documents?.map(doc => (
<div key={doc.id} className="document-card">
<div className="document-header">
<a href={doc.url} target="_blank" rel="noopener">
{doc.filename}
</a>
<span className={`status ${doc.status}`}>
{doc.status}
</span>
</div>

<div className="document-meta">
<span>Size: {(doc.size / 1024).toFixed(1)} KB</span>
<span>Type: {doc.type || 'Unknown'}</span>
{doc.indexed && <span className="badge">Indexed</span>}
</div>

{doc.classification && (
<div className="classification">
<strong>{doc.classification.category}</strong>
<span>
({(doc.classification.confidence * 100).toFixed(0)}% confidence)
</span>
</div>
)}

{doc.tags && (
<div className="tags">
{doc.tags.map(tag => (
<span key={tag} className="tag">{tag}</span>
))}
</div>
)}

<div className="document-actions">
{!doc.indexed && (
<button onClick={() => index(doc.id)}>
Index
</button>
)}
{!doc.classification && (
<button onClick={() => classify(doc.id)}>
Classify
</button>
)}
<button onClick={() => handleDelete(doc.id)}>
Delete
</button>
</div>
</div>
))}
</div>
</div>
);
}

export default DocumentManager;

Supported File Types

  • Documents: PDF, DOC, DOCX, TXT, RTF
  • Spreadsheets: XLS, XLSX, CSV
  • Presentations: PPT, PPTX
  • Images: JPG, PNG, GIF, WEBP
  • Archives: ZIP, TAR, GZ

Maximum file size: 100 MB per file

Next Steps